0.16.0 #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish to Scoop | |
| # Renders the Scoop manifest pointing at the official ZIP archives and | |
| # commits it to the configured Scoop bucket repository whenever a non-draft, | |
| # non-prerelease release is published. | |
| permissions: | |
| contents: read | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Release version to publish (must already exist as a GitHub release).' | |
| required: true | |
| type: string | |
| dry_run: | |
| description: 'Render the manifest but do not commit to the bucket repo.' | |
| required: false | |
| type: boolean | |
| default: true | |
| workflow_call: | |
| inputs: | |
| version: | |
| description: 'Release version to publish.' | |
| required: true | |
| type: string | |
| dry_run: | |
| description: 'Render the manifest but do not commit to the bucket repo.' | |
| required: false | |
| type: boolean | |
| default: false | |
| secrets: | |
| SCOOP_BUCKET_PAT: | |
| description: 'PAT (or deploy key token) with write access to the Scoop bucket repository.' | |
| required: false | |
| env: | |
| # Override via the bucket repo's own clone URL when forking. | |
| SCOOP_BUCKET_REPO: mickem/scoop-bucket | |
| SCOOP_BUCKET_BRANCH: main | |
| jobs: | |
| publish: | |
| if: ${{ github.event_name != 'release' || (github.event.release.draft == false && github.event.release.prerelease == false) }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Resolve release tag and version | |
| id: ctx | |
| run: | | |
| if [ "${{ github.event_name }}" = 'release' ]; then | |
| tag='${{ github.event.release.tag_name }}' | |
| version="$tag" | |
| dry_run='false' | |
| # published_at is ISO-8601 like 2026-05-15T14:30:00Z; the | |
| # manifest wants YYYY-MM-DD. | |
| published_at='${{ github.event.release.published_at }}' | |
| release_date="${published_at:0:10}" | |
| else | |
| tag='${{ inputs.version }}' | |
| version='${{ inputs.version }}' | |
| dry_run='${{ inputs.dry_run }}' | |
| release_date='' | |
| fi | |
| if [ -z "$tag" ]; then | |
| echo 'No release tag/version available.' >&2 | |
| exit 1 | |
| fi | |
| { | |
| echo "tag=$tag" | |
| echo "version=$version" | |
| echo "dry_run=$dry_run" | |
| echo "release_date=$release_date" | |
| } >> "$GITHUB_OUTPUT" | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| - name: Render Scoop manifest | |
| run: | | |
| render_args=( | |
| --version "${{ steps.ctx.outputs.version }}" | |
| --release-tag "${{ steps.ctx.outputs.tag }}" | |
| --templates packaging/scoop | |
| --output dist/scoop | |
| --download-dir dist/_assets | |
| --asset "ZIP_X64=NSCP-${{ steps.ctx.outputs.version }}-x64.zip" | |
| --asset "ZIP_X86=NSCP-${{ steps.ctx.outputs.version }}-Win32.zip" | |
| ) | |
| if [ -n "${{ steps.ctx.outputs.release_date }}" ]; then | |
| render_args+=(--release-date "${{ steps.ctx.outputs.release_date }}") | |
| fi | |
| python packaging/scripts/render_templates.py "${render_args[@]}" | |
| - name: Validate manifest is parseable JSON | |
| run: python -m json.tool dist/scoop/nsclient.json > /dev/null | |
| - name: Verify rendered hashes round-trip from URL | |
| run: | | |
| python packaging/scripts/verify_manifest_hashes.py scoop dist/scoop | |
| - name: Verify Scoop bin paths exist in the ZIP | |
| run: | | |
| set -euo pipefail | |
| python <<'PY' | |
| import json, sys, zipfile | |
| from pathlib import Path | |
| manifest = json.loads(Path("dist/scoop/nsclient.json").read_text()) | |
| version = "${{ steps.ctx.outputs.version }}" | |
| archives = { | |
| "x64": Path(f"dist/_assets/NSCP-{version}-x64.zip"), | |
| "Win32": Path(f"dist/_assets/NSCP-{version}-Win32.zip"), | |
| } | |
| # Normalise manifest bin entries to the path string scoop uses to | |
| # locate the file inside the extracted archive. | |
| bins = [] | |
| for entry in manifest.get("bin", []): | |
| if isinstance(entry, str): | |
| bins.append(entry) | |
| elif isinstance(entry, list) and entry: | |
| bins.append(entry[0]) | |
| missing = [] | |
| for arch, zpath in archives.items(): | |
| with zipfile.ZipFile(zpath) as zf: | |
| names = {n.replace("\\", "/") for n in zf.namelist()} | |
| for b in bins: | |
| target = b.replace("\\", "/") | |
| if target not in names: | |
| missing.append(f"{zpath.name}: {b}") | |
| if missing: | |
| for m in missing: | |
| print(f"::error::scoop bin entry not found — {m}") | |
| sys.exit(1) | |
| print("All scoop bin entries verified in both archives.") | |
| PY | |
| - name: Upload rendered manifest | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: scoop-manifest-${{ steps.ctx.outputs.version }} | |
| path: dist/scoop/nsclient.json | |
| if-no-files-found: error | |
| - name: Commit manifest to Scoop bucket repo | |
| if: ${{ steps.ctx.outputs.dry_run != 'true' }} | |
| env: | |
| SCOOP_BUCKET_PAT: ${{ secrets.SCOOP_BUCKET_PAT }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${SCOOP_BUCKET_PAT:-}" ]; then | |
| echo "::warning::SCOOP_BUCKET_PAT secret is not configured. Skipping commit." | |
| exit 0 | |
| fi | |
| # Pass the PAT via http.extraHeader so it never lands in argv or | |
| # the cloned repo's .git/config. GitHub Actions masks the secret | |
| # in logs but cannot mask process state on the runner. | |
| auth_header="Authorization: Basic $(printf 'x-access-token:%s' "$SCOOP_BUCKET_PAT" | base64 -w0)" | |
| bucket_dir="$(mktemp -d)" | |
| git -c http.extraHeader="$auth_header" clone --depth=1 \ | |
| --branch "$SCOOP_BUCKET_BRANCH" \ | |
| "https://github.com/${SCOOP_BUCKET_REPO}.git" \ | |
| "$bucket_dir" | |
| mkdir -p "$bucket_dir/bucket" | |
| cp dist/scoop/nsclient.json "$bucket_dir/bucket/nsclient.json" | |
| cd "$bucket_dir" | |
| git config user.name 'github-actions[bot]' | |
| git config user.email 'github-actions[bot]@users.noreply.github.com' | |
| git add bucket/nsclient.json | |
| if git diff --cached --quiet; then | |
| echo 'No changes to nsclient.json; nothing to commit.' | |
| exit 0 | |
| fi | |
| git commit -m "nsclient: bump to ${{ steps.ctx.outputs.version }}" | |
| git -c http.extraHeader="$auth_header" push \ | |
| origin "HEAD:${SCOOP_BUCKET_BRANCH}" |