name: Sync VERSION from desktop release # Updates the VERSION file in this repo whenever feedBack-desktop # publishes a new tagged release. feedBack-desktop's build.yml # dispatches the `desktop-released` event at the end of a successful # tag build (see docs in CLAUDE.md). A `workflow_dispatch` trigger is # kept for manual testing / recovery. # # Related issue: #81. on: repository_dispatch: types: [desktop-released] workflow_dispatch: inputs: version: description: 'Version to sync (vX.Y.Z or X.Y.Z)' required: true permissions: contents: write # Serialize runs so a rapid-fire pair of dispatches can't produce a # non-fast-forward push race. Later runs queue behind earlier ones. concurrency: group: sync-version cancel-in-progress: false jobs: sync: runs-on: ubuntu-latest steps: # Always operate on main regardless of trigger branch. repository_dispatch # already runs against the default branch, but workflow_dispatch can be # launched from any branch in the UI — pinning ref: main keeps both # paths committing to the same place. - uses: actions/checkout@v4 with: ref: main - name: Determine target version id: v # Pass untrusted payloads through env vars instead of ${{ }} # expansion inside the shell body — inline expansion makes the # script vulnerable to command injection if a dispatch client # sent a value like `$(...)` or a quote break. env: RAW_DISPATCH: ${{ github.event.client_payload.version }} RAW_INPUT: ${{ inputs.version }} EVENT_NAME: ${{ github.event_name }} run: | if [ "$EVENT_NAME" = "workflow_dispatch" ]; then raw="$RAW_INPUT" else raw="$RAW_DISPATCH" fi # Accept both "vX.Y.Z" and "X.Y.Z" on the wire. version="${raw#v}" # Anchored semver guard — rejects payloads like # "soundfonts-v1" (seen in the desktop release list) or any # stray text. The emitter side validates too, but we don't # trust cross-repo inputs. if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "::error::Invalid version payload: '$raw'" exit 1 fi echo "version=$version" >> "$GITHUB_OUTPUT" - name: Compare with current VERSION id: cmp env: TARGET: ${{ steps.v.outputs.version }} run: | current=$(tr -d '[:space:]' < VERSION) if [ "$current" = "$TARGET" ]; then echo "No change (already at $current)." echo "changed=false" >> "$GITHUB_OUTPUT" else echo "Bumping $current -> $TARGET." echo "changed=true" >> "$GITHUB_OUTPUT" echo "previous=$current" >> "$GITHUB_OUTPUT" fi - name: Commit and push if: steps.cmp.outputs.changed == 'true' env: TARGET: ${{ steps.v.outputs.version }} run: | printf '%s\n' "$TARGET" > VERSION git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]@users.noreply.github.com' git add VERSION git commit -m "chore: sync VERSION to $TARGET (desktop release)" # Explicit HEAD:main push — if workflow_dispatch was somehow # launched with ref: main overridden in the UI, this still # lands on main rather than pushing to whatever the tracking # branch was. git push origin HEAD:main