diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml index ea190c2..bdf64de 100644 --- a/.github/workflows/publish-packages.yml +++ b/.github/workflows/publish-packages.yml @@ -80,10 +80,15 @@ jobs: update-homebrew: name: Update Homebrew Formula + # Independent of `publish-crates-io`. The Homebrew formula consumes + # tarballs from the GitHub release, not crates.io — a crates.io + # publish failure (like the v0.1.0 manifest-verification regression) + # must not block channels that don't depend on crates.io. + # # Gate twofold: # 1. Skip prereleases (rc / beta / alpha) so a soak release - # does not push the stable Cargo.toml version to crates.io - # as `latest`. + # does not push the stable version to the Homebrew tap as + # `latest`. # 2. Skip tags that don't match `vX.Y.Z` shape so a chart # release (e.g. `helm-vX.Y.Z`) does not trigger CLI # publishing. workflow_dispatch (empty event.release) @@ -95,7 +100,6 @@ jobs: startsWith(github.event.release.tag_name, 'v')) }} runs-on: ubuntu-latest - needs: [publish-crates-io] steps: - uses: actions/checkout@v4 @@ -166,10 +170,19 @@ jobs: update-aur: name: Update AUR Package + # Independent of `publish-crates-io`. AUR-bin consumes the + # GitHub-release binary tarballs, not crates.io. Status of the + # underlying tarball production: BROKEN until release.yml is + # extended to produce `jarvy-v--.tar.gz` + # artifacts (see docs/release-quirks-jarvy.md). When that lands, + # this job will start succeeding for users who run `yay -S + # jarvy-bin` — the AUR push itself only writes the PKGBUILD; the + # user-side `makepkg` is what hits the tarball URL. + # # Gate twofold: # 1. Skip prereleases (rc / beta / alpha) so a soak release - # does not push the stable Cargo.toml version to crates.io - # as `latest`. + # does not push the stable version to AUR as the user-facing + # latest. # 2. Skip tags that don't match `vX.Y.Z` shape so a chart # release (e.g. `helm-vX.Y.Z`) does not trigger CLI # publishing. workflow_dispatch (empty event.release) @@ -181,7 +194,6 @@ jobs: startsWith(github.event.release.tag_name, 'v')) }} runs-on: ubuntu-latest - needs: [publish-crates-io] steps: - uses: actions/checkout@v4 @@ -192,12 +204,58 @@ jobs: VERSION="${VERSION#v}" echo "version=$VERSION" >> $GITHUB_OUTPUT + # Skip when AUR credentials aren't configured. Mirror the + # Homebrew secret-presence gate so missing setup surfaces as a + # workflow warning instead of a release-breaking failure. The + # three secrets ship together (username + email + ssh key) and + # all three are required by the AUR action. + - name: Check AUR credentials + id: aur_secret + run: | + if [ -z "${{ secrets.AUR_SSH_PRIVATE_KEY }}" ] \ + || [ -z "${{ secrets.AUR_USERNAME }}" ] \ + || [ -z "${{ secrets.AUR_EMAIL }}" ]; then + echo "::warning::AUR credentials not configured; skipping AUR push. Set AUR_SSH_PRIVATE_KEY + AUR_USERNAME + AUR_EMAIL secrets to enable AUR distribution." + echo "have_secret=false" >> $GITHUB_OUTPUT + else + echo "have_secret=true" >> $GITHUB_OUTPUT + fi + - name: Update PKGBUILD + if: steps.aur_secret.outputs.have_secret == 'true' run: | VERSION="${{ steps.version.outputs.version }}" sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" dist/aur/PKGBUILD-bin + # Compute real sha256sums from SHA256SUMS.txt. The PKGBUILD + # ships with `SHA256_PLACEHOLDER_X86` / `SHA256_PLACEHOLDER_ARM` + # tokens that previously were never substituted — the AUR push + # would land a broken PKGBUILD that fails `makepkg` integrity + # check on user `yay -S jarvy-bin`. Pull the real sha256s from + # the published checksum file; if the tarball assets aren't + # there yet (pre-tarball-production gap), this step fails loud + # so the AUR push doesn't quietly write garbage. + - name: Substitute sha256sums from release checksums + if: steps.aur_secret.outputs.have_secret == 'true' + run: | + set -euo pipefail + VERSION="${{ steps.version.outputs.version }}" + curl -fsSL \ + "https://github.com/bearbinary/jarvy/releases/download/v${VERSION}/SHA256SUMS.txt" \ + -o /tmp/sha256sums.txt + X86_SHA=$(awk -v p="jarvy-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz" \ + '$2 ~ p { print $1; exit }' /tmp/sha256sums.txt) + ARM_SHA=$(awk -v p="jarvy-v${VERSION}-aarch64-unknown-linux-gnu.tar.gz" \ + '$2 ~ p { print $1; exit }' /tmp/sha256sums.txt) + if [ -z "$X86_SHA" ] || [ -z "$ARM_SHA" ]; then + echo "::error::AUR linux-gnu tarballs not present in release. release.yml does not yet produce `jarvy-v--unknown-linux-gnu.tar.gz` (see docs/release-quirks-jarvy.md tarball pipeline gap). Skip AUR push until tarball production lands." + exit 1 + fi + sed -i "s/SHA256_PLACEHOLDER_X86/$X86_SHA/g" dist/aur/PKGBUILD-bin + sed -i "s/SHA256_PLACEHOLDER_ARM/$ARM_SHA/g" dist/aur/PKGBUILD-bin + - name: Publish to AUR + if: steps.aur_secret.outputs.have_secret == 'true' uses: KSXGitHub/github-actions-deploy-aur@v2.7.2 with: pkgname: jarvy-bin @@ -206,18 +264,22 @@ jobs: commit_email: ${{ secrets.AUR_EMAIL }} ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} commit_message: "Update to ${{ steps.version.outputs.version }}" - continue-on-error: true # AUR publish may require manual setup update-winget: name: Submit to winget + # Independent of `publish-crates-io`. winget consumes the .msi + # from the GitHub release; crates.io publish state is irrelevant. + # + # winget submissions open a PR to microsoft/winget-pkgs which + # Microsoft maintainers review/merge. `wingetcreate --submit` (the + # API the action uses) is the canonical user-facing flow — see + # docs/release-quirks-jarvy.md for the `public-pr-guard` skill + # carve-out covering this case. + # # Gate twofold: - # 1. Skip prereleases (rc / beta / alpha) so a soak release - # does not push the stable Cargo.toml version to crates.io - # as `latest`. + # 1. Skip prereleases (rc / beta / alpha). # 2. Skip tags that don't match `vX.Y.Z` shape so a chart - # release (e.g. `helm-vX.Y.Z`) does not trigger CLI - # publishing. workflow_dispatch (empty event.release) - # remains unaffected — manual reruns still publish. + # release (e.g. `helm-vX.Y.Z`) does not trigger. if: >- ${{ !github.event.release.prerelease && @@ -225,7 +287,6 @@ jobs: startsWith(github.event.release.tag_name, 'v')) }} runs-on: windows-latest - needs: [publish-crates-io] steps: - uses: actions/checkout@v4 @@ -237,30 +298,50 @@ jobs: VERSION="${VERSION#v}" echo "version=$VERSION" >> $GITHUB_OUTPUT + # Skip when WINGET_TOKEN isn't configured. The token is a fine- + # grained GitHub PAT with `public_repo` scope; the action uses + # it to fork microsoft/winget-pkgs and open the submission PR. + - name: Check winget token + id: winget_secret + shell: bash + run: | + if [ -z "${{ secrets.WINGET_TOKEN }}" ]; then + echo "::warning::WINGET_TOKEN not configured; skipping winget submission. Generate a fine-grained PAT with public_repo scope on your account, add as WINGET_TOKEN secret to enable winget distribution." + echo "have_secret=false" >> $GITHUB_OUTPUT + else + echo "have_secret=true" >> $GITHUB_OUTPUT + fi + - name: Update winget manifest + if: steps.winget_secret.outputs.have_secret == 'true' shell: bash run: | VERSION="${{ steps.version.outputs.version }}" sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" dist/windows/winget.yaml + # installers-regex matches the actual asset name pattern produced + # by cargo-packager on Windows: `jarvy__x64_en-US.msi`. The + # previous regex (`jarvy-.*-x86_64-pc-windows-.*\.zip$`) was a + # leftover from a cargo-dist plan never wired up — the release + # matrix has only ever produced `.msi`, so the regex matched + # zero assets and the action silently no-op'd. - name: Submit to winget-pkgs + if: steps.winget_secret.outputs.have_secret == 'true' uses: vedantmgoyal2009/winget-releaser@v2 with: identifier: Jarvy.Jarvy - installers-regex: 'jarvy-.*-x86_64-pc-windows-.*\.zip$' + installers-regex: '^jarvy_\d+\.\d+\.\d+_x64_en-US\.msi$' token: ${{ secrets.WINGET_TOKEN }} - continue-on-error: true # May require manual approval update-chocolatey: name: Publish to Chocolatey + # Independent of `publish-crates-io`. Chocolatey consumes the .msi + # from the GitHub release; crates.io publish state is irrelevant. + # # Gate twofold: - # 1. Skip prereleases (rc / beta / alpha) so a soak release - # does not push the stable Cargo.toml version to crates.io - # as `latest`. + # 1. Skip prereleases (rc / beta / alpha). # 2. Skip tags that don't match `vX.Y.Z` shape so a chart - # release (e.g. `helm-vX.Y.Z`) does not trigger CLI - # publishing. workflow_dispatch (empty event.release) - # remains unaffected — manual reruns still publish. + # release (e.g. `helm-vX.Y.Z`) does not trigger. if: >- ${{ !github.event.release.prerelease && @@ -268,7 +349,6 @@ jobs: startsWith(github.event.release.tag_name, 'v')) }} runs-on: windows-latest - needs: [publish-crates-io] steps: - uses: actions/checkout@v4 @@ -280,7 +360,22 @@ jobs: VERSION="${VERSION#v}" echo "version=$VERSION" >> $GITHUB_OUTPUT + # Skip when CHOCOLATEY_API_KEY isn't configured. Get one at + # https://community.chocolatey.org/account/profile/api after + # signing up and getting your maintainer account approved. + - name: Check Chocolatey API key + id: choco_secret + shell: bash + run: | + if [ -z "${{ secrets.CHOCOLATEY_API_KEY }}" ]; then + echo "::warning::CHOCOLATEY_API_KEY not configured; skipping Chocolatey push. Get your API key at https://community.chocolatey.org/account/profile/api and add as CHOCOLATEY_API_KEY secret to enable Chocolatey distribution." + echo "have_secret=false" >> $GITHUB_OUTPUT + else + echo "have_secret=true" >> $GITHUB_OUTPUT + fi + - name: Update nuspec + install script (substitute placeholders) + if: steps.choco_secret.outputs.have_secret == 'true' # The Chocolatey package ships TWO templated files: # - jarvy.nuspec: VERSION_PLACEHOLDER (the package version) # - tools/chocolateyinstall.ps1: VERSION_PLACEHOLDER (URL path) @@ -320,9 +415,9 @@ jobs: echo "Substituted version=$VERSION sha256=$MSI_SHA_UPPER into both files." - name: Pack and push to Chocolatey + if: steps.choco_secret.outputs.have_secret == 'true' shell: powershell run: | cd dist/windows/chocolatey choco pack choco push jarvy.${{ steps.version.outputs.version }}.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.CHOCOLATEY_API_KEY }} - continue-on-error: true # May require manual setup diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 37fda28..6db8502 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,15 @@ jobs: needs: [verify_tag] # verify_tag is skipped on workflow_dispatch; allow that to count as success. if: always() && (needs.verify_tag.result == 'success' || needs.verify_tag.result == 'skipped') + # Cap matrix legs at 60 min. Healthy runs are <10 min (Linux/Windows) + # and <15 min (macos-latest arm64). Without a cap, a queue-starved + # leg silently waits up to GitHub's hard 24 h job timeout — happened + # twice on the dropped macos-13 Intel slot during v0.1.0-rc.1 / rc.2 + # (runs 25340850348 + 25342271485), where each job sat at "Waiting + # for a runner to pick up this job..." for the full 24 h before + # GitHub killed it. Re-adding macos-13 or hitting any other + # runner-pool exhaustion now dies in 60 min instead. + timeout-minutes: 60 permissions: contents: read strategy: