Repository navigation
fix(frontend): clear runtime critical and high advisories #3435
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 Version Guard | |
| # A published package's version is the ONLY check available from outside this | |
| # repo. When source ships without a version bump, that check silently passes | |
| # while the artifact and the repo disagree — and nobody can tell. | |
| # | |
| # It has happened twice: | |
| # #979 @commonlyai/mcp npm 0.3.0 and main 0.3.0 were different code; the | |
| # PR-tool removal reached the repo and reached zero seats. | |
| # #1017 @commonlyai/cli npm 0.1.9 and main 0.1.9 were different code, with | |
| # SEVEN source commits since the bump — including #995, the quota | |
| # misclassification that had seats probing a dead provider every 5s. | |
| # | |
| # Both were found by hand, months and hours late respectively. This makes the | |
| # third one go red instead. | |
| on: | |
| pull_request: | |
| branches: [ main ] | |
| # `edited` catches base-branch retargeting — see the note in | |
| # pr-base-freshness.yml. A stacked PR retargeted to main after its parent | |
| # merges enters this guard's population without any event in the list | |
| # below ever firing for it. | |
| types: [opened, synchronize, reopened, ready_for_review, edited] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| version-guard: | |
| name: Source changed ⇒ version bumped | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Every published package whose source moved must bump its version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| BASE="origin/${{ github.event.pull_request.base.ref }}" | |
| # NO --depth here. `checkout` above already fetched full history | |
| # (fetch-depth: 0); a --depth=1 fetch of the base UNDOES that by | |
| # shallowing the ref, and once the base is one commit deep the fork | |
| # point is unreachable. `git diff BASE...HEAD` then dies with | |
| # "fatal: <base>...HEAD: no merge base" — which reads like a problem | |
| # with the PR's diff and is entirely a problem with this fetch. | |
| # | |
| # It only fires once the base has advanced past the fork point, which | |
| # is why the guard worked for weeks and then failed on #1113. Locally | |
| # reproducible: full clone, advance base 12 commits past a feature | |
| # branch, `git fetch --depth=1 origin <base>`, and the very next | |
| # three-dot diff fails. | |
| # | |
| # Also dropped the `|| true`. A base we cannot fetch means every | |
| # comparison below is meaningless, so it must stop rather than | |
| # silently compare against whatever ref happens to be on disk — the | |
| # failure mode this whole guard exists to prevent. | |
| git fetch --no-tags origin "${{ github.event.pull_request.base.ref }}" | |
| # Prove the base is usable before trusting any diff against it. A | |
| # guard that cannot see its own baseline must say so, not pass. | |
| if ! git merge-base "$BASE" HEAD >/dev/null 2>&1; then | |
| echo "::error::No merge base between $BASE and HEAD — the checkout is too shallow for this guard to compare anything. Not passing on an unusable baseline." | |
| exit 1 | |
| fi | |
| fail=0 | |
| # pkg_dir : the published package root (holds package.json) | |
| for pkg in cli commonly-mcp; do | |
| # Did any SOURCE file move? Docs and tests do not require a release. | |
| changed=$(git diff --name-only "$BASE"...HEAD -- "$pkg/src" | wc -l | tr -d ' ') | |
| if [ "$changed" = "0" ]; then | |
| echo "· $pkg: no src changes" | |
| continue | |
| fi | |
| base_v=$(git show "$BASE:$pkg/package.json" 2>/dev/null | sed -n 's/.*"version": "\([^"]*\)".*/\1/p' | head -1) | |
| head_v=$(sed -n 's/.*"version": "\([^"]*\)".*/\1/p' "$pkg/package.json" | head -1) | |
| # `sort -V` is not semver. It orders 1.0.0 BEFORE 1.0.0-beta.1, | |
| # reading a prerelease as NEWER than its own release, and it | |
| # inverts in both directions: base=1.0.0 head=1.0.0-beta.1 would | |
| # PASS — the exact backwards walk this check exists to stop — and | |
| # the legitimate promotion 1.0.0-beta.1 → 1.0.0 would FAIL. | |
| # No version ever committed to either package carries a | |
| # prerelease, so this is latent today. It fails OPEN in the | |
| # direction that matters, so the guard refuses to judge rather | |
| # than guessing, on the same principle as the merge-base check | |
| # above: a guard that cannot compare its inputs must say so. | |
| case "$base_v$head_v" in | |
| *-*) | |
| echo "::error file=$pkg/package.json::$pkg version comparison involves a prerelease ($base_v → $head_v). \`sort -V\` orders a prerelease as NEWER than its release, so this guard reaches the wrong answer in BOTH directions. Teach it semver before landing a prerelease; do not merge on this check alone." | |
| fail=1 | |
| continue | |
| ;; | |
| esac | |
| # An INCREASE, not merely a difference. A long-lived branch that | |
| # bumped while the base moved further ahead leaves head_v BELOW | |
| # base_v — different, so an equality test passes it, and merging | |
| # then walks the version backwards on the base. Observed on two | |
| # open PRs at once (base 0.1.21, heads 0.1.20 and 0.1.19), both | |
| # showing this check green. | |
| newest=$(printf '%s\n%s\n' "$base_v" "$head_v" | sort -V | tail -1) | |
| if [ "$base_v" = "$head_v" ]; then | |
| echo "::error file=$pkg/package.json::$pkg/src changed ($changed file(s)) but version is still $head_v. A published version that maps to two different artifacts defeats the only check available from outside this repo — bump it, or move the change out of $pkg/src." | |
| fail=1 | |
| elif [ "$newest" != "$head_v" ]; then | |
| echo "::error file=$pkg/package.json::$pkg/src changed ($changed file(s)) and $head_v is BELOW the base's $base_v. Merging would walk the published version backwards. Rebase and bump above $base_v." | |
| fail=1 | |
| else | |
| echo "✓ $pkg: src changed and version rose $base_v → $head_v" | |
| fi | |
| done | |
| exit $fail | |
| # The check above compares this PR against the base. It cannot see a | |
| # SECOND open PR bumping the same package to the same version, because | |
| # neither branch contains the other's commit — and that pair merges | |
| # CLEAN. Measured, not assumed: two branches both taking cli 0.1.30 to | |
| # 0.1.31 while touching different files under cli/src merge with no | |
| # conflict, and the result is one 0.1.31 holding both PRs' source. Each | |
| # PR's guard was green the whole time. npm then has a version that maps | |
| # to an artifact neither PR alone produced, which is the exact defect | |
| # #979 and #1017 were. | |
| # | |
| # Older PR keeps the version, newer picks the next one — so it is always | |
| # fixable by one author alone. Failing both would be a deadlock. | |
| - name: No older open PR is taking this package to the same version | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Every gh call is checked. An unchecked failure yields an empty | |
| # version list, which reads as "this PR bumps nothing" and PASSES — | |
| # the guard at its most reassuring exactly when it has gone blind. | |
| # Same reason the `|| true` came off the fetch above. | |
| proposed() { | |
| local pr="$1" pkg="$2" patch | |
| if ! patch=$(gh api "repos/$REPO/pulls/$pr/files" --paginate \ | |
| --jq ".[] | select(.filename == \"$pkg/package.json\") | .patch"); then | |
| echo "::error::gh api failed reading PR #$pr. Not passing on a check that could not run." >&2 | |
| return 1 | |
| fi | |
| # Only an ADDED version line counts. A PR that merely carries an | |
| # old package.json (every stale branch does) proposes nothing. | |
| printf '%s\n' "$patch" | sed -n 's/^+.*"version": "\([^"]*\)".*/\1/p' | head -1 | |
| } | |
| if ! older=$(gh api "repos/$REPO/pulls?state=open&per_page=100" --paginate \ | |
| --jq ".[] | select(.number < $PR) | .number"); then | |
| echo "::error::gh api failed listing open PRs. Not passing on a blind check." | |
| exit 1 | |
| fi | |
| fail=0 | |
| for pkg in cli commonly-mcp; do | |
| mine=$(proposed "$PR" "$pkg") | |
| [ -z "$mine" ] && { echo "· $pkg: this PR proposes no version"; continue; } | |
| echo "· $pkg: this PR proposes $mine" | |
| for other in $older; do | |
| theirs=$(proposed "$other" "$pkg") | |
| [ "$theirs" = "$mine" ] || continue | |
| echo "::error file=$pkg/package.json::$pkg $mine is already claimed by the older open PR #$other. Both bumps merge clean — git sees the same line changed the same way — so npm would carry one $mine built from both PRs' source. #$other keeps $mine; pick the next version here." | |
| fail=1 | |
| done | |
| done | |
| if [ "$fail" = "0" ]; then | |
| echo "✓ no older open PR claims these versions" | |
| fi | |
| exit $fail |