From 996b07feedc2a55cebd6962895e5d7a0d7afc0b8 Mon Sep 17 00:00:00 2001 From: Attila Laszlo Nagy Date: Wed, 16 Sep 2026 17:21:30 +0200 Subject: [PATCH] fix(ci): serialize mise bootstrap to protect Node GPG state --- .github/workflows/ci.yml | 8 ++++++ .github/workflows/release-docker.yml | 7 +++++ .github/workflows/release-helm.yml | 2 ++ .github/workflows/release-native.yml | 1 + .github/workflows/release-npm.yml | 3 +- .github/workflows/release-python.yml | 3 +- .github/workflows/release.yml | 1 + tools/ci/tests/test_required_ci.py | 42 ++++++++++++++++++++++++++++ tools/init.sh | 3 +- 9 files changed, 67 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c8d857ea..4a73269e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: >- mise exec -- uv run --frozen --project . pytest -q @@ -61,6 +62,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise exec -- uv lock --check --project . - run: mise run sync @@ -90,6 +92,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise exec -- pnpm install --frozen-lockfile - run: mise run ts -- build @@ -108,6 +111,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise run sync - run: mise run rust-fmt -- --check @@ -143,6 +147,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise run sync - run: mise run openapi @@ -165,6 +170,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise run helm -- dependencies - run: mise run helm -- lint --set payloadStore.enabled=false @@ -196,6 +202,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise run sync - run: mise exec -- pnpm install --frozen-lockfile @@ -225,6 +232,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 cache: false - run: mise run cpu-stack - name: Preserve diagnostics diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml index 4719f66e9..467ff8bc5 100644 --- a/.github/workflows/release-docker.yml +++ b/.github/workflows/release-docker.yml @@ -33,6 +33,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - id: resolve name: Validate source closure and coordinated versions env: @@ -64,6 +65,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - id: restore name: Reuse an already retained original-run image on retry @@ -155,6 +157,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -191,6 +194,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - id: restore name: Reuse an already retained original-run image on retry @@ -283,6 +287,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -326,6 +331,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -391,6 +397,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: diff --git a/.github/workflows/release-helm.yml b/.github/workflows/release-helm.yml index 771c25b80..50d49a271 100644 --- a/.github/workflows/release-helm.yml +++ b/.github/workflows/release-helm.yml @@ -31,6 +31,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - name: Validate, render, and retain the exact chart env: RELEASE_VERSION: ${{ inputs.version }} @@ -96,6 +97,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ghcr.io diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index fe462bec6..d5506e325 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -31,6 +31,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: docker-service-sie-server-sidecar-${{ inputs.version }} diff --git a/.github/workflows/release-npm.yml b/.github/workflows/release-npm.yml index eff62dace..845e780f9 100644 --- a/.github/workflows/release-npm.yml +++ b/.github/workflows/release-npm.yml @@ -31,7 +31,8 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 - install_args: python uv node pnpm + # Install everything here; later mise exec would auto-install omitted tools. + install_args: --jobs=1 - name: Validate source and optional release identity env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml index 4166d4169..f1a49bdab 100644 --- a/.github/workflows/release-python.yml +++ b/.github/workflows/release-python.yml @@ -31,7 +31,8 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 - install_args: python uv + # Install everything here; later mise exec would auto-install omitted tools. + install_args: --jobs=1 - name: Validate source and optional release identity env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14e51b6c2..48250233f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,6 +83,7 @@ jobs: - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2 with: version: 2026.5.5 + install_args: --jobs=1 if: steps.release.outputs.prs != '' && steps.release.outputs.prs != '[]' - name: Refresh coupled locks on the release pull request if: steps.release.outputs.prs != '' && steps.release.outputs.prs != '[]' diff --git a/tools/ci/tests/test_required_ci.py b/tools/ci/tests/test_required_ci.py index 9e1230ae7..a5bb05716 100644 --- a/tools/ci/tests/test_required_ci.py +++ b/tools/ci/tests/test_required_ci.py @@ -100,6 +100,48 @@ def test_mise_workflow_setups_pin_concrete_versions(): ) +def test_mise_bootstrap_serializes_the_complete_toolset(): + # Partial installs defer the other tools to `mise exec`, outside install_args. + for path in sorted((ROOT / ".github/workflows").glob("*.y*ml")): + workflow = yaml.safe_load(path.read_text()) + for name, job in workflow["jobs"].items(): + for step in job.get("steps", []): + if not step.get("uses", "").startswith("jdx/mise-action@"): + continue + inputs = step.get("with", {}) + if inputs.get("install", True) is not False: + assert shlex.split(inputs.get("install_args", "")) == ["--jobs=1"], ( + f"{path.name}: {name} must serialize the full tool install to avoid Node GPG races" + ) + + +@pytest.mark.parametrize("install_status", [0, 1]) +def test_init_serializes_install_and_stops_on_failure(tmp_path, install_status): + log = tmp_path / "mise.log" + mise = tmp_path / "mise" + mise.write_text( + '#!/bin/sh\nprintf "%s\\n" "$*" >> "$MISE_TEST_LOG"\n' + 'if [ "$1" = install ]; then exit "$MISE_TEST_INSTALL_STATUS"; fi\n' + ) + mise.chmod(0o755) + result = subprocess.run( + ["bash", str(ROOT / "tools/init.sh")], + env={ + **os.environ, + "PATH": f"{tmp_path}{os.pathsep}{os.environ['PATH']}", + "MISE_TEST_LOG": str(log), + "MISE_TEST_INSTALL_STATUS": str(install_status), + }, + capture_output=True, + check=False, + ) + assert result.returncode == install_status + expected = ["trust", "install --jobs=1"] + if install_status == 0: + expected.append("run full-sync") + assert log.read_text().splitlines() == expected + + @pytest.mark.parametrize(("mutate_lock", "old_venv", "code"), [(False, False, 0), (True, False, 1), (False, True, 1)]) def test_bootstrap_rejects_reused_environment_and_changed_lock(tmp_path, mutate_lock, old_venv, code): subprocess.run(["git", "init", "--quiet", str(tmp_path)], check=True) diff --git a/tools/init.sh b/tools/init.sh index 37374a7fe..0a460cae7 100755 --- a/tools/init.sh +++ b/tools/init.sh @@ -13,7 +13,8 @@ echo "Trusting mise config..." mise trust echo "Installing mise tools and deps..." -mise install +# The pinned Node versions share GPG bootstrap state; install them sequentially. +mise install --jobs=1 mise run full-sync if ! command -v cmake >/dev/null 2>&1; then