mirror of
https://github.com/apache/airflow.git
synced 2026-11-04 07:12:48 +00:00
The PROD image build first built the Airflow and provider wheels, but only after the CI image and generate-constraints had finished, although the wheels need neither. In a RunsOn pull request run that put them on the chain that ends the run: CI image to minute 9.1, constraints to 15.2, wheels 15.5 to 17.9, PROD image to 29.6, Kubernetes tests to 41.3. The CI workflows now build the wheels in their own job right after build-info and tell the PROD image build they are already uploaded; the other callers of prod-image-build.yml still build them as before. Generated-by: Claude Opus 5
558 lines
21 KiB
Python
558 lines
21 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import shlex
|
|
import shutil
|
|
import subprocess
|
|
import tarfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
import yaml
|
|
from ci import prek_cache_markers
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
PREK_ACTION = ".github/actions/install-prek/action.yml"
|
|
|
|
|
|
def load_yaml(path):
|
|
return yaml.load((ROOT / path).read_text(), Loader=yaml.BaseLoader)
|
|
|
|
|
|
def find_step(path, *, step_id=None, name=None):
|
|
steps = load_yaml(path)["runs"]["steps"]
|
|
return next(
|
|
step
|
|
for step in steps
|
|
if (step_id is not None and step.get("id") == step_id)
|
|
or (name is not None and step.get("name") == name)
|
|
)
|
|
|
|
|
|
def run_shell(script, env):
|
|
return subprocess.run(
|
|
["bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", script],
|
|
cwd=ROOT,
|
|
env={**os.environ, **env},
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=15,
|
|
check=False,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sandbox(tmp_path):
|
|
home = tmp_path / "home"
|
|
home.mkdir()
|
|
output = tmp_path / "output"
|
|
output.touch()
|
|
return {"HOME": str(home), "GITHUB_OUTPUT": str(output), "HOOKS": ""}
|
|
|
|
|
|
def read_outputs(env):
|
|
return dict(line.split("=", 1) for line in Path(env["GITHUB_OUTPUT"]).read_text().splitlines())
|
|
|
|
|
|
def run_cache_step(step, env, tmp_path):
|
|
script = step["run"].replace("/tmp/cache-prek.tar.gz", str(tmp_path / "cache-prek.tar.gz"))
|
|
return run_shell(script, env)
|
|
|
|
|
|
def _extract_tar_archive(handle: tarfile.TarFile, path: Path) -> None:
|
|
if hasattr(tarfile, "data_filter"):
|
|
handle.extractall(path, filter="fully_trusted")
|
|
else:
|
|
handle.extractall(path)
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_tools(tmp_path):
|
|
tools = tmp_path / "bin"
|
|
tools.mkdir()
|
|
log = tmp_path / "commands.log"
|
|
command = tools / "command"
|
|
command.write_text(
|
|
r"""#!/usr/bin/env bash
|
|
name="${0##*/}"
|
|
printf '%s\n' "${name} $*" >> "${COMMAND_LOG}"
|
|
if [[ -n "${FAIL_MATCH:-}" && "${name} $*" == *"${FAIL_MATCH}"* ]]; then
|
|
exit 42
|
|
fi
|
|
if [[ "${name}" == "prek" ]]; then
|
|
count_file="${COMMAND_COUNT}"
|
|
count=0
|
|
[[ ! -f "${count_file}" ]] || count=$(<"${count_file}")
|
|
count=$((count + 1))
|
|
printf '%s' "${count}" > "${count_file}"
|
|
if [[ "${WRITE_MARKER_ATTEMPT:-}" == "${count}" ]]; then
|
|
marker="${HOME}/.cache/prek/hooks/python-new/.prek-hook.json"
|
|
mkdir -p "${marker%/*}"
|
|
printf '{"schema_version":1}' > "${marker}"
|
|
fi
|
|
if [[ "${WRITE_LOG_ONLY:-false}" == "true" ]]; then
|
|
mkdir -p "${HOME}/.cache/prek"
|
|
printf 'diagnostic\n' >> "${HOME}/.cache/prek/prek.log"
|
|
fi
|
|
if (( count <= ${FAIL_ATTEMPTS:-0} )); then
|
|
exit 42
|
|
fi
|
|
fi
|
|
"""
|
|
)
|
|
command.chmod(0o755)
|
|
for name in ("prek", "sleep"):
|
|
(tools / name).symlink_to(command)
|
|
return {
|
|
"PATH": f"{tools}:{os.environ['PATH']}",
|
|
"COMMAND_LOG": str(log),
|
|
"COMMAND_COUNT": str(tmp_path / "command-count"),
|
|
}
|
|
|
|
|
|
def read_commands(env):
|
|
return [shlex.split(line) for line in Path(env["COMMAND_LOG"]).read_text().splitlines()]
|
|
|
|
|
|
def test_cache_key_keeps_only_environment_inputs():
|
|
step = find_step(PREK_ACTION, step_id="cache-key")
|
|
assert "cache-prek-v13-${PLATFORM}" in step["run"]
|
|
assert "python${PYTHON_VERSION}" in step["run"]
|
|
assert "uv${UV_VERSION}" in step["run"]
|
|
assert "prek${PREK_VERSION}" in step["run"]
|
|
assert step["env"]["PREK_CONFIG_HASH"] == "${{ hashFiles('**/.pre-commit-config.yaml') }}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("inputs", "expected_save", "expected_reason"),
|
|
[
|
|
({}, False, "reader"),
|
|
({"SAVE_CACHE": "true", "STASH_HIT": "false"}, True, "cache-miss"),
|
|
({"SAVE_CACHE": "true", "TAR_RESTORED": "false"}, True, "extraction-failed"),
|
|
({"SAVE_CACHE": "true", "CACHE_CHANGED": "true"}, True, "cache-repaired"),
|
|
({"SAVE_CACHE": "true", "EVENT_NAME": "schedule"}, True, "non-pr-republication"),
|
|
({"SAVE_CACHE": "true", "EVENT_NAME": "push"}, True, "non-pr-republication"),
|
|
({"SAVE_CACHE": "true", "EVENT_NAME": "workflow_dispatch"}, True, "non-pr-republication"),
|
|
(
|
|
{"SAVE_CACHE": "true", "CHANGE_DETECTION_UNCERTAIN": "true"},
|
|
True,
|
|
"change-detection-uncertain",
|
|
),
|
|
({"SAVE_CACHE": "true"}, False, "unchanged"),
|
|
({"HOOKS": "mypy-providers"}, False, "selected-hooks"),
|
|
({"HOOKS": "mypy-providers", "SAVE_CACHE": "true", "STASH_HIT": "false"}, False, "selected-hooks"),
|
|
],
|
|
)
|
|
def test_cache_refresh_policy(sandbox, inputs, expected_save, expected_reason):
|
|
env = {
|
|
**sandbox,
|
|
"SAVE_CACHE": "false",
|
|
"STASH_HIT": "true",
|
|
"TAR_RESTORED": "true",
|
|
"CACHE_CHANGED": "false",
|
|
"CHANGE_DETECTION_UNCERTAIN": "false",
|
|
"EVENT_NAME": "pull_request",
|
|
**inputs,
|
|
}
|
|
result = run_shell(find_step(PREK_ACTION, step_id="cache-policy")["run"], env)
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_outputs(env)["save"] == str(expected_save).lower()
|
|
assert read_outputs(env)["reason"] == expected_reason
|
|
|
|
|
|
def test_restored_hooks_are_always_validated(sandbox, fake_tools):
|
|
marker = Path(sandbox["HOME"]) / ".cache/prek/hooks/python-existing/.prek-hook.json"
|
|
marker.parent.mkdir(parents=True)
|
|
marker.write_text('{"schema_version":1}')
|
|
step = find_step(PREK_ACTION, step_id="install-hooks")
|
|
assert "if" not in step
|
|
result = run_shell(step["run"], {**sandbox, **fake_tools, "WRITE_LOG_ONLY": "true"})
|
|
assert result.returncode == 0, result.stderr
|
|
assert [
|
|
"prek",
|
|
"install-hooks",
|
|
"--skip",
|
|
"run-skill-eval",
|
|
"--skip",
|
|
"run-skill-eval-codex",
|
|
"--skip",
|
|
"view-skill-eval",
|
|
] in read_commands(fake_tools)
|
|
assert read_outputs(sandbox)["cache-changed"] == "false"
|
|
assert read_outputs(sandbox)["change-detection-uncertain"] == "false"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("hooks", "expected_arguments", "expected_reason"),
|
|
[
|
|
(
|
|
"compile-fab-assets compile-edge-assets",
|
|
["compile-fab-assets", "compile-edge-assets"],
|
|
"selected-hooks",
|
|
),
|
|
(
|
|
"compile-fab-assets\ncompile-edge-assets\n",
|
|
["compile-fab-assets", "compile-edge-assets"],
|
|
"selected-hooks",
|
|
),
|
|
(
|
|
" \n",
|
|
["--skip", "run-skill-eval", "--skip", "run-skill-eval-codex", "--skip", "view-skill-eval"],
|
|
"cache-miss",
|
|
),
|
|
],
|
|
ids=("space-separated", "newline-separated", "whitespace-only"),
|
|
)
|
|
def test_hooks_input_decides_what_is_prepared_and_saved(
|
|
sandbox, fake_tools, hooks, expected_arguments, expected_reason
|
|
):
|
|
install_step = find_step(PREK_ACTION, step_id="install-hooks")
|
|
policy_step = find_step(PREK_ACTION, step_id="cache-policy")
|
|
assert install_step["env"]["HOOKS"] == policy_step["env"]["HOOKS"] == "${{ inputs.hooks }}"
|
|
result = run_shell(install_step["run"], {**sandbox, **fake_tools, "HOOKS": hooks})
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_commands(fake_tools) == [["prek", "install-hooks", *expected_arguments]]
|
|
result = run_shell(
|
|
policy_step["run"], {**sandbox, "HOOKS": hooks, "SAVE_CACHE": "true", "STASH_HIT": "false"}
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_outputs(sandbox)["reason"] == expected_reason
|
|
|
|
|
|
def find_hatch_build_hooks():
|
|
return {
|
|
hook
|
|
for path in (*ROOT.glob("providers/*/hatch_build.py"), *ROOT.glob("providers/*/*/hatch_build.py"))
|
|
for hook in re.findall(r'"prek", "run", "([\w-]+)"', path.read_text())
|
|
}
|
|
|
|
|
|
def find_hooks_run_by_job(job):
|
|
hooks = set()
|
|
for step in job["steps"]:
|
|
command = step.get("run", "")
|
|
if "prepare-provider-distributions" in command:
|
|
hooks |= find_hatch_build_hooks()
|
|
hooks.update(re.findall(r"^prek .* ([\w-]+) --all-files$", command, re.MULTILINE))
|
|
return hooks
|
|
|
|
|
|
def test_selected_hooks_match_the_hooks_each_job_runs():
|
|
selections = {
|
|
f"{workflow.name}:{job_id}": (set(step["with"]["hooks"].split()), find_hooks_run_by_job(job))
|
|
for workflow in (ROOT / ".github/workflows").glob("*.yml")
|
|
for job_id, job in load_yaml(workflow)["jobs"].items()
|
|
for step in job.get("steps", [])
|
|
if step.get("uses") == "./.github/actions/install-prek" and "hooks" in step.get("with", {})
|
|
}
|
|
assert sorted(selections) == [
|
|
"ci-amd.yml:migration-round-trip",
|
|
"ci-amd.yml:mypy-providers",
|
|
"ci-arm.yml:migration-round-trip",
|
|
"ci-arm.yml:mypy-providers",
|
|
"generate-constraints.yml:generate-constraints-matrix",
|
|
"prod-packages-build.yml:build-prod-packages",
|
|
"test-providers.yml:prepare-install-verify-provider-distributions",
|
|
"test-providers.yml:providers-compatibility-tests-matrix",
|
|
]
|
|
for site, (selected, run) in selections.items():
|
|
assert selected == run, (
|
|
f"Set the hooks input of the install-prek step in {site} to: {' '.join(sorted(run))}"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("cache_state", ("missing", "empty", "renamed-markers", "log-only"))
|
|
@pytest.mark.parametrize("save_cache", ("true", "false"))
|
|
def test_markerless_cache_refresh_policy(sandbox, fake_tools, cache_state, save_cache):
|
|
cache = Path(sandbox["HOME"]) / ".cache/prek"
|
|
if cache_state == "empty":
|
|
cache.mkdir(parents=True)
|
|
elif cache_state == "renamed-markers":
|
|
marker = cache / "environments/python-one/.new-marker.json"
|
|
marker.parent.mkdir(parents=True)
|
|
marker.write_text("{}")
|
|
result = run_shell(
|
|
find_step(PREK_ACTION, step_id="install-hooks")["run"],
|
|
{**sandbox, **fake_tools, "WRITE_LOG_ONLY": str(cache_state == "log-only").lower()},
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
outputs = read_outputs(sandbox)
|
|
uncertain = cache_state in ("renamed-markers", "log-only")
|
|
assert outputs["cache-changed"] == "false"
|
|
assert outputs["change-detection-uncertain"] == str(uncertain).lower()
|
|
|
|
result = run_shell(
|
|
find_step(PREK_ACTION, step_id="cache-policy")["run"],
|
|
{
|
|
**sandbox,
|
|
"SAVE_CACHE": save_cache,
|
|
"STASH_HIT": "true",
|
|
"TAR_RESTORED": "true",
|
|
"CACHE_CHANGED": outputs["cache-changed"],
|
|
"CHANGE_DETECTION_UNCERTAIN": outputs["change-detection-uncertain"],
|
|
"EVENT_NAME": "pull_request",
|
|
},
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
outputs = read_outputs(sandbox)
|
|
assert outputs["save"] == str(uncertain and save_cache == "true").lower()
|
|
assert outputs["reason"] == (
|
|
"reader" if save_cache == "false" else "change-detection-uncertain" if uncertain else "unchanged"
|
|
)
|
|
|
|
|
|
def test_change_detection_spans_failed_then_successful_attempts(sandbox, fake_tools):
|
|
result = run_shell(
|
|
find_step(PREK_ACTION, step_id="install-hooks")["run"],
|
|
{**sandbox, **fake_tools, "FAIL_ATTEMPTS": "1", "WRITE_MARKER_ATTEMPT": "1"},
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert len([cmd for cmd in read_commands(fake_tools) if cmd[:2] == ["prek", "install-hooks"]]) == 2
|
|
assert read_outputs(sandbox)["cache-changed"] == "true"
|
|
|
|
|
|
def test_unreadable_installation_metadata_is_uncertain(sandbox, fake_tools):
|
|
marker = Path(sandbox["HOME"]) / ".cache/prek/hooks/python-broken/.prek-hook.json"
|
|
marker.mkdir(parents=True)
|
|
result = run_shell(find_step(PREK_ACTION, step_id="install-hooks")["run"], {**sandbox, **fake_tools})
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_outputs(sandbox)["cache-changed"] == "false"
|
|
assert read_outputs(sandbox)["change-detection-uncertain"] == "true"
|
|
|
|
|
|
def test_hook_install_failure_remains_fatal(sandbox, fake_tools):
|
|
assert "if" not in find_step(PREK_ACTION, step_id="cache-policy")
|
|
assert "continue-on-error" not in find_step(PREK_ACTION, step_id="install-hooks")
|
|
result = run_shell(
|
|
find_step(PREK_ACTION, step_id="install-hooks")["run"],
|
|
{**sandbox, **fake_tools, "FAIL_MATCH": "prek install-hooks"},
|
|
)
|
|
assert result.returncode != 0
|
|
assert len([cmd for cmd in read_commands(fake_tools) if cmd[:2] == ["prek", "install-hooks"]]) == 4
|
|
|
|
|
|
def test_marker_snapshot_uses_relative_paths_and_exact_contents(tmp_path):
|
|
cache = tmp_path / "prek"
|
|
hook_marker = cache / "hooks/python-one/.prek-hook.json"
|
|
repo_marker = cache / "repos/repo-one/.prek-repo.json"
|
|
hook_marker.parent.mkdir(parents=True)
|
|
repo_marker.parent.mkdir(parents=True)
|
|
hook_marker.write_bytes(b'{"hook": 1}\n')
|
|
repo_marker.write_bytes(b'{"repo": 1}\n')
|
|
|
|
snapshot = prek_cache_markers.snapshot_markers(cache)
|
|
|
|
assert set(snapshot) == {"hooks/python-one/.prek-hook.json", "repos/repo-one/.prek-repo.json"}
|
|
assert snapshot == prek_cache_markers.snapshot_markers(cache)
|
|
|
|
|
|
def test_repaired_archive_is_reused_without_another_save(sandbox, fake_tools, tmp_path):
|
|
cache = Path(sandbox["HOME"]) / ".cache/prek"
|
|
(cache / "hooks/python-incomplete").mkdir(parents=True)
|
|
marker = cache / "hooks/python-existing/.prek-hook.json"
|
|
marker.parent.mkdir(parents=True)
|
|
marker.write_text('{"schema_version":1}')
|
|
install_step = find_step(PREK_ACTION, step_id="install-hooks")
|
|
policy_step = find_step(PREK_ACTION, step_id="cache-policy")
|
|
|
|
first_install = run_shell(install_step["run"], {**sandbox, **fake_tools, "WRITE_MARKER_ATTEMPT": "1"})
|
|
assert first_install.returncode == 0, first_install.stderr
|
|
first_outputs = read_outputs(sandbox)
|
|
assert first_outputs["cache-changed"] == "true"
|
|
|
|
Path(sandbox["GITHUB_OUTPUT"]).write_text("")
|
|
policy_env = {
|
|
**sandbox,
|
|
"SAVE_CACHE": "true",
|
|
"STASH_HIT": "true",
|
|
"TAR_RESTORED": "true",
|
|
"CACHE_CHANGED": "true",
|
|
"CHANGE_DETECTION_UNCERTAIN": "false",
|
|
"EVENT_NAME": "pull_request",
|
|
}
|
|
assert run_shell(policy_step["run"], policy_env).returncode == 0
|
|
assert read_outputs(sandbox) == {"save": "true", "reason": "cache-repaired"}
|
|
|
|
Path(sandbox["GITHUB_OUTPUT"]).write_text("")
|
|
archive_step = find_step(PREK_ACTION, step_id="archive-prek")
|
|
assert run_cache_step(archive_step, sandbox, tmp_path).returncode == 0
|
|
shutil.rmtree(cache)
|
|
Path(sandbox["GITHUB_OUTPUT"]).write_text("")
|
|
restore_step = find_step(PREK_ACTION, step_id="restore-prek-tar")
|
|
assert run_cache_step(restore_step, sandbox, tmp_path).returncode == 0
|
|
|
|
Path(sandbox["GITHUB_OUTPUT"]).write_text("")
|
|
second_install = run_shell(install_step["run"], {**sandbox, **fake_tools})
|
|
assert second_install.returncode == 0, second_install.stderr
|
|
second_outputs = read_outputs(sandbox)
|
|
assert second_outputs["cache-changed"] == "false"
|
|
|
|
Path(sandbox["GITHUB_OUTPUT"]).write_text("")
|
|
second_policy_env = {
|
|
**policy_env,
|
|
"CACHE_CHANGED": "false",
|
|
}
|
|
assert run_shell(policy_step["run"], second_policy_env).returncode == 0
|
|
assert read_outputs(sandbox) == {"save": "false", "reason": "unchanged"}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("supports_data_filter", "expected_kwargs"),
|
|
[
|
|
(True, {"filter": "fully_trusted"}),
|
|
(False, {}),
|
|
],
|
|
)
|
|
def test_extract_tar_archive_uses_supported_filter(
|
|
tmp_path, monkeypatch, supports_data_filter, expected_kwargs
|
|
):
|
|
content = tmp_path / "content"
|
|
content.mkdir()
|
|
(content / "payload").write_text("cache data")
|
|
archive = tmp_path / "cache.tar.gz"
|
|
with tarfile.open(archive, "w:gz") as handle:
|
|
handle.add(content, arcname="cache")
|
|
|
|
if supports_data_filter:
|
|
monkeypatch.setattr(tarfile, "data_filter", lambda member, path: member, raising=False)
|
|
else:
|
|
monkeypatch.delattr(tarfile, "data_filter", raising=False)
|
|
|
|
with patch.object(tarfile.TarFile, "extractall", autospec=True) as extractall:
|
|
with tarfile.open(archive, "r:gz") as handle:
|
|
_extract_tar_archive(handle, tmp_path)
|
|
extractall.assert_called_once_with(handle, tmp_path, **expected_kwargs)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
os.environ.get("RUN_PREK_INTEGRATION") != "1",
|
|
reason="Set RUN_PREK_INTEGRATION=1 to exercise real prek 0.5.2",
|
|
)
|
|
def test_real_prek_repair_and_reuse(tmp_path):
|
|
repository = tmp_path / "repository"
|
|
repository.mkdir()
|
|
subprocess.run(["git", "init", "-q"], cwd=repository, check=True)
|
|
(repository / ".pre-commit-config.yaml").write_text(
|
|
"""\
|
|
repos:
|
|
- repo: local
|
|
hooks:
|
|
- id: local-python
|
|
name: local python
|
|
entry: python -c 'print("ok")'
|
|
language: python
|
|
pass_filenames: false
|
|
"""
|
|
)
|
|
cache = tmp_path / "prek-cache"
|
|
env = {**os.environ, "PREK_HOME": str(cache)}
|
|
command = ["uvx", "--from", "prek==0.5.2", "prek", "install-hooks"]
|
|
|
|
def install():
|
|
return subprocess.run(
|
|
command,
|
|
cwd=repository,
|
|
env=env,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=180,
|
|
check=False,
|
|
)
|
|
|
|
first_install = install()
|
|
assert first_install.returncode == 0, first_install.stderr
|
|
marker = next((cache / "hooks").glob("*/.prek-hook.json"))
|
|
marker.unlink()
|
|
before_repair = prek_cache_markers.snapshot_markers(cache)
|
|
|
|
repair = install()
|
|
assert repair.returncode == 0, repair.stderr
|
|
repaired = prek_cache_markers.snapshot_markers(cache)
|
|
assert repaired != before_repair
|
|
|
|
archive = tmp_path / "prek-cache.tar.gz"
|
|
with tarfile.open(archive, "w:gz") as handle:
|
|
handle.add(cache, arcname="prek-cache")
|
|
shutil.rmtree(cache)
|
|
with tarfile.open(archive, "r:gz") as handle:
|
|
_extract_tar_archive(handle, tmp_path)
|
|
before_reuse = prek_cache_markers.snapshot_markers(cache)
|
|
|
|
reuse = install()
|
|
assert reuse.returncode == 0, reuse.stderr
|
|
assert prek_cache_markers.snapshot_markers(cache) == before_reuse
|
|
|
|
|
|
@pytest.mark.parametrize("payload", ("missing", "corrupt", "wrong-directory"))
|
|
def test_unusable_archive_is_a_miss(sandbox, tmp_path, payload):
|
|
archive = tmp_path / "cache-prek.tar.gz"
|
|
if payload == "corrupt":
|
|
archive.write_bytes(b"not a gzip archive")
|
|
elif payload == "wrong-directory":
|
|
other = tmp_path / "other"
|
|
other.mkdir()
|
|
with tarfile.open(archive, "w:gz") as handle:
|
|
handle.add(other, arcname=".cache/other")
|
|
result = run_cache_step(find_step(PREK_ACTION, step_id="restore-prek-tar"), sandbox, tmp_path)
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_outputs(sandbox)["tar-restored"] == "false"
|
|
|
|
|
|
def test_archive_roundtrip_preserves_permissions_links_and_contents(sandbox, tmp_path):
|
|
cache = Path(sandbox["HOME"]) / ".cache/prek"
|
|
cache.mkdir(parents=True)
|
|
executable = cache / "python"
|
|
executable.write_text("payload\n")
|
|
executable.chmod(0o751)
|
|
(cache / "link").symlink_to("python")
|
|
result = run_cache_step(find_step(PREK_ACTION, step_id="archive-prek"), sandbox, tmp_path)
|
|
assert result.returncode == 0, result.stderr
|
|
shutil.rmtree(cache)
|
|
result = run_cache_step(find_step(PREK_ACTION, step_id="restore-prek-tar"), sandbox, tmp_path)
|
|
assert result.returncode == 0, result.stderr
|
|
assert read_outputs(sandbox)["tar-restored"] == "true"
|
|
assert executable.read_text() == "payload\n"
|
|
assert executable.stat().st_mode & 0o777 == 0o751
|
|
assert (cache / "link").is_symlink()
|
|
assert os.readlink(cache / "link") == "python"
|
|
|
|
|
|
def test_missing_cache_cannot_be_saved(sandbox, tmp_path):
|
|
result = run_cache_step(find_step(PREK_ACTION, step_id="archive-prek"), sandbox, tmp_path)
|
|
assert result.returncode != 0
|
|
assert "duration-seconds" not in read_outputs(sandbox)
|
|
|
|
|
|
def test_cache_cleanup_preserves_unrelated_cache(sandbox, tmp_path):
|
|
home = Path(sandbox["HOME"])
|
|
for name in ("prek", "uv"):
|
|
(home / ".cache" / name).mkdir(parents=True)
|
|
archive = tmp_path / "cache-prek.tar.gz"
|
|
archive.write_bytes(b"stale")
|
|
result = run_cache_step(
|
|
find_step(PREK_ACTION, name="Clear local prek cache before restore"), sandbox, tmp_path
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert not (home / ".cache/prek").exists()
|
|
assert (home / ".cache/uv").is_dir()
|
|
assert not archive.exists()
|