name: Publish Package Repositories on: workflow_dispatch: inputs: run_id: description: "Release run ID to publish (leave empty for latest successful stable)" required: false default: "" permissions: contents: read actions: read jobs: publish: runs-on: ubuntu-latest timeout-minutes: 25 steps: - name: Checkout (shallow) uses: actions/checkout@v4 with: fetch-depth: 1 submodules: false # Publishing does not require submodule contents - name: Validate and resolve artifact source run id: resolve-run shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail if [ -n "${{ inputs.run_id }}" ]; then RUN_ID="${{ inputs.run_id }}" echo "Validating provided run_id: $RUN_ID" RUN_INFO=$(gh api "repos/${{ github.repository }}/actions/runs/${RUN_ID}" \ --jq '{conclusion: .conclusion, head_branch: .head_branch, event: .event}') CONCLUSION=$(echo "$RUN_INFO" | jq -r '.conclusion') BRANCH=$(echo "$RUN_INFO" | jq -r '.head_branch') EVENT=$(echo "$RUN_INFO" | jq -r '.event') if [[ "$CONCLUSION" != "success" ]]; then echo "::error::Run $RUN_ID did not succeed (conclusion=$CONCLUSION)" exit 1 fi if [[ "$BRANCH" != "main" ]]; then echo "::warning::Run $RUN_ID is from branch '$BRANCH', not main" fi if [[ "$EVENT" == "pull_request" ]]; then echo "::error::Run $RUN_ID is a pull_request run; refusing artifact from untrusted source" exit 1 fi echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT" else # Find latest successful release.yml run on main (stable tag push) RUN_ID=$(gh api "repos/${{ github.repository }}/actions/workflows/release.yml/runs?per_page=20&branch=main" \ --jq '.workflow_runs[] | select(.conclusion=="success" and (.event=="push" or .event=="workflow_dispatch")) | .id' | head -1) if [ -z "$RUN_ID" ]; then echo "::error::No successful Release workflow run found on main" exit 1 fi echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT" echo "Resolved latest successful Release run on main: $RUN_ID" fi - name: Download repo artifacts (apt-repo) uses: actions/download-artifact@v4 with: run-id: ${{ steps.resolve-run.outputs.run_id }} name: apt-repo path: repo-artifacts/apt-repo github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download repo artifacts (yum-repo) uses: actions/download-artifact@v4 with: run-id: ${{ steps.resolve-run.outputs.run_id }} name: yum-repo path: repo-artifacts/yum-repo github-token: ${{ secrets.GITHUB_TOKEN }} - name: Prepare upload set id: prep shell: bash run: | set -euo pipefail ls -1 repo-artifacts || true # Expect artifacts named: apt-repo, yum-repo, homebrew-formula, etc. if [ -d repo-artifacts/apt-repo ]; then mv repo-artifacts/apt-repo aptrepo fi if [ -d repo-artifacts/yum-repo ]; then mv repo-artifacts/yum-repo yumrepo fi # latest.json is inside assets attached to release; we attempt fetch from GitHub release API. TAG=$(gh release list --limit 1 --json tagName --jq '.[0].tagName') || TAG="" if [ -z "$TAG" ]; then echo "Could not determine latest tag; skipping manifest fetch" >&2 else echo "Latest tag: $TAG" >&2 gh release download "$TAG" --pattern latest.json --dir . || echo "No latest.json asset yet" fi # Summaries echo "HAS_APT=$([ -d aptrepo ] && echo 1 || echo 0)" >> "$GITHUB_OUTPUT" echo "HAS_YUM=$([ -d yumrepo ] && echo 1 || echo 0)" >> "$GITHUB_OUTPUT" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish to R2 if: ${{ steps.prep.outputs.HAS_APT == '1' || steps.prep.outputs.HAS_YUM == '1' }} env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN || secrets.R2_SECRET_ACCESS_KEY }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} GPG_PUBLIC_KEY_B64: ${{ secrets.GPG_PUBLIC_KEY_B64 || '' }} shell: bash run: | set -euo pipefail if [ -z "${CLOUDFLARE_API_TOKEN:-}" ] || [ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]; then echo "Cloudflare API token/account missing; aborting publish." >&2 exit 1 fi WRANGLER_ROOT="${RUNNER_TEMP:-/tmp}/wrangler-cli" WRANGLER_BIN="$WRANGLER_ROOT/node_modules/.bin/wrangler" if [ ! -x "$WRANGLER_BIN" ]; then npm install --prefix "$WRANGLER_ROOT" wrangler@4.69.0 >/dev/null fi "$WRANGLER_BIN" --version >&2 upload_file() { local src="$1"; shift local key="$1"; shift local ct="application/octet-stream" case "$key" in *.json) ct="application/json" ;; *.key) ct="text/plain" ;; *.txt) ct="text/plain" ;; *.xml) ct="application/xml" ;; *.gz) ct="application/gzip" ;; *.deb) ct="application/vnd.debian.binary-package" ;; *.rpm) ct="application/x-rpm" ;; *.asc) ct="application/pgp-signature" ;; esac echo "-- Upload $key" >&2 "$WRANGLER_BIN" r2 object put "yams-repository/$key" \ --remote \ --file "$src" \ --content-type "$ct" } if [ -n "${GPG_PUBLIC_KEY_B64:-}" ]; then echo "$GPG_PUBLIC_KEY_B64" | base64 -d > gpg.key upload_file gpg.key gpg.key fi if [ -d aptrepo ]; then find aptrepo -type f -print0 | while IFS= read -r -d '' f; do rel="${f#aptrepo/}"; upload_file "$f" "aptrepo/$rel"; done fi if [ -d yumrepo ]; then find yumrepo -type f -print0 | while IFS= read -r -d '' f; do rel="${f#yumrepo/}"; upload_file "$f" "yumrepo/$rel"; done fi if [ -f latest.json ]; then upload_file latest.json latest.json fi echo "Publish complete." >&2 - name: Warn if nothing to publish if: ${{ steps.prep.outputs.HAS_APT != '1' && steps.prep.outputs.HAS_YUM != '1' }} run: echo "No repo artifacts found to publish." >&2