From 1f7eba70ef24aad1b63f551f263ef4d06da783e8 Mon Sep 17 00:00:00 2001 From: Dmitry Bolotin Date: Mon, 25 May 2026 15:45:54 +0200 Subject: [PATCH 01/19] Add fix-beta.sh: re-sync v4-beta with v4 (inverse of merge-beta.sh) --- fix-beta.sh | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100755 fix-beta.sh diff --git a/fix-beta.sh b/fix-beta.sh new file mode 100755 index 00000000..5ea659bf --- /dev/null +++ b/fix-beta.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# +# fix-beta.sh — re-sync v4-beta with v4 after they diverge. +# +# Use when: +# - Someone committed directly to v4 (bypassing the v4-beta → merge-beta.sh +# cycle) and you want v4-beta to incorporate those changes. +# - Post-promotion: v4 has the rename-sed commit from merge-beta.sh that +# v4-beta does not have; run this script to bring v4-beta back in sync. +# +# What it does: +# 1. Refuses if the working tree is dirty. +# 2. Re-execs itself from a tmp copy so the script survives the checkout. +# 3. Switches to v4-beta, pulls latest. +# 4. Merges v4 in with --strategy-option theirs (v4 content wins on conflict). +# 5. Sed-flips @v4 -> @v4-beta across action.yaml + .github/workflows/*.yaml +# (mirrors merge-beta.sh's file scope, opposite direction). +# 6. Commits the flip on top of the merge commit and pushes. +# +# Inverse of merge-beta.sh. +# +# Sed safety: @v4 is a prefix of @v4-beta. Naive `s|@v4|@v4-beta|g` would +# produce @v4-beta-beta. Two scoped patterns are used instead: match @v4 at +# end-of-line, and @v4 followed by whitespace. + +set -o nounset +set -o errexit +set -o pipefail + +: ${TARGET_BRANCH:="v4-beta"} # branch being fixed +: ${SOURCE_BRANCH:="v4"} # branch being merged in + +if git status --porcelain | grep -q .; then + echo "" + echo "# -------------------------------------------------------------- #" + echo "# Repository has local changes. Automatic merge is not possible. #" + echo "# -------------------------------------------------------------- #" + echo "" + git status --porcelain + exit 1 +fi + +if [ "${__REAL_RUN:-}" != "true" ]; then + echo "Copying script to temporary file to not lose original code during checkouts..." + tmp_script="$(mktemp)" + cat "${0}" > "${tmp_script}" + chmod +x "${tmp_script}" + __REAL_RUN="true" "${tmp_script}" + exit 0 +fi + +echo "Updating remote repository info..." +git fetch --prune origin + +echo "Switching to '${TARGET_BRANCH}'..." +git checkout "${TARGET_BRANCH}" +git pull --ff-only + +echo "Merging 'origin/${SOURCE_BRANCH}' into '${TARGET_BRANCH}' (theirs wins on conflict)..." +git merge \ + --no-edit \ + --message "Merge ${SOURCE_BRANCH} into ${TARGET_BRANCH}" \ + "origin/${SOURCE_BRANCH}" \ + --strategy-option theirs + +echo "Flipping @${SOURCE_BRANCH} -> @${TARGET_BRANCH} in action.yaml and workflows..." +{ + find . -type f -name "action.yaml" + find .github/workflows -type f -name "*.yaml" +} | + while read -r file; do + sed "s|@${SOURCE_BRANCH}\$|@${TARGET_BRANCH}|g; s|@${SOURCE_BRANCH}\([[:space:]]\)|@${TARGET_BRANCH}\1|g" "${file}" > "${file}.tmp" + mv "${file}.tmp" "${file}" + done + +if git diff --quiet; then + echo "No ref flips needed; merge alone was sufficient." +else + git add -A + git commit -m "Re-flip @${SOURCE_BRANCH} -> @${TARGET_BRANCH} after merging ${SOURCE_BRANCH}" +fi + +echo "Pushing '${TARGET_BRANCH}'..." +git push origin "${TARGET_BRANCH}" + +echo "Done. ${TARGET_BRANCH} is now in sync with ${SOURCE_BRANCH}, with internal refs restored to @${TARGET_BRANCH}." From 234cf21e845c15c49be2f7726218c01ed1d4a887 Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Tue, 26 May 2026 11:31:44 -0700 Subject: [PATCH 02/19] ci: add changeset coverage check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New composite action `actions/changeset/check-coverage` that fails when a PR's changesets don't bump every workspace package the PR modifies. Catches the catalog-software gap (pnpm-workspace.yaml catalog version bumped without a matching changeset for the consuming workflow package). Wired into the existing `check-changesets` job in node-simple-pnpm.yaml as a `continue-on-error: true` step on PR / merge_group events. Emits `::error file=…,line=…::` annotations inline on the PR diff; never blocks merge. --- .github/workflows/node-simple-pnpm.yaml | 9 + actions/changeset/check-coverage/action.yaml | 26 ++ .../check-coverage/check-coverage.sh | 267 ++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 actions/changeset/check-coverage/action.yaml create mode 100755 actions/changeset/check-coverage/check-coverage.sh diff --git a/.github/workflows/node-simple-pnpm.yaml b/.github/workflows/node-simple-pnpm.yaml index aeb9a79d..32dd875f 100644 --- a/.github/workflows/node-simple-pnpm.yaml +++ b/.github/workflows/node-simple-pnpm.yaml @@ -582,6 +582,15 @@ jobs: run: | pnpm changeset status --since="origin/${BRANCH_NAME}" + - name: Check changeset coverage + if: | + always() && + (github.event_name == 'pull_request' || github.event_name == 'merge_group') + continue-on-error: true + uses: milaboratory/github-ci/actions/changeset/check-coverage@v4-beta + with: + base-branch: ${{ inputs.changeset-default-branch }} + pre-calculated-build: name: pre-build runs-on: ${{ inputs.gha-runner-label }} diff --git a/actions/changeset/check-coverage/action.yaml b/actions/changeset/check-coverage/action.yaml new file mode 100644 index 00000000..3610522e --- /dev/null +++ b/actions/changeset/check-coverage/action.yaml @@ -0,0 +1,26 @@ +name: Check changeset coverage +author: 'MiLaboratories' +description: | + Fail if a PR's changesets don't cover every workspace package it modifies. + + Catches the common gap: bumping a catalog version in pnpm-workspace.yaml + (e.g. a software package) without a matching changeset bump for the + workflow package that consumes it. + + Runs after `pnpm install` — needs `pnpm m ls` to resolve the workspace. + +inputs: + base-branch: + description: Base branch to diff against (e.g. main). + required: true + +runs: + using: 'composite' + + steps: + - name: Check changeset coverage + shell: bash + env: + ACTION_PATH: ${{ github.action_path }} + BASE_BRANCH: ${{ inputs.base-branch }} + run: '${ACTION_PATH}/check-coverage.sh' diff --git a/actions/changeset/check-coverage/check-coverage.sh b/actions/changeset/check-coverage/check-coverage.sh new file mode 100755 index 00000000..0f509114 --- /dev/null +++ b/actions/changeset/check-coverage/check-coverage.sh @@ -0,0 +1,267 @@ +#!/usr/bin/env bash +# +# Verify the PR's changesets bump every workspace package the PR modifies. +# Exit 1 on a coverage gap; exit 2 on tooling failure; exit 0 otherwise. +# +# Two sources of "modified": +# +# 1. Direct edits to a workspace package's files (longest-prefix match +# against the workspace package roots from `pnpm m ls`). +# +# 2. Catalog version bumps in pnpm-workspace.yaml: for each touched +# catalog key, find workspace packages that consume it via +# `"": "catalog:..."` and require those packages to bump. +# +# Skips private (unpublished) workspace packages — they never appear in +# the changeset's release set. +# +# Runs from the repo root after `pnpm install`. + +set -o nounset +set -o errexit +set -o pipefail + +: "${BASE_BRANCH:?BASE_BRANCH required}" + +log() { printf '%s\n' "$*" >&2; } +err() { printf '::error::%s\n' "$*" >&2; } + +# Use cwd-relative paths for tmp files — changeset's --output mis-handles +# absolute paths on macOS (prepends cwd, causing ENOENT). Cwd-relative is +# safe on every platform. +status_json=".changeset-coverage-status-$$.json" +pkg_list_json=".changeset-coverage-pkgs-$$.json" + +# --------------------------------------------------------------------------- +# 1. Bumped set from `changeset status --output=...`. +# --------------------------------------------------------------------------- +trap 'rm -f "${status_json}" "${pkg_list_json}"' EXIT + +# Invoke the binary directly. `pnpm exec` and `pnpm