Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ jobs:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
persist-credentials: false
# Keep the same explicit selection in every exec below to avoid lazy tool installs.
- uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2
with:
version: 2026.5.5
install_args: --jobs=1
install_args: --jobs=1 python uv github:rhysd/actionlint
cache: false
- run: >-
mise exec -- uv run --frozen --project . pytest -q
mise exec --no-deps python uv github:rhysd/actionlint -- uv run --frozen --project . pytest -q
tools/ci/tests/test_required_ci.py tools/ci/tests/test_public_tree.py
- run: mise exec -- python tools/ci/check_public_tree.py
- run: mise exec --no-deps python uv github:rhysd/actionlint -- python tools/ci/check_public_tree.py
- run: >-
mise exec -- actionlint
mise exec --no-deps python uv github:rhysd/actionlint -- actionlint
-ignore '^unexpected key "queue" for "concurrency" section\. expected one of "cancel-in-progress", "group"$'

bootstrap:
Expand Down
33 changes: 29 additions & 4 deletions tools/ci/tests/test_required_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ 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.
def test_mise_bootstrap_serializes_required_toolsets():
# Unqualified `mise exec` installs missing tools 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():
Expand All @@ -110,11 +110,36 @@ def test_mise_bootstrap_serializes_the_complete_toolset():
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"
expected = ["--jobs=1"]
if path.name == "ci.yml" and name == "policy":
expected += ["python", "uv", "github:rhysd/actionlint"]
assert shlex.split(inputs.get("install_args", "")) == expected, (
f"{path.name}: {name} must serialize its tool install to avoid Node GPG races"
)


def test_policy_exec_does_not_lazily_install_unrelated_tools():
workflow = yaml.safe_load((ROOT / ".github/workflows/ci.yml").read_text())
policy = workflow["jobs"]["policy"]
commands = [shlex.split(step["run"]) for step in policy["steps"] if "run" in step]
prefix = ["mise", "exec", "--no-deps", "python", "uv", "github:rhysd/actionlint", "--"]
assert len(commands) == 3
assert all(command[: len(prefix)] == prefix for command in commands)
assert commands[0][len(prefix) :] == [
"uv",
"run",
"--frozen",
"--project",
".",
"pytest",
"-q",
"tools/ci/tests/test_required_ci.py",
"tools/ci/tests/test_public_tree.py",
]
assert commands[1][len(prefix) :] == ["python", "tools/ci/check_public_tree.py"]
assert commands[2][len(prefix)] == "actionlint"


@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"
Expand Down
Loading