mirror of
https://github.com/oliverbooth/fdup.git
synced 2024-11-10 04:05:40 +00:00
Compare commits
6 Commits
d2ca59e174
...
c65d6ed414
Author | SHA1 | Date | |
---|---|---|---|
c65d6ed414 | |||
f07fd7f641 | |||
8273a34fef | |||
7da6faff83 | |||
3396c2bc74 | |||
435318cd20 |
66
.github/workflows/prerelease.yml
vendored
Normal file
66
.github/workflows/prerelease.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
name: Tagged Pre-Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v[0-9]+.[0-9]+.[0-9]+-*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: "Tagged Pre-Release"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: get_version
|
||||||
|
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
||||||
|
|
||||||
|
- name: Build and Publish
|
||||||
|
run: |
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/win-x64 -r win-x64
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/win-x86 -r win-x86
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/linux-x64 -r linux-x64
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/osx-x64 -r osx-x64
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: publish
|
||||||
|
path: publish/
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
|
with:
|
||||||
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release Assets
|
||||||
|
id: upload-release-assets
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: |
|
||||||
|
./publish/win-x64/fdup.exe
|
||||||
|
./publish/win-x86/fdup.exe
|
||||||
|
./publish/linux-x64/fdup
|
||||||
|
./publish/osx-x64/fdup
|
||||||
|
asset_name: |
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-win-x64.exe
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-win-x86.exe
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-linux_x64
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-macos_x64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
66
.github/workflows/release.yml
vendored
Normal file
66
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
name: Tagged Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: "Tagged Release"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: get_version
|
||||||
|
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
||||||
|
|
||||||
|
- name: Build and Publish
|
||||||
|
run: |
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/win-x64 -r win-x64
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/win-x86 -r win-x86
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/linux-x64 -r linux-x64
|
||||||
|
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/osx-x64 -r osx-x64
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: publish
|
||||||
|
path: publish/
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
|
with:
|
||||||
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release Assets
|
||||||
|
id: upload-release-assets
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: |
|
||||||
|
./publish/win-x64/fdup.exe
|
||||||
|
./publish/win-x86/fdup.exe
|
||||||
|
./publish/linux-x64/fdup
|
||||||
|
./publish/osx-x64/fdup
|
||||||
|
asset_name: |
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-win-x64.exe
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-win-x86.exe
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-linux_x64
|
||||||
|
fdup-${{ steps.get_version.outputs.VERSION }}-macos_x64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
@ -6,7 +6,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyName>fdup</AssemblyName>
|
<AssemblyName>fdup</AssemblyName>
|
||||||
<VersionPrefix>1.1.0</VersionPrefix>
|
<VersionPrefix>1.2.0</VersionPrefix>
|
||||||
<Authors>Oliver Booth</Authors>
|
<Authors>Oliver Booth</Authors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Security;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
@ -10,7 +8,7 @@ namespace FindDuplicates;
|
|||||||
|
|
||||||
internal sealed class ListCommand : AsyncCommand<ListSettings>
|
internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, List<FileInfo>> _fileHashMap = new();
|
private readonly ConcurrentDictionary<string, ConcurrentBag<FileInfo>> _fileHashMap = new();
|
||||||
|
|
||||||
public override async Task<int> ExecuteAsync(CommandContext context, ListSettings settings)
|
public override async Task<int> ExecuteAsync(CommandContext context, ListSettings settings)
|
||||||
{
|
{
|
||||||
@ -31,7 +29,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
int duplicates = 0;
|
int duplicates = 0;
|
||||||
foreach ((string hash, List<FileInfo> files) in _fileHashMap)
|
foreach ((string hash, ConcurrentBag<FileInfo> files) in _fileHashMap)
|
||||||
{
|
{
|
||||||
int fileCount = files.Count;
|
int fileCount = files.Count;
|
||||||
|
|
||||||
@ -91,18 +89,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
if (relativePath != ".")
|
if (relativePath != ".")
|
||||||
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{relativePath}[/]");
|
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{relativePath}[/]");
|
||||||
|
|
||||||
if (settings.Recursive)
|
AddChildDirectories(settings, currentDirectory, directoryStack);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (DirectoryInfo childDirectory in currentDirectory.EnumerateDirectories())
|
|
||||||
directoryStack.Push(childDirectory);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -110,7 +97,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
{
|
{
|
||||||
string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName);
|
string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName);
|
||||||
AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
|
AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
|
||||||
tasks.Add(Task.Run(() => ProcessFile(file)));
|
tasks.Add(Task.Run(() => ProcessFile(file, settings)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -120,7 +107,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessFile(FileInfo file)
|
private void ProcessFile(FileInfo file, ListSettings settings)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[64];
|
Span<byte> buffer = stackalloc byte[64];
|
||||||
try
|
try
|
||||||
@ -129,13 +116,27 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
||||||
SHA512.HashData(bufferedStream, buffer);
|
SHA512.HashData(bufferedStream, buffer);
|
||||||
string hash = ByteSpanToString(buffer);
|
string hash = ByteSpanToString(buffer);
|
||||||
Trace.WriteLine($"{file.FullName}: {hash}");
|
if (settings.Verbose)
|
||||||
|
AnsiConsole.WriteLine($"{file.FullName} ->\n {hash}");
|
||||||
|
|
||||||
if (!_fileHashMap.TryGetValue(hash, out List<FileInfo>? cache))
|
ConcurrentBag<FileInfo> cache = _fileHashMap.GetOrAdd(hash, _ => []);
|
||||||
_fileHashMap[hash] = cache = new List<FileInfo>();
|
cache.Add(file);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lock (cache)
|
private static void AddChildDirectories(ListSettings settings, DirectoryInfo directory, Stack<DirectoryInfo> stack)
|
||||||
cache.Add(file);
|
{
|
||||||
|
if (!settings.Recursive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (DirectoryInfo childDirectory in directory.EnumerateDirectories())
|
||||||
|
stack.Push(childDirectory);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -5,12 +5,18 @@ namespace FindDuplicates;
|
|||||||
|
|
||||||
internal sealed class ListSettings : CommandSettings
|
internal sealed class ListSettings : CommandSettings
|
||||||
{
|
{
|
||||||
[CommandArgument(0, "<path>")]
|
[CommandArgument(0, "[path]")]
|
||||||
[Description("The path to search.")]
|
[Description("The path to search. Defaults to the current directory.")]
|
||||||
public string InputPath { get; set; } = string.Empty;
|
[DefaultValue(".")]
|
||||||
|
public string InputPath { get; set; } = ".";
|
||||||
|
|
||||||
[CommandOption("-r|--recursive")]
|
[CommandOption("-r|--recursive")]
|
||||||
[Description("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
[Description("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
||||||
[DefaultValue(false)]
|
[DefaultValue(false)]
|
||||||
public bool Recursive { get; set; } = false;
|
public bool Recursive { get; set; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--verbose")]
|
||||||
|
[Description("Enable verbose output.")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool Verbose { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user