fix(scripts): document AI review-pipeline self-host env vars (#7143) #77
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: Package Release Please | |
| # Conventional-commit-driven release automation for the four published npm packages in this monorepo | |
| # (packages/loopover-mcp, packages/loopover-engine, packages/loopover-miner, | |
| # packages/loopover-ui-kit). release-please maintains a Release PR per component that bumps its | |
| # SemVer + regenerates its CHANGELOG.md from conventional commits touching that package's own | |
| # subtree. Merging a Release PR tags the component (mcp-v<semver> / engine-v<semver> / | |
| # miner-v<semver> / ui-kit-v<semver>, matching this repo's existing mcp-v* tag convention) and | |
| # creates the GitHub Release. release-please then explicitly dispatches the matching publish | |
| # workflow (publish-mcp.yml / publish-engine.yml / publish-miner.yml / publish-ui-kit.yml) via | |
| # workflow_dispatch -- a tag-push trigger can't be used here: GITHUB_TOKEN-created tags don't fire | |
| # push-triggered workflows (proven the hard way cutting v0.7.0 manually -- see #4132/#4135/#4154's | |
| # tag-move dance). | |
| # | |
| # separate-pull-requests: true (release-please-config.json) -- unlike metagraphed's combined-PR | |
| # default, these four packages have genuinely independent release cadences and separate publish | |
| # targets/workflows, and bundling them would work against this repo's small-focused-PR convention. | |
| # | |
| # linked-versions plugin, group "engine-and-dependents" (release-please-config.json) -- the | |
| # exception to the above: packages/loopover-mcp and packages/loopover-miner both carry a REAL | |
| # runtime `dependencies` entry on packages/loopover-engine, so they are not actually independent of | |
| # it. The node-workspace plugin bumps a dependent's `@loopover/engine` version range the moment | |
| # engine's own version changes, but under plain separate-pull-requests: true that bump lands on the | |
| # dependent's OWN separate branch/PR -- meaning the dependent's package.json ends up requiring an | |
| # engine version that exists NOWHERE (not locally on that branch, since engine's bump lives on a | |
| # different unmerged branch; not on npm either, since it hasn't published yet). `npm ci` then fails | |
| # with ETARGET until a human notices and manually walks engine's release through to publish before | |
| # updating the dependent's branch -- confirmed live across several mcp/miner release cycles | |
| # (#7086/#7087/#7107/#7108/#7119/#7120/#7121; node-workspace's OWN `merge` option, tried first, does | |
| # NOT override separate-pull-requests -- verified against release-please's manifest.ts and | |
| # docs/manifest-releaser.md: separate-pull-requests is enforced as the final PR-splitting decision | |
| # regardless of what plugins attempt internally). linked-versions is the mechanism that actually | |
| # overrides per-component isolation, but ONLY for the named `components` in its group: engine's new | |
| # version and its dependents' bumped ranges land together in ONE PR/commit, so npm workspaces | |
| # resolves the dependency from the LOCAL checkout (always satisfies the range, regardless of npm | |
| # registry publish timing or ordering) instead of hitting the ETARGET race. packages/loopover-ui-kit | |
| # has no dependency edge to any other package here and is not in the group, so it keeps its fully | |
| # independent release cadence untouched. | |
| # | |
| # Runs on every push to main, not just a schedule: release-please is idempotent (it recomputes | |
| # purely from conventional-commit history since each component's last release tag), so the extra | |
| # runs cost nothing when there's no new commit or merged Release PR to act on. The push trigger is | |
| # not just about refreshing the Release PR's proposed diff sooner -- it's what actually completes a | |
| # release: merging a component's Release PR only tags it, creates the GitHub Release, and dispatches | |
| # the matching publish workflow on release-please's THIS run, so without a push trigger a merge sits | |
| # fully un-published until the next cron tick (confirmed live: mcp-v3.1.0's Release PR merged 8 | |
| # minutes after a cron run had already passed, leaving it tagged-in-package.json-but-unpublished for | |
| # what would have been up to 2 days). The schedule stays as a redundant safety net in case a | |
| # push-triggered run is ever skipped/fails; workflow_dispatch stays available for an on-demand run. | |
| on: | |
| push: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: "0 16 */2 * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: mcp-release-please | |
| cancel-in-progress: false | |
| # The release-please action can create public tags and GitHub Releases before it dispatches | |
| # the package publish workflows. Keep the whole job behind the protected release environment | |
| # so those release artifacts have the same approval boundary as the npm publish jobs. | |
| jobs: | |
| release-please: | |
| runs-on: ubuntu-latest | |
| environment: release | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| actions: write # dispatch publish-mcp.yml / publish-engine.yml / publish-miner.yml / publish-ui-kit.yml after a release | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| with: | |
| persist-credentials: false | |
| # Authenticates as a real account (via RELEASE_PLEASE_TOKEN, a fine-grained PAT) rather than the | |
| # default GITHUB_TOKEN. GitHub Apps like github-actions[bot] aren't tracked as repo collaborators, | |
| # so PRs/commits it authors were repeatedly re-flagged as needing manual workflow-run approval on | |
| # every push, even with the least-restrictive fork-PR-approval setting -- a real collaborator | |
| # identity doesn't hit that gate. This also sidesteps the same GITHUB_TOKEN recursion-prevention | |
| # rule noted below for tag pushes. | |
| - name: Run release-please | |
| id: release | |
| uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1 | |
| with: | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| token: ${{ secrets.RELEASE_PLEASE_TOKEN }} | |
| # release-please's extra-files JSON-path updater doesn't reliably reach package-lock.json's | |
| # per-workspace version fields (keys containing "/", nested under a manifest-mode component | |
| # block) -- confirmed empirically (both the mcp-v0.7.0 dry run's precursor and the first two | |
| # engine-v0.2.0 dry runs left it stale, breaking npm ci with "Missing: <pkg>@<version> from | |
| # lock file"). Patches it directly on whichever release branch(es) release-please just | |
| # created/updated, using the same branch naming convention its own commits already rely on. | |
| # | |
| # Also needs packages/loopover-miner/expected-engine.version bumped to match | |
| # packages/loopover-engine/package.json's new version whenever a branch bumps engine: | |
| # release-please's engine component only ever touches files under packages/loopover-engine/**, | |
| # so it can never update that cross-package pin itself -- scripts/check-engine-parity.ts's | |
| # checkMinerEngineVersionPinSync would otherwise fail on every release that bumps engine | |
| # (confirmed live on the engine-v3.1.0 release PR, #5807). | |
| # | |
| # Branches to check are content-derived (git ls-remote --heads matching the release-please | |
| # branch prefix), not a hardcoded per-component list: the linked-versions plugin (grouping | |
| # engine with its dependents, release-please-config.json) puts mcp/engine/miner on ONE shared | |
| # `--groups--<groupName>` branch instead of three separate `--components--<name>` branches, so | |
| # a hardcoded `for component in mcp engine miner ui-kit` loop checking only the old | |
| # `--components--` naming would silently never find/sync that branch at all (confirmed live: | |
| # PR #7127's engine-and-dependents group branch left expected-engine.version stale, failing | |
| # engine-parity:drift-check and two engine-version-display tests). Similarly, the | |
| # expected-engine.version sync itself is now driven by comparing the checked-out branch's own | |
| # committed pin against its own package.json (not an `if [ component = engine ]` check), so it | |
| # fires correctly whether engine's bump landed via a solo branch or a shared group branch. | |
| - name: Sync package-lock.json and engine-version pin on any release branch | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| trusted_sync_script="${RUNNER_TEMP}/sync-release-lockfile-versions.mjs" | |
| cp scripts/sync-release-lockfile-versions.mjs "$trusted_sync_script" | |
| git remote set-url origin "https://github.com/${GITHUB_REPOSITORY}.git" | |
| gh auth setup-git | |
| branches="$(git ls-remote --heads origin 'release-please--branches--main--*' | awk '{print $2}' | sed 's#refs/heads/##')" | |
| if [ -z "$branches" ]; then | |
| echo "No release-please branches found, skipping." | |
| fi | |
| for branch in $branches; do | |
| git fetch origin "$branch" | |
| git checkout -B "sync-check-$(printf '%s' "$branch" | tr '/' '-')" "origin/$branch" | |
| node "$trusted_sync_script" packages/loopover-mcp packages/loopover-engine packages/loopover-miner packages/loopover-ui-kit | |
| if git diff --quiet package-lock.json; then | |
| echo "package-lock.json already in sync on $branch." | |
| else | |
| git add package-lock.json | |
| git commit -m "chore(release): sync package-lock.json" | |
| git push origin "HEAD:$branch" | |
| fi | |
| engine_version="$(node -p "require('./packages/loopover-engine/package.json').version")" | |
| printf '%s\n' "$engine_version" > packages/loopover-miner/expected-engine.version | |
| if git diff --quiet packages/loopover-miner/expected-engine.version; then | |
| echo "expected-engine.version already in sync on $branch." | |
| else | |
| git add packages/loopover-miner/expected-engine.version | |
| git commit -m "chore(release): sync miner engine-version pin" | |
| git push origin "HEAD:$branch" | |
| fi | |
| done | |
| # release-please creates the component tag + GitHub Release with GITHUB_TOKEN, which (by | |
| # GitHub's recursion-prevention rule) does NOT fire push/tag-based workflows. workflow_dispatch | |
| # IS exempt from that rule, so dispatch the OIDC publish workflow explicitly, telling it | |
| # release-please already created the tag/release (released_by_release_please=true skips both | |
| # workflows' own tag-creation and GitHub-release steps). Without this, merging a Release PR | |
| # would silently never publish. | |
| # | |
| # Dispatched against the release tag itself, NOT main: main can move between this step and | |
| # the dispatched run actually starting (anything else merging in that window), and the | |
| # publish workflow's own safety check hard-fails if the commit it's building doesn't match | |
| # the tag release-please just created (confirmed live: engine-v0.2.0's first dry run hit | |
| # exactly this race when an unrelated PR merged seconds apart). A tag doesn't move, so | |
| # dispatching against it makes the publish deterministic regardless of main's activity. | |
| - name: Dispatch MCP publish | |
| if: ${{ steps.release.outputs['packages/loopover-mcp--release_created'] == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh workflow run publish-mcp.yml --ref "${{ steps.release.outputs['packages/loopover-mcp--tag_name'] }}" -f released_by_release_please=true | |
| - name: Dispatch Engine publish | |
| if: ${{ steps.release.outputs['packages/loopover-engine--release_created'] == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh workflow run publish-engine.yml --ref "${{ steps.release.outputs['packages/loopover-engine--tag_name'] }}" -f released_by_release_please=true | |
| - name: Dispatch Miner publish | |
| if: ${{ steps.release.outputs['packages/loopover-miner--release_created'] == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh workflow run publish-miner.yml --ref "${{ steps.release.outputs['packages/loopover-miner--tag_name'] }}" -f released_by_release_please=true | |
| - name: Dispatch UI Kit publish | |
| if: ${{ steps.release.outputs['packages/loopover-ui-kit--release_created'] == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh workflow run publish-ui-kit.yml --ref "${{ steps.release.outputs['packages/loopover-ui-kit--tag_name'] }}" -f released_by_release_please=true |