This document covers how to package and distribute ATS Scanner to end users across all major options — from the simplest (GitHub Releases) to the most polished (package managers). They are ordered from lowest to highest effort.
A single executable file per platform that requires no runtime installation. Users download, unzip, and run.
# Windows x64
dotnet publish src/AtsScanner.Cli -c Release -r win-x64 \
--self-contained true \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-o ./dist/win-x64
# macOS (Apple Silicon)
dotnet publish src/AtsScanner.Cli -c Release -r osx-arm64 \
--self-contained true \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-o ./dist/osx-arm64
# macOS (Intel)
dotnet publish src/AtsScanner.Cli -c Release -r osx-x64 \
--self-contained true \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-o ./dist/osx-x64
# Linux x64
dotnet publish src/AtsScanner.Cli -c Release -r linux-x64 \
--self-contained true \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-o ./dist/linux-x64
# Linux ARM64
dotnet publish src/AtsScanner.Cli -c Release -r linux-arm64 \
--self-contained true \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-o ./dist/linux-arm64Add to src/AtsScanner.Cli/AtsScanner.Cli.csproj:
<PropertyGroup>
<AssemblyName>ats-scanner</AssemblyName>
<Version>1.0.0</Version>
<Description>Local, privacy-first ATS resume scanner</Description>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>PublishTrimmed requires verifying that PdfPig and DocumentFormat.OpenXml are trimmer-safe. If trimming breaks PDF/DOCX parsing at runtime, remove -p:PublishTrimmed=true and note that binaries will be ~80–100 MB instead of ~30 MB.
The repository’s .github/workflows/release.yml builds and attaches all five CLI binaries automatically when a version tag is pushed:
name: Release
on:
push:
tags: ['v*']
jobs:
build:
strategy:
matrix:
include:
- rid: win-x64
os: windows-latest
ext: .exe
- rid: osx-arm64
os: macos-latest
ext: ''
- rid: osx-x64
os: macos-latest
ext: ''
- rid: linux-x64
os: ubuntu-latest
ext: ''
- rid: linux-arm64
os: ubuntu-latest
ext: ''
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- name: Publish
run: >
dotnet publish src/AtsScanner.Cli -c Release -r ${{ matrix.rid }}
--self-contained true
-p:PublishSingleFile=true
-o ./dist/${{ matrix.rid }}
- name: Package
shell: bash
run: |
cd dist/${{ matrix.rid }}
zip -r ../../ats-scanner-${{ matrix.rid }}.zip .
- uses: actions/upload-artifact@v4
with:
name: ats-scanner-${{ matrix.rid }}
path: ats-scanner-${{ matrix.rid }}.zip
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
- uses: softprops/action-gh-release@v2
with:
files: '**/*.zip'
generate_release_notes: trueWindows (PowerShell):
# Download, unzip, optionally add to PATH
Invoke-WebRequest https://github.com/your-username/ASTScannerButWithPrivacy/releases/latest/download/ats-scanner-win-x64.zip -OutFile ats-scanner.zip
Expand-Archive ats-scanner.zip -DestinationPath $env:LOCALAPPDATA\ats-scanner
# Add $env:LOCALAPPDATA\ats-scanner to $env:PATH in System settingsmacOS / Linux:
curl -Lo ats-scanner.zip https://github.com/your-username/ASTScannerButWithPrivacy/releases/latest/download/ats-scanner-linux-x64.zip
unzip ats-scanner.zip -d ~/.local/bin/
chmod +x ~/.local/bin/ats-scannerUse ats-scanner-linux-arm64.zip instead on Linux ARM64 systems.
The release workflow also publishes GUI packages named ats-scanner-gui-<rid>.zip, including ats-scanner-gui-linux-arm64.zip for Linux ARM64 desktops.
Best for users who already have .NET installed. One command installs from anywhere.
dotnet tool install -g AtsScanner
ats-scanner scan resume.pdf- Change the project SDK and properties in
src/AtsScanner.Cli/AtsScanner.Cli.csproj:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>ats-scanner</ToolCommandName>
<PackageId>AtsScanner</PackageId>
<Version>1.0.0</Version>
<Authors>Your Name</Authors>
<Description>Local, privacy-first ATS resume scanner for Workday, Greenhouse, Taleo, Lever, SuccessFactors, Haufe-umantis, digitalent.ch, SmartRecruiters, Avature, Personio, and prospective.ch</Description>
<PackageTags>ats;resume;cv;scanner;career</PackageTags>
<PackageProjectUrl>https://github.com/your-username/ASTScannerButWithPrivacy</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/your-username/ASTScannerButWithPrivacy</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>- Pack and push:
dotnet pack src/AtsScanner.Cli -c Release -o ./nupkg
dotnet nuget push ./nupkg/AtsScanner.1.0.0.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json- GitHub Actions — automate on tag push (add to the release workflow above):
publish-nuget:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- run: dotnet pack src/AtsScanner.Cli -c Release -o ./nupkg
- run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.jsonStore NUGET_API_KEY as a GitHub Actions secret.
- NuGet package IDs are globally unique — check nuget.org that
AtsScanneris available before committing to it. - The tool runs on any platform where .NET 10 is installed, so no per-platform builds are needed.
TargetFrameworkcan be a list (net10.0;net9.0) to support older runtimes.
Lets Windows users install with winget install AtsScanner. Best reached through the winget-pkgs community repository.
- A published GitHub Release with a
.zipor.exeinstaller (Option 1 must be done first) - The release asset URL and its SHA256 hash
Create three files locally, then submit via PR to microsoft/winget-pkgs:
manifests/y/YourName/AtsScanner/1.0.0/YourName.AtsScanner.installer.yaml
PackageIdentifier: YourName.AtsScanner
PackageVersion: 1.0.0
MinimumOSVersion: '10.0.0.0'
InstallerType: zip
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: ats-scanner.exe
PortableCommandAlias: ats-scanner
Installers:
- Architecture: x64
InstallerUrl: https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-win-x64.zip
InstallerSha256: <SHA256_OF_ZIP>
ManifestType: installer
ManifestVersion: 1.6.0manifests/y/YourName/AtsScanner/1.0.0/YourName.AtsScanner.locale.en-US.yaml
PackageIdentifier: YourName.AtsScanner
PackageVersion: 1.0.0
PackageLocale: en-US
Publisher: Your Name
PackageName: ATS Scanner
ShortDescription: Local, privacy-first ATS resume scanner
License: MIT
PackageUrl: https://github.com/your-username/ASTScannerButWithPrivacy
ManifestType: defaultLocale
ManifestVersion: 1.6.0manifests/y/YourName/AtsScanner/1.0.0/YourName.AtsScanner.yaml
PackageIdentifier: YourName.AtsScanner
PackageVersion: 1.0.0
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.6.0winget install YourName.AtsScanner
ats-scanner scan resume.pdfLets macOS and Linux users install with brew install. The easiest path is a personal tap first, then applying to homebrew-core once the project has traction.
- Create a new GitHub repository named
homebrew-ats-scanner - Add a formula file
Formula/ats-scanner.rb:
class AtsScanner < Formula
desc "Local, privacy-first ATS resume scanner"
homepage "https://github.com/your-username/ASTScannerButWithPrivacy"
version "1.0.0"
license "MIT"
on_macos do
on_arm do
url "https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-osx-arm64.zip"
sha256 "<SHA256_OF_OSX_ARM64_ZIP>"
end
on_intel do
url "https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-osx-x64.zip"
sha256 "<SHA256_OF_OSX_X64_ZIP>"
end
end
on_linux do
on_arm do
url "https://github.com/your-username/ATSScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-linux-arm64.zip"
sha256 "<SHA256_OF_LINUX_ARM64_ZIP>"
end
on_intel do
url "https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-linux-x64.zip"
sha256 "<SHA256_OF_LINUX_X64_ZIP>"
end
end
def install
bin.install "ats-scanner"
end
test do
assert_match "ATS Scanner", shell_output("#{bin}/ats-scanner --version")
end
endbrew tap your-username/ats-scanner
brew install ats-scannerSubmit a PR to Homebrew/homebrew-core once the project has at least 75 stars and a stable release. Run brew audit --new ats-scanner locally first to validate the formula meets their requirements.
Similar to Homebrew but for Windows. No review required for a personal bucket.
- Create a GitHub repository named
scoop-ats-scanner - Add
bucket/ats-scanner.json:
{
"version": "1.0.0",
"description": "Local, privacy-first ATS resume scanner",
"homepage": "https://github.com/your-username/ASTScannerButWithPrivacy",
"license": "MIT",
"architecture": {
"64bit": {
"url": "https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v1.0.0/ats-scanner-win-x64.zip",
"hash": "<SHA256>"
}
},
"bin": "ats-scanner.exe",
"checkver": {
"github": "https://github.com/your-username/ASTScannerButWithPrivacy"
},
"autoupdate": {
"architecture": {
"64bit": {
"url": "https://github.com/your-username/ASTScannerButWithPrivacy/releases/download/v$version/ats-scanner-win-x64.zip"
}
}
}
}scoop bucket add ats-scanner https://github.com/your-username/scoop-ats-scanner
scoop install ats-scanner| Phase | Action | Effort | Audience |
|---|---|---|---|
| 1 | Set up GitHub Actions release workflow (Option 1) | Low | Technical users comfortable with downloading zips |
| 2 | Publish as dotnet global tool on NuGet.org (Option 2) | Low | .NET developers |
| 3 | Submit to WinGet (Option 3) | Medium | Windows users |
| 4 | Create personal Homebrew tap (Option 4) | Medium | macOS / Linux users |
| 5 | Submit to homebrew-core / Scoop main | High | Broad general audience |
Use Semantic Versioning: MAJOR.MINOR.PATCH
- Bump PATCH for bug fixes and updated ATS profile rules
- Bump MINOR for new platforms or new CLI features
- Bump MAJOR for breaking changes to output format or options
Tag releases as v1.0.0, v1.1.0, etc. The GitHub Actions workflow triggers on any v* tag.
To release:
git tag v1.0.0
git push origin v1.0.0