From 2b40ed7d7eb7911cc08b5d346dc5b6c89c74e3ec Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 15:32:19 +1000 Subject: [PATCH 01/10] feat: add winget, Homebrew, and AUR packaging with automated release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - release.yml: tag-triggered workflow cross-compiles all five platform binaries and publishes them as GitHub Release assets (cpool-*.zip) - winget-releaser.yml: auto-submits PiersSinclair.CardPool to microsoft/winget-pkgs on each release using winget-releaser action - homebrew-releaser.yml: updates SHA256 hashes and version in piers-sinclair/homebrew-cpool tap on each release - packaging/winget: reference manifests for v1.2.1 (zip+portable, x64 and arm64) - packaging/homebrew/cpool.rb: multi-platform formula (macOS arm64/x64, Linux x64) - packaging/aur: PKGBUILD and .SRCINFO for Arch User Repository (linux-x64) - README: add winget, Homebrew, and AUR install options (Options B–D) - CLAUDE.md: document new release automation and all distribution channels Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/homebrew-releaser.yml | 52 +++++++++++++++++++ .github/workflows/release.yml | 44 ++++++++++++++++ .github/workflows/winget-releaser.yml | 15 ++++++ CLAUDE.md | 39 ++++++++++++-- README.md | 31 ++++++++++- packaging/aur/.SRCINFO | 11 ++++ packaging/aur/PKGBUILD | 14 +++++ packaging/homebrew/cpool.rb | 29 +++++++++++ .../PiersSinclair.CardPool.installer.yaml | 17 ++++++ .../PiersSinclair.CardPool.locale.en-US.yaml | 21 ++++++++ .../1.2.1/PiersSinclair.CardPool.yaml | 5 ++ 11 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/homebrew-releaser.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/winget-releaser.yml create mode 100644 packaging/aur/.SRCINFO create mode 100644 packaging/aur/PKGBUILD create mode 100644 packaging/homebrew/cpool.rb create mode 100644 packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml create mode 100644 packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml create mode 100644 packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml diff --git a/.github/workflows/homebrew-releaser.yml b/.github/workflows/homebrew-releaser.yml new file mode 100644 index 0000000..a97f1f5 --- /dev/null +++ b/.github/workflows/homebrew-releaser.yml @@ -0,0 +1,52 @@ +name: Homebrew Release + +on: + release: + types: [published] + +jobs: + homebrew: + runs-on: ubuntu-latest + steps: + - name: Checkout tap + uses: actions/checkout@v6 + with: + repository: piers-sinclair/homebrew-cpool + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + + - name: Update formula + env: + VERSION: ${{ github.ref_name }} + run: | + set -euo pipefail + VERSION="${VERSION#v}" + BASE="https://github.com/piers-sinclair/cardpool/releases/download/v${VERSION}" + + get_sha256() { curl -fsSL "$1" | sha256sum | awk '{print $1}'; } + + SHA_OSX_ARM64=$(get_sha256 "${BASE}/cpool-osx-arm64.zip") + SHA_OSX_X64=$( get_sha256 "${BASE}/cpool-osx-x64.zip") + SHA_LINUX_X64=$(get_sha256 "${BASE}/cpool-linux-x64.zip") + + sed -i "s/version \"[0-9.]*\"/version \"${VERSION}\"/" Formula/cpool.rb + + awk \ + -v osx_arm64="${SHA_OSX_ARM64}" \ + -v osx_x64="${SHA_OSX_X64}" \ + -v linux_x64="${SHA_LINUX_X64}" ' + /cpool-osx-arm64\.zip/ { print; getline; sub(/sha256 "[0-9a-f]+"/, "sha256 \"" osx_arm64 "\""); print; next } + /cpool-osx-x64\.zip/ { print; getline; sub(/sha256 "[0-9a-f]+"/, "sha256 \"" osx_x64 "\""); print; next } + /cpool-linux-x64\.zip/ { print; getline; sub(/sha256 "[0-9a-f]+"/, "sha256 \"" linux_x64 "\""); print; next } + { print } + ' Formula/cpool.rb > Formula/cpool.rb.tmp + mv Formula/cpool.rb.tmp Formula/cpool.rb + + - name: Commit and push + env: + VERSION: ${{ github.ref_name }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/cpool.rb + git commit -m "cpool ${VERSION}" + git push diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c6263df --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,44 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Publish all platforms + run: | + for rid in win-x64 win-arm64 osx-arm64 osx-x64 linux-x64; do + dotnet publish src/CardPool.Cli -p:PublishProfile=$rid -o dist/$rid + done + + - name: Create archives + run: | + zip -j cpool-win-x64.zip dist/win-x64/cpool.exe + zip -j cpool-win-arm64.zip dist/win-arm64/cpool.exe + zip -j cpool-osx-arm64.zip dist/osx-arm64/cpool + zip -j cpool-osx-x64.zip dist/osx-x64/cpool + zip -j cpool-linux-x64.zip dist/linux-x64/cpool + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: cpool-*.zip + generate_release_notes: true diff --git a/.github/workflows/winget-releaser.yml b/.github/workflows/winget-releaser.yml new file mode 100644 index 0000000..6288cf2 --- /dev/null +++ b/.github/workflows/winget-releaser.yml @@ -0,0 +1,15 @@ +name: Winget Release + +on: + release: + types: [published] + +jobs: + winget: + runs-on: windows-latest + steps: + - uses: vedantmgoyal9/winget-releaser@v2 + with: + identifier: PiersSinclair.CardPool + installers-regex: 'cpool-win-(x64|arm64)\.zip$' + token: ${{ secrets.WINGET_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index cbf3d39..60cf235 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,16 +96,49 @@ dotnet test tests/CardPool.Tests ## Distribution -The CLI is packaged as both a **.NET Global Tool** and a **self-contained single-file executable**. +The CLI is distributed via **.NET Global Tool** (NuGet), **winget**, **Homebrew**, **AUR**, and **self-contained single-file executables**. -### Global Tool +### GitHub Releases (automated) + +Pushing a `v*.*.*` tag triggers `.github/workflows/release.yml`, which cross-compiles all five platform binaries from ubuntu-latest and publishes them as a GitHub Release: + +``` +cpool-win-x64.zip, cpool-win-arm64.zip, cpool-osx-arm64.zip, cpool-osx-x64.zip, cpool-linux-x64.zip +``` + +After the release is published, two dependent workflows fire automatically: +- `winget-releaser.yml` — submits a PR to `microsoft/winget-pkgs` (requires `WINGET_TOKEN` secret) +- `homebrew-releaser.yml` — updates `Formula/cpool.rb` in `piers-sinclair/homebrew-cpool` (requires `HOMEBREW_TAP_TOKEN` secret) + +### Global Tool (NuGet) + +Published to nuget.org automatically on merge to main via `publish.yml`. PackageId is `CardPool`, command is `cpool`. ```bash dotnet pack src/CardPool.Cli -c Release -o dist/ dotnet tool install -g CardPool --add-source dist/ ``` -The `.nupkg` is produced by `true` in the csproj. PackageId is `CardPool`, command is `cpool`. +The `.nupkg` is produced by `true` in the csproj. + +### winget (Windows) + +Reference manifests live in `packaging/winget/manifests/p/PiersSinclair/CardPool//`. The `winget-releaser.yml` workflow auto-submits the real manifests (with computed SHA256 hashes) to `microsoft/winget-pkgs` on each release. + +Package identifier: `PiersSinclair.CardPool` — installer type `zip` with nested `portable` exe, adds `cpool` to PATH. + +### Homebrew (macOS + Linux) + +The formula template lives in `packaging/homebrew/cpool.rb` and is also kept in the tap repo `piers-sinclair/homebrew-cpool` at `Formula/cpool.rb`. The `homebrew-releaser.yml` workflow updates the SHA256 hashes and version in the tap repo on each release. + +```bash +brew tap piers-sinclair/cpool +brew install cpool +``` + +### AUR (Arch Linux) + +The `PKGBUILD` and `.SRCINFO` live in `packaging/aur/`. To publish a new version, update `pkgver`, `sha256sums_x86_64`, and `.SRCINFO`, then push to `aur.archlinux.org/cpool.git`. ### Self-contained executables diff --git a/README.md b/README.md index 2dfcd23..6cf47db 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,34 @@ cpool --help To update: `dotnet tool update -g CardPool` To remove: `dotnet tool uninstall -g CardPool` -### Option B — Self-contained executable (no .NET required) +### Option B — winget (Windows) + +```powershell +winget install PiersSinclair.CardPool +``` + +To update: `winget upgrade PiersSinclair.CardPool` +To remove: `winget uninstall PiersSinclair.CardPool` + +### Option C — Homebrew (macOS + Linux) + +```bash +brew tap piers-sinclair/cpool +brew install cpool +``` + +To update: `brew upgrade cpool` +To remove: `brew uninstall cpool && brew untap piers-sinclair/cpool` + +### Option D — AUR (Arch Linux) + +```bash +yay -S cpool +``` + +Or with any other AUR helper, or manually: `git clone https://aur.archlinux.org/cpool.git && cd cpool && makepkg -si` + +### Option E — Self-contained executable (direct download) Download the latest zip for your platform from the [Releases](https://github.com/piers-sinclair/cardpool/releases) page: @@ -48,7 +75,7 @@ Download the latest zip for your platform from the [Releases](https://github.com To uninstall — Windows: run `uninstall.ps1`; macOS/Linux: run `bash uninstall.sh`. -### Option C — Run from source +### Option F — Run from source ```bash git clone https://github.com/piers-sinclair/cardpool diff --git a/packaging/aur/.SRCINFO b/packaging/aur/.SRCINFO new file mode 100644 index 0000000..fab7536 --- /dev/null +++ b/packaging/aur/.SRCINFO @@ -0,0 +1,11 @@ +pkgbase = cpool + pkgdesc = Card pool generator for trading card game formats + pkgver = 1.2.1 + pkgrel = 1 + url = https://github.com/piers-sinclair/cardpool + arch = x86_64 + license = MIT + source_x86_64 = cpool-1.2.1.zip::https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-linux-x64.zip + sha256sums_x86_64 = 0000000000000000000000000000000000000000000000000000000000000000 + +pkgname = cpool diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD new file mode 100644 index 0000000..f7e310b --- /dev/null +++ b/packaging/aur/PKGBUILD @@ -0,0 +1,14 @@ +# Maintainer: Piers Sinclair +pkgname=cpool +pkgver=1.2.1 +pkgrel=1 +pkgdesc='Card pool generator for trading card game formats' +arch=('x86_64') +url='https://github.com/piers-sinclair/cardpool' +license=('MIT') +source_x86_64=("${pkgname}-${pkgver}.zip::https://github.com/piers-sinclair/cardpool/releases/download/v${pkgver}/cpool-linux-x64.zip") +sha256sums_x86_64=('0000000000000000000000000000000000000000000000000000000000000000') + +package() { + install -Dm755 "${srcdir}/cpool" "${pkgdir}/usr/bin/cpool" +} diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb new file mode 100644 index 0000000..8fab04c --- /dev/null +++ b/packaging/homebrew/cpool.rb @@ -0,0 +1,29 @@ +class Cpool < Formula + desc "Card pool generator for trading card game formats" + homepage "https://github.com/piers-sinclair/cardpool" + version "1.2.1" + license "MIT" + + on_macos do + if Hardware::CPU.arm? + url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-arm64.zip" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + else + url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-x64.zip" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + end + + on_linux do + url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-linux-x64.zip" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + + def install + bin.install "cpool" + end + + test do + assert_match version.to_s, shell_output("#{bin}/cpool --version") + end +end diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml new file mode 100644 index 0000000..22cf478 --- /dev/null +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml @@ -0,0 +1,17 @@ +PackageIdentifier: PiersSinclair.CardPool +PackageVersion: 1.2.1 +InstallerLocale: en-US +InstallerType: zip +NestedInstallerType: portable +NestedInstallerFiles: + - RelativeFilePath: cpool.exe + PortableCommandAlias: cpool +Installers: + - Architecture: x64 + InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-win-x64.zip + InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 + - Architecture: arm64 + InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-win-arm64.zip + InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 +ManifestType: installer +ManifestVersion: 1.9.0 diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml new file mode 100644 index 0000000..e1f1721 --- /dev/null +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml @@ -0,0 +1,21 @@ +PackageIdentifier: PiersSinclair.CardPool +PackageVersion: 1.2.1 +PackageLocale: en-US +Publisher: Piers Sinclair +PublisherUrl: https://github.com/piers-sinclair +PackageName: CardPool +PackageUrl: https://github.com/piers-sinclair/cardpool +License: MIT +LicenseUrl: https://github.com/piers-sinclair/cardpool/blob/main/LICENSE +ShortDescription: Card pool generator for trading card game formats +Description: |- + CardPool fetches Yu-Gi-Oh! cards from YGOProDeck, enriches them with errata history + from Yugipedia, applies configurable word-count rules, and exports eligible card pools + to Excel/CSV. Designed for Edison format play. +Tags: + - yugioh + - tcg + - card-pool + - cli +ManifestType: defaultLocale +ManifestVersion: 1.9.0 diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml new file mode 100644 index 0000000..f1d5314 --- /dev/null +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml @@ -0,0 +1,5 @@ +PackageIdentifier: PiersSinclair.CardPool +PackageVersion: 1.2.1 +DefaultLocale: en-US +ManifestType: version +ManifestVersion: 1.9.0 From dfb0c4cf24741a1f74b3707d304f6ffcb443dfd0 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 15:49:09 +1000 Subject: [PATCH 02/10] fix: remove AUR, fix version to 1.2.2, add packaging validation and smoke tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove packaging/aur/ (Homebrew on Linux covers the use case adequately) - Remove AUR option from README and CLAUDE.md - Rename winget manifest directory 1.2.1 → 1.2.2 and update all version refs - Update Homebrew formula version to 1.2.2 - Fix publish.yml trigger: tags (v*.*.*) instead of every push to main, so NuGet is published only when a release is cut, not on every commit - Add validate-packaging.yml: runs on PRs touching packaging/ — winget validate on Windows runner, brew style on macOS runner - Add smoke-test.yml: triggered by workflow_run after homebrew-releaser succeeds, installs cpool from the Homebrew tap on macOS and Linux and asserts version output; winget install can be triggered manually (workflow_dispatch) after winget-pkgs PR merges Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/publish.yml | 4 +- .github/workflows/smoke-test.yml | 65 +++++++++++++++++++ .github/workflows/validate-packaging.yml | 41 ++++++++++++ CLAUDE.md | 8 +-- README.md | 12 +--- packaging/aur/.SRCINFO | 11 ---- packaging/aur/PKGBUILD | 14 ---- packaging/homebrew/cpool.rb | 2 +- .../PiersSinclair.CardPool.installer.yaml | 6 +- .../PiersSinclair.CardPool.locale.en-US.yaml | 2 +- .../PiersSinclair.CardPool.yaml | 2 +- 11 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/smoke-test.yml create mode 100644 .github/workflows/validate-packaging.yml delete mode 100644 packaging/aur/.SRCINFO delete mode 100644 packaging/aur/PKGBUILD rename packaging/winget/manifests/p/PiersSinclair/CardPool/{1.2.1 => 1.2.2}/PiersSinclair.CardPool.installer.yaml (85%) rename packaging/winget/manifests/p/PiersSinclair/CardPool/{1.2.1 => 1.2.2}/PiersSinclair.CardPool.locale.en-US.yaml (96%) rename packaging/winget/manifests/p/PiersSinclair/CardPool/{1.2.1 => 1.2.2}/PiersSinclair.CardPool.yaml (83%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fcc8511..b579f66 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,8 +2,8 @@ name: Publish to NuGet on: push: - branches: - - main + tags: + - 'v*.*.*' workflow_dispatch: jobs: diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml new file mode 100644 index 0000000..a9c683f --- /dev/null +++ b/.github/workflows/smoke-test.yml @@ -0,0 +1,65 @@ +name: Package Manager Smoke Test + +on: + workflow_run: + workflows: ["Homebrew Release"] + types: [completed] + workflow_dispatch: + inputs: + test_winget: + description: 'Test winget install (run after winget-pkgs PR is merged)' + type: boolean + default: false + +jobs: + homebrew-macos: + if: > + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || + github.event_name == 'workflow_dispatch' + runs-on: macos-latest + steps: + - name: Install from tap + run: | + brew tap piers-sinclair/cpool + brew install cpool + + - name: Verify version + run: | + VERSION=$(cpool --version) + echo "Installed version: $VERSION" + [[ -n "$VERSION" ]] || { echo "cpool --version returned empty output"; exit 1; } + + homebrew-linux: + if: > + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || + github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - name: Install Homebrew + run: | + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + echo "/home/linuxbrew/.linuxbrew/bin" >> "$GITHUB_PATH" + + - name: Install from tap + run: | + brew tap piers-sinclair/cpool + brew install cpool + + - name: Verify version + run: | + VERSION=$(cpool --version) + echo "Installed version: $VERSION" + [[ -n "$VERSION" ]] || { echo "cpool --version returned empty output"; exit 1; } + + winget: + if: github.event_name == 'workflow_dispatch' && inputs.test_winget + runs-on: windows-latest + steps: + - name: Install via winget + run: winget install PiersSinclair.CardPool --accept-source-agreements --accept-package-agreements + + - name: Verify version + run: | + $version = cpool --version + Write-Output "Installed version: $version" + if (-not $version) { Write-Error "cpool --version returned empty output"; exit 1 } diff --git a/.github/workflows/validate-packaging.yml b/.github/workflows/validate-packaging.yml new file mode 100644 index 0000000..82b3315 --- /dev/null +++ b/.github/workflows/validate-packaging.yml @@ -0,0 +1,41 @@ +name: Validate Packaging + +on: + push: + branches: + - main + paths: + - 'packaging/**' + - '.github/workflows/validate-packaging.yml' + pull_request: + paths: + - 'packaging/**' + - '.github/workflows/validate-packaging.yml' + workflow_dispatch: + +jobs: + winget: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Resolve manifest version + id: ver + shell: pwsh + run: | + $version = (Select-String -Path src/CardPool.Cli/CardPool.Cli.csproj -Pattern '(.*?)').Matches[0].Groups[1].Value + echo "version=$version" >> $env:GITHUB_OUTPUT + + - name: Validate winget manifest + shell: pwsh + run: winget validate packaging/winget/manifests/p/PiersSinclair/CardPool/${{ steps.ver.outputs.version }} + + homebrew: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Check formula style + run: brew style packaging/homebrew/cpool.rb diff --git a/CLAUDE.md b/CLAUDE.md index 60cf235..f18bea0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,7 +96,7 @@ dotnet test tests/CardPool.Tests ## Distribution -The CLI is distributed via **.NET Global Tool** (NuGet), **winget**, **Homebrew**, **AUR**, and **self-contained single-file executables**. +The CLI is distributed via **.NET Global Tool** (NuGet), **winget**, **Homebrew**, and **self-contained single-file executables**. ### GitHub Releases (automated) @@ -112,7 +112,7 @@ After the release is published, two dependent workflows fire automatically: ### Global Tool (NuGet) -Published to nuget.org automatically on merge to main via `publish.yml`. PackageId is `CardPool`, command is `cpool`. +Published to nuget.org on `v*.*.*` tag push via `publish.yml` (same trigger as `release.yml`). PackageId is `CardPool`, command is `cpool`. ```bash dotnet pack src/CardPool.Cli -c Release -o dist/ @@ -136,10 +136,6 @@ brew tap piers-sinclair/cpool brew install cpool ``` -### AUR (Arch Linux) - -The `PKGBUILD` and `.SRCINFO` live in `packaging/aur/`. To publish a new version, update `pkgver`, `sha256sums_x86_64`, and `.SRCINFO`, then push to `aur.archlinux.org/cpool.git`. - ### Self-contained executables Five publish profiles live in `src/CardPool.Cli/Properties/PublishProfiles/`: diff --git a/README.md b/README.md index 6cf47db..3c409c1 100644 --- a/README.md +++ b/README.md @@ -47,15 +47,7 @@ brew install cpool To update: `brew upgrade cpool` To remove: `brew uninstall cpool && brew untap piers-sinclair/cpool` -### Option D — AUR (Arch Linux) - -```bash -yay -S cpool -``` - -Or with any other AUR helper, or manually: `git clone https://aur.archlinux.org/cpool.git && cd cpool && makepkg -si` - -### Option E — Self-contained executable (direct download) +### Option D — Self-contained executable (direct download) Download the latest zip for your platform from the [Releases](https://github.com/piers-sinclair/cardpool/releases) page: @@ -75,7 +67,7 @@ Download the latest zip for your platform from the [Releases](https://github.com To uninstall — Windows: run `uninstall.ps1`; macOS/Linux: run `bash uninstall.sh`. -### Option F — Run from source +### Option E — Run from source ```bash git clone https://github.com/piers-sinclair/cardpool diff --git a/packaging/aur/.SRCINFO b/packaging/aur/.SRCINFO deleted file mode 100644 index fab7536..0000000 --- a/packaging/aur/.SRCINFO +++ /dev/null @@ -1,11 +0,0 @@ -pkgbase = cpool - pkgdesc = Card pool generator for trading card game formats - pkgver = 1.2.1 - pkgrel = 1 - url = https://github.com/piers-sinclair/cardpool - arch = x86_64 - license = MIT - source_x86_64 = cpool-1.2.1.zip::https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-linux-x64.zip - sha256sums_x86_64 = 0000000000000000000000000000000000000000000000000000000000000000 - -pkgname = cpool diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD deleted file mode 100644 index f7e310b..0000000 --- a/packaging/aur/PKGBUILD +++ /dev/null @@ -1,14 +0,0 @@ -# Maintainer: Piers Sinclair -pkgname=cpool -pkgver=1.2.1 -pkgrel=1 -pkgdesc='Card pool generator for trading card game formats' -arch=('x86_64') -url='https://github.com/piers-sinclair/cardpool' -license=('MIT') -source_x86_64=("${pkgname}-${pkgver}.zip::https://github.com/piers-sinclair/cardpool/releases/download/v${pkgver}/cpool-linux-x64.zip") -sha256sums_x86_64=('0000000000000000000000000000000000000000000000000000000000000000') - -package() { - install -Dm755 "${srcdir}/cpool" "${pkgdir}/usr/bin/cpool" -} diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index 8fab04c..a0684c3 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,7 +1,7 @@ class Cpool < Formula desc "Card pool generator for trading card game formats" homepage "https://github.com/piers-sinclair/cardpool" - version "1.2.1" + version "1.2.2" license "MIT" on_macos do diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml similarity index 85% rename from packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml rename to packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml index 22cf478..7cc768c 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.installer.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml @@ -1,5 +1,5 @@ PackageIdentifier: PiersSinclair.CardPool -PackageVersion: 1.2.1 +PackageVersion: 1.2.2 InstallerLocale: en-US InstallerType: zip NestedInstallerType: portable @@ -8,10 +8,10 @@ NestedInstallerFiles: PortableCommandAlias: cpool Installers: - Architecture: x64 - InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-win-x64.zip + InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.2/cpool-win-x64.zip InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 - Architecture: arm64 - InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.1/cpool-win-arm64.zip + InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.2/cpool-win-arm64.zip InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 ManifestType: installer ManifestVersion: 1.9.0 diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml similarity index 96% rename from packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml rename to packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml index e1f1721..3a6ddf5 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.locale.en-US.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml @@ -1,5 +1,5 @@ PackageIdentifier: PiersSinclair.CardPool -PackageVersion: 1.2.1 +PackageVersion: 1.2.2 PackageLocale: en-US Publisher: Piers Sinclair PublisherUrl: https://github.com/piers-sinclair diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml similarity index 83% rename from packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml rename to packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml index f1d5314..6e9171b 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.1/PiersSinclair.CardPool.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml @@ -1,5 +1,5 @@ PackageIdentifier: PiersSinclair.CardPool -PackageVersion: 1.2.1 +PackageVersion: 1.2.2 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.9.0 From 12f84a8261506d9ad46d11217cf2cdf396286ccb Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:01:27 +1000 Subject: [PATCH 03/10] fix: correct formula structure, winget schema headers, trademark removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Homebrew formula: - Replace on_macos/on_linux blocks (url disallowed inside them) with top-level if OS.mac? / elsif OS.mac? / else conditionals — correct pattern for binary distros - Use unique placeholder sha256 per platform (brew style flags identical branches) - Update desc to mention 25 Format and Discord link, drop trademarked game name Winget manifests: - Add yaml-language-server schema header to all three files (winget validate warning) - Use distinct placeholder sha256 for x64 and arm64 (validate warns on duplicates) - Remove trademarked game name and Edison format ref from locale description - Add 25 Format framing and Discord link (discord.gg/wDXgNHukb) - Swap yugioh tag for 25-format validate-packaging workflow: - Add --manifest flag to winget validate (was using positional arg) - Create temporary git tap before brew style so Homebrew uses formula-scoped RuboCop config (suppresses Sorbet/FrozenStringLiteral cops that fire on raw files) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/validate-packaging.yml | 15 ++++++++++--- packaging/homebrew/cpool.rb | 22 ++++++++----------- .../PiersSinclair.CardPool.installer.yaml | 5 +++-- .../PiersSinclair.CardPool.locale.en-US.yaml | 11 +++++----- .../1.2.2/PiersSinclair.CardPool.yaml | 1 + 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/.github/workflows/validate-packaging.yml b/.github/workflows/validate-packaging.yml index 82b3315..ca2fbbd 100644 --- a/.github/workflows/validate-packaging.yml +++ b/.github/workflows/validate-packaging.yml @@ -25,11 +25,11 @@ jobs: shell: pwsh run: | $version = (Select-String -Path src/CardPool.Cli/CardPool.Cli.csproj -Pattern '(.*?)').Matches[0].Groups[1].Value - echo "version=$version" >> $env:GITHUB_OUTPUT + "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - name: Validate winget manifest shell: pwsh - run: winget validate packaging/winget/manifests/p/PiersSinclair/CardPool/${{ steps.ver.outputs.version }} + run: winget validate --manifest packaging/winget/manifests/p/PiersSinclair/CardPool/${{ steps.ver.outputs.version }} homebrew: runs-on: macos-latest @@ -37,5 +37,14 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Create temporary tap + run: | + mkdir -p /tmp/homebrew-cpool/Formula + cp packaging/homebrew/cpool.rb /tmp/homebrew-cpool/Formula/cpool.rb + git -C /tmp/homebrew-cpool init + git -C /tmp/homebrew-cpool -c user.email="ci@ci" -c user.name="CI" add Formula + git -C /tmp/homebrew-cpool -c user.email="ci@ci" -c user.name="CI" commit -m "init" + brew tap local/cpool /tmp/homebrew-cpool + - name: Check formula style - run: brew style packaging/homebrew/cpool.rb + run: brew style local/cpool/cpool diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index a0684c3..155ecfa 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,22 +1,18 @@ class Cpool < Formula - desc "Card pool generator for trading card game formats" + desc "Card pool generator for the 25 Format (discord.gg/wDXgNHukb)" homepage "https://github.com/piers-sinclair/cardpool" version "1.2.2" license "MIT" - on_macos do - if Hardware::CPU.arm? - url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-arm64.zip" - sha256 "0000000000000000000000000000000000000000000000000000000000000000" - else - url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-x64.zip" - sha256 "0000000000000000000000000000000000000000000000000000000000000000" - end - end - - on_linux do + if OS.mac? && Hardware::CPU.arm? + url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-arm64.zip" + sha256 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + elsif OS.mac? + url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-osx-x64.zip" + sha256 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + else url "https://github.com/piers-sinclair/cardpool/releases/download/v#{version}/cpool-linux-x64.zip" - sha256 "0000000000000000000000000000000000000000000000000000000000000000" + sha256 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" end def install diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml index 7cc768c..367e790 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.installer.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json PackageIdentifier: PiersSinclair.CardPool PackageVersion: 1.2.2 InstallerLocale: en-US @@ -9,9 +10,9 @@ NestedInstallerFiles: Installers: - Architecture: x64 InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.2/cpool-win-x64.zip - InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 + InstallerSha256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - Architecture: arm64 InstallerUrl: https://github.com/piers-sinclair/cardpool/releases/download/v1.2.2/cpool-win-arm64.zip - InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 + InstallerSha256: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ManifestType: installer ManifestVersion: 1.9.0 diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml index 3a6ddf5..2d5696d 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json PackageIdentifier: PiersSinclair.CardPool PackageVersion: 1.2.2 PackageLocale: en-US @@ -7,15 +8,15 @@ PackageName: CardPool PackageUrl: https://github.com/piers-sinclair/cardpool License: MIT LicenseUrl: https://github.com/piers-sinclair/cardpool/blob/main/LICENSE -ShortDescription: Card pool generator for trading card game formats +ShortDescription: Card pool generator for the 25 Format community Description: |- - CardPool fetches Yu-Gi-Oh! cards from YGOProDeck, enriches them with errata history - from Yugipedia, applies configurable word-count rules, and exports eligible card pools - to Excel/CSV. Designed for Edison format play. + CardPool fetches card data from YGOProDeck, enriches it with errata history from Yugipedia, + applies configurable word-count rules, and exports eligible card pools to Excel/CSV. + Built for the 25 Format — join the community at https://discord.gg/wDXgNHukb. Tags: - - yugioh - tcg - card-pool - cli + - 25-format ManifestType: defaultLocale ManifestVersion: 1.9.0 diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml index 6e9171b..ec1ab69 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json PackageIdentifier: PiersSinclair.CardPool PackageVersion: 1.2.2 DefaultLocale: en-US From 31de8ecda2b3f23da29a4a4af54b674fedd70143 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:07:43 +1000 Subject: [PATCH 04/10] feat: auto-release on version bump, consolidate NuGet into release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit release.yml now triggers on push to main when CardPool.Cli.csproj changes: - check job reads from csproj and calls gh release view to skip if a release for that version already exists (idempotent) - release job builds all five platforms, packs NuGet, creates GitHub Release (which auto-creates the git tag), then pushes to nuget.org - workflow_dispatch kept for manual re-runs - winget-releaser and homebrew-releaser still trigger from release:published publish.yml deleted — NuGet publish is now part of the unified release job. No manual tag push required: merge a version bump to main and all channels (GitHub Releases, NuGet, winget, Homebrew) publish automatically. Update descriptions: 'originally built for the 25 Format' framing in both the Homebrew formula desc and the winget locale, with Discord link retained in the winget long description. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/publish.yml | 31 ------------- .github/workflows/release.yml | 43 +++++++++++++++++-- CLAUDE.md | 12 ++++-- packaging/homebrew/cpool.rb | 2 +- .../PiersSinclair.CardPool.locale.en-US.yaml | 4 +- 5 files changed, 52 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index b579f66..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Publish to NuGet - -on: - push: - tags: - - 'v*.*.*' - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: '10.x' - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build src/CardPool.Cli --configuration Release --no-restore - - - name: Pack - run: dotnet pack src/CardPool.Cli --configuration Release --no-build --output ./nupkg - - - name: Push to NuGet - run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6263df..b463358 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,14 +2,44 @@ name: Release on: push: - tags: - - 'v*.*.*' + branches: + - main + paths: + - 'src/CardPool.Cli/CardPool.Cli.csproj' + workflow_dispatch: permissions: contents: write jobs: + check: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.ver.outputs.version }} + tag: ${{ steps.ver.outputs.tag }} + should-release: ${{ steps.ver.outputs.should-release }} + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Check version + id: ver + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION=$(grep -oP '(?<=)[^<]+' src/CardPool.Cli/CardPool.Cli.csproj) + TAG="v${VERSION}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "tag=${TAG}" >> $GITHUB_OUTPUT + if gh release view "${TAG}" &>/dev/null; then + echo "should-release=false" >> $GITHUB_OUTPUT + else + echo "should-release=true" >> $GITHUB_OUTPUT + fi + release: + needs: check + if: needs.check.outputs.should-release == 'true' runs-on: ubuntu-latest steps: - name: Checkout @@ -26,7 +56,7 @@ jobs: - name: Publish all platforms run: | for rid in win-x64 win-arm64 osx-arm64 osx-x64 linux-x64; do - dotnet publish src/CardPool.Cli -p:PublishProfile=$rid -o dist/$rid + dotnet publish src/CardPool.Cli -p:PublishProfile=$rid -o dist/$rid --no-restore done - name: Create archives @@ -37,8 +67,15 @@ jobs: zip -j cpool-osx-x64.zip dist/osx-x64/cpool zip -j cpool-linux-x64.zip dist/linux-x64/cpool + - name: Pack NuGet + run: dotnet pack src/CardPool.Cli --configuration Release --no-restore --output ./nupkg + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: + tag_name: ${{ needs.check.outputs.tag }} files: cpool-*.zip generate_release_notes: true + + - name: Push to NuGet + run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/CLAUDE.md b/CLAUDE.md index f18bea0..3749c2b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,19 +100,25 @@ The CLI is distributed via **.NET Global Tool** (NuGet), **winget**, **Homebrew* ### GitHub Releases (automated) -Pushing a `v*.*.*` tag triggers `.github/workflows/release.yml`, which cross-compiles all five platform binaries from ubuntu-latest and publishes them as a GitHub Release: +Merging a `` bump in `CardPool.Cli.csproj` to `main` triggers `release.yml`, which: +1. Checks whether a GitHub Release for that version already exists — skips everything if so +2. Cross-compiles all five platform binaries from ubuntu-latest +3. Creates a GitHub Release tagged `v` with the zips attached +4. Pushes the NuGet package to nuget.org ``` cpool-win-x64.zip, cpool-win-arm64.zip, cpool-osx-arm64.zip, cpool-osx-x64.zip, cpool-linux-x64.zip ``` -After the release is published, two dependent workflows fire automatically: +After the GitHub Release is published, two dependent workflows fire automatically: - `winget-releaser.yml` — submits a PR to `microsoft/winget-pkgs` (requires `WINGET_TOKEN` secret) - `homebrew-releaser.yml` — updates `Formula/cpool.rb` in `piers-sinclair/homebrew-cpool` (requires `HOMEBREW_TAP_TOKEN` secret) +`release.yml` also accepts `workflow_dispatch` for manual re-runs. + ### Global Tool (NuGet) -Published to nuget.org on `v*.*.*` tag push via `publish.yml` (same trigger as `release.yml`). PackageId is `CardPool`, command is `cpool`. +Published to nuget.org as part of `release.yml` (consolidated — no separate publish workflow). PackageId is `CardPool`, command is `cpool`. ```bash dotnet pack src/CardPool.Cli -c Release -o dist/ diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index 155ecfa..88ebe14 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,5 +1,5 @@ class Cpool < Formula - desc "Card pool generator for the 25 Format (discord.gg/wDXgNHukb)" + desc "Card pool generator for trading card games, originally built for the 25 Format" homepage "https://github.com/piers-sinclair/cardpool" version "1.2.2" license "MIT" diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml index 2d5696d..17b0f31 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml @@ -8,11 +8,11 @@ PackageName: CardPool PackageUrl: https://github.com/piers-sinclair/cardpool License: MIT LicenseUrl: https://github.com/piers-sinclair/cardpool/blob/main/LICENSE -ShortDescription: Card pool generator for the 25 Format community +ShortDescription: Card pool generator for trading card games Description: |- CardPool fetches card data from YGOProDeck, enriches it with errata history from Yugipedia, applies configurable word-count rules, and exports eligible card pools to Excel/CSV. - Built for the 25 Format — join the community at https://discord.gg/wDXgNHukb. + Originally built for the 25 Format — join the community at https://discord.gg/wDXgNHukb. Tags: - tcg - card-pool From 59ae5af57b089399e6e4e7d3bbf833664bcc5a8f Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:08:33 +1000 Subject: [PATCH 05/10] feat: add Discord community link to all package manager descriptions Homebrew desc and winget ShortDescription now both include the 25 Format Discord invite (discord.gg/wDXgNHukb) to encourage community discovery. Framing is 'originally built for the 25 Format' throughout to avoid implying the tool is exclusively for that format. Co-Authored-By: Claude Sonnet 4.6 --- packaging/homebrew/cpool.rb | 2 +- .../CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index 88ebe14..0c37285 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,5 +1,5 @@ class Cpool < Formula - desc "Card pool generator for trading card games, originally built for the 25 Format" + desc "Card pool generator for trading card games — join the 25 Format community at discord.gg/wDXgNHukb" homepage "https://github.com/piers-sinclair/cardpool" version "1.2.2" license "MIT" diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml index 17b0f31..3334398 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml @@ -8,7 +8,7 @@ PackageName: CardPool PackageUrl: https://github.com/piers-sinclair/cardpool License: MIT LicenseUrl: https://github.com/piers-sinclair/cardpool/blob/main/LICENSE -ShortDescription: Card pool generator for trading card games +ShortDescription: Card pool generator for trading card games — join the 25 Format community at discord.gg/wDXgNHukb Description: |- CardPool fetches card data from YGOProDeck, enriches it with errata history from Yugipedia, applies configurable word-count rules, and exports eligible card pools to Excel/CSV. From ea1135e7a6ea7250d9e941f02033c80450cba0a7 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:10:08 +1000 Subject: [PATCH 06/10] fix: clarify 'originally built for' framing in all package manager descriptions Both Homebrew desc and winget ShortDescription/Description now lead with 'originally built for the 25 Format' and close with a community invite, making clear the origin while welcoming all TCG use cases. Co-Authored-By: Claude Sonnet 4.6 --- packaging/homebrew/cpool.rb | 2 +- .../CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index 0c37285..e2a20c5 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,5 +1,5 @@ class Cpool < Formula - desc "Card pool generator for trading card games — join the 25 Format community at discord.gg/wDXgNHukb" + desc "Card pool generator originally built for the 25 Format — join us at discord.gg/wDXgNHukb" homepage "https://github.com/piers-sinclair/cardpool" version "1.2.2" license "MIT" diff --git a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml index 3334398..89b2c77 100644 --- a/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml +++ b/packaging/winget/manifests/p/PiersSinclair/CardPool/1.2.2/PiersSinclair.CardPool.locale.en-US.yaml @@ -8,11 +8,11 @@ PackageName: CardPool PackageUrl: https://github.com/piers-sinclair/cardpool License: MIT LicenseUrl: https://github.com/piers-sinclair/cardpool/blob/main/LICENSE -ShortDescription: Card pool generator for trading card games — join the 25 Format community at discord.gg/wDXgNHukb +ShortDescription: Card pool generator originally built for the 25 Format — join the community at discord.gg/wDXgNHukb Description: |- CardPool fetches card data from YGOProDeck, enriches it with errata history from Yugipedia, applies configurable word-count rules, and exports eligible card pools to Excel/CSV. - Originally built for the 25 Format — join the community at https://discord.gg/wDXgNHukb. + Originally built for the 25 Format trading card game community — join us at https://discord.gg/wDXgNHukb. Tags: - tcg - card-pool From ad6de62ae572ff16ac688d64253a1e03a18d0dc5 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:13:37 +1000 Subject: [PATCH 07/10] fix: shorten Homebrew desc to under 80 chars (brew style limit) --- packaging/homebrew/cpool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/homebrew/cpool.rb b/packaging/homebrew/cpool.rb index e2a20c5..5d1d566 100644 --- a/packaging/homebrew/cpool.rb +++ b/packaging/homebrew/cpool.rb @@ -1,5 +1,5 @@ class Cpool < Formula - desc "Card pool generator originally built for the 25 Format — join us at discord.gg/wDXgNHukb" + desc "Card pool generator originally built for the 25 Format — discord.gg/wDXgNHukb" homepage "https://github.com/piers-sinclair/cardpool" version "1.2.2" license "MIT" From d13390377252370238e1316fd0a8b9c79914ada1 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:22:23 +1000 Subject: [PATCH 08/10] fix: homebrew-releaser copies full formula from cardpool repo before patching hashes Previously only sed/awk-patched version+sha256 in the tap, meaning any structural change to packaging/homebrew/cpool.rb (install logic, test, URL pattern) would silently never reach the tap. Now: checkout both repos, cp the full formula file from cardpool into the tap, then stamp in real sha256 values. packaging/homebrew/cpool.rb is the single source of truth for formula structure. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/homebrew-releaser.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/homebrew-releaser.yml b/.github/workflows/homebrew-releaser.yml index a97f1f5..73c3820 100644 --- a/.github/workflows/homebrew-releaser.yml +++ b/.github/workflows/homebrew-releaser.yml @@ -8,13 +8,19 @@ jobs: homebrew: runs-on: ubuntu-latest steps: + - name: Checkout cardpool + uses: actions/checkout@v6 + with: + path: cardpool + - name: Checkout tap uses: actions/checkout@v6 with: repository: piers-sinclair/homebrew-cpool token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + path: tap - - name: Update formula + - name: Sync formula and update hashes env: VERSION: ${{ github.ref_name }} run: | @@ -28,7 +34,9 @@ jobs: SHA_OSX_X64=$( get_sha256 "${BASE}/cpool-osx-x64.zip") SHA_LINUX_X64=$(get_sha256 "${BASE}/cpool-linux-x64.zip") - sed -i "s/version \"[0-9.]*\"/version \"${VERSION}\"/" Formula/cpool.rb + cp cardpool/packaging/homebrew/cpool.rb tap/Formula/cpool.rb + + sed -i "s/version \"[0-9.]*\"/version \"${VERSION}\"/" tap/Formula/cpool.rb awk \ -v osx_arm64="${SHA_OSX_ARM64}" \ @@ -38,12 +46,13 @@ jobs: /cpool-osx-x64\.zip/ { print; getline; sub(/sha256 "[0-9a-f]+"/, "sha256 \"" osx_x64 "\""); print; next } /cpool-linux-x64\.zip/ { print; getline; sub(/sha256 "[0-9a-f]+"/, "sha256 \"" linux_x64 "\""); print; next } { print } - ' Formula/cpool.rb > Formula/cpool.rb.tmp - mv Formula/cpool.rb.tmp Formula/cpool.rb + ' tap/Formula/cpool.rb > tap/Formula/cpool.rb.tmp + mv tap/Formula/cpool.rb.tmp tap/Formula/cpool.rb - name: Commit and push env: VERSION: ${{ github.ref_name }} + working-directory: tap run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" From adde2a194b3fcb7cf0c279fd771abc1cabc18610 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:28:51 +1000 Subject: [PATCH 09/10] docs: document release secrets renewal and Homebrew formula sync process Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3749c2b..ba8561a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -135,13 +135,51 @@ Package identifier: `PiersSinclair.CardPool` — installer type `zip` with neste ### Homebrew (macOS + Linux) -The formula template lives in `packaging/homebrew/cpool.rb` and is also kept in the tap repo `piers-sinclair/homebrew-cpool` at `Formula/cpool.rb`. The `homebrew-releaser.yml` workflow updates the SHA256 hashes and version in the tap repo on each release. +`packaging/homebrew/cpool.rb` in this repo is the **single source of truth** for the formula. Never edit `piers-sinclair/homebrew-cpool` directly. + +On each release, `homebrew-releaser.yml`: +1. Checks out both this repo and the tap repo +2. Copies `packaging/homebrew/cpool.rb` wholesale into `tap/Formula/cpool.rb` +3. Stamps in the real SHA256 hashes computed from the published zip downloads +4. Commits and pushes to `piers-sinclair/homebrew-cpool` + +To change anything about the formula (install logic, description, test, URL pattern) — edit `packaging/homebrew/cpool.rb` here and merge. The tap is updated automatically on the next release. ```bash brew tap piers-sinclair/cpool brew install cpool ``` +### Release secrets + +Three secrets are required for the automated release pipeline. All are set at: +**https://github.com/piers-sinclair/cardpool/settings/secrets/actions** + +| Secret | Used by | Expires | +|--------|---------|---------| +| `NUGET_API_KEY` | `release.yml` → NuGet push | Check nuget.org | +| `WINGET_TOKEN` | `winget-releaser.yml` → PR on microsoft/winget-pkgs | When PAT expires | +| `HOMEBREW_TAP_TOKEN` | `homebrew-releaser.yml` → push to piers-sinclair/homebrew-cpool | When PAT expires | + +**Renewing `NUGET_API_KEY`** +1. Go to **https://www.nuget.org/account/apikeys** +2. Find the existing key → **Regenerate** (or **Create** a new one scoped to `CardPool`) +3. Copy the new value → update the secret at the Actions secrets page above + +**Renewing `WINGET_TOKEN`** +1. Go to **https://github.com/settings/tokens** (classic tokens) +2. Find `winget-releaser` → **Regenerate** (keep scope: `public_repo` only) +3. Copy the new value → update the secret + +**Renewing `HOMEBREW_TAP_TOKEN`** +1. Go to **https://github.com/settings/personal-access-tokens** (fine-grained tokens) +2. Find `homebrew-tap-releaser` → **Regenerate** + - Repository access: `homebrew-cpool` only + - Permission: **Contents** → Read and write +3. Copy the new value → update the secret + +If a release workflow fails with a 401/403 error, an expired secret is the most likely cause — check the expiry dates before debugging further. + ### Self-contained executables Five publish profiles live in `src/CardPool.Cli/Properties/PublishProfiles/`: From 74f21cfc8fcbc97d30568cc045a381025186e809 Mon Sep 17 00:00:00 2001 From: Piers Sinclair Date: Sun, 31 May 2026 16:31:14 +1000 Subject: [PATCH 10/10] docs: add RELEASING.md with full maintainer release guide, link from README Covers: how to release (version bump + merge), automated pipeline steps, post-release winget smoke test, secrets setup and renewal for all three channels (NuGet/winget/Homebrew), and Homebrew formula sync process. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 4 +++ RELEASING.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 RELEASING.md diff --git a/README.md b/README.md index 3c409c1..676a48a 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,10 @@ cpool --version # show installed version - [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) +### Releasing + +See [RELEASING.md](RELEASING.md) for the full release process, including how to renew the NuGet, winget, and Homebrew secrets. + ### Run from source ```bash diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..573568f --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,79 @@ +# Releasing + +CardPool is distributed via NuGet, winget, Homebrew, and GitHub Releases. The entire pipeline is automated — the only manual step is bumping the version number. + +## How to release + +1. Update `` in `src/CardPool.Cli/CardPool.Cli.csproj` +2. Commit and merge to `main` + +That's it. The pipeline detects the version change and handles everything else automatically. + +## What happens automatically + +Merging a version bump triggers `release.yml`, which: + +1. Checks if a GitHub Release for that version already exists — skips if so (safe to re-run) +2. Cross-compiles all five platform binaries +3. Creates a GitHub Release tagged `v` with the binaries attached +4. Pushes the NuGet package to nuget.org + +Once the GitHub Release is published, two further workflows fire: + +- **`winget-releaser.yml`** — opens a pull request on [microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) with the correct installer manifest and real SHA256 hashes. Microsoft's bot reviews and merges this within a few days, after which `winget install PiersSinclair.CardPool` works. +- **`homebrew-releaser.yml`** — copies the formula template from `packaging/homebrew/cpool.rb`, stamps in real SHA256 hashes, and pushes to the [piers-sinclair/homebrew-cpool](https://github.com/piers-sinclair/homebrew-cpool) tap. `brew install cpool` works immediately after. + +## Post-release verification + +Homebrew is verified automatically: `smoke-test.yml` runs after `homebrew-releaser.yml` and installs `cpool` from the tap on both macOS and Linux. + +Winget requires a manual check once the winget-pkgs PR is merged: + +1. Go to **Actions → Package Manager Smoke Test → Run workflow** +2. Tick **"Test winget install"** and run +3. Confirm the job passes + +## Required secrets + +All three secrets are set at **https://github.com/piers-sinclair/cardpool/settings/secrets/actions**. + +| Secret | Used by | What it needs | +|--------|---------|---------------| +| `NUGET_API_KEY` | NuGet push | API key scoped to the `CardPool` package | +| `WINGET_TOKEN` | winget PR submission | Classic PAT — `public_repo` scope only | +| `HOMEBREW_TAP_TOKEN` | Homebrew tap push | Fine-grained PAT — `homebrew-cpool` repo, Contents read/write | + +### Renewing `NUGET_API_KEY` + +1. Go to **https://www.nuget.org/account/apikeys** +2. Find the existing key → **Regenerate** (or create a new one scoped to `CardPool`) +3. Paste the new value into the `NUGET_API_KEY` secret + +### Renewing `WINGET_TOKEN` + +1. Go to **https://github.com/settings/tokens** (classic tokens) +2. Find `winget-releaser` → **Regenerate**, keeping scope: `public_repo` only +3. Paste the new value into the `WINGET_TOKEN` secret + +### Renewing `HOMEBREW_TAP_TOKEN` + +1. Go to **https://github.com/settings/personal-access-tokens** (fine-grained tokens) +2. Find `homebrew-tap-releaser` → **Regenerate** + - Repository access: `homebrew-cpool` only + - Permission: **Contents** → Read and write +3. Paste the new value into the `HOMEBREW_TAP_TOKEN` secret + +> If a release workflow fails with a 401 or 403 error, an expired secret is the most likely cause. + +## Homebrew formula + +`packaging/homebrew/cpool.rb` in this repository is the **single source of truth** for the Homebrew formula. Never edit [piers-sinclair/homebrew-cpool](https://github.com/piers-sinclair/homebrew-cpool) directly. + +On each release, `homebrew-releaser.yml` copies the full formula file from this repo into the tap and replaces the placeholder SHA256 values with real hashes. Any change to the formula — description, install logic, test, URL pattern — belongs in `packaging/homebrew/cpool.rb` here and takes effect on the next release. + +## Packaging reference files + +| Path | Purpose | +|------|---------| +| `packaging/homebrew/cpool.rb` | Homebrew formula template (source of truth) | +| `packaging/winget/manifests/…/` | Winget manifest reference for the current version — the `winget-releaser` action generates the real submission automatically |