Initial Commit
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A release carries the work, not the debris left beside it.
|
||||
|
||||
python3 tests/test_vm_release_contents.py
|
||||
|
||||
WHAT WENT WRONG, MEASURED
|
||||
|
||||
Release `v3` of the project `windows-troubleshooting-toolkit` held ten
|
||||
files:
|
||||
|
||||
_build.ps1 _launch.bat _structcheck.ps1 _w_diagnostics.ps1
|
||||
_w_network.ps1 _w_performance.ps1 _w_system.ps1 check_disp.ps1
|
||||
check_disp2.ps1 check_disp3.ps1
|
||||
|
||||
Every one of them scratch, and not one of them the 34 KB
|
||||
`TroubleshootingToolkit.ps1` the session had been asked to build. Two
|
||||
causes, compounding: `git add -A` commits whatever the agent left lying
|
||||
in the project folder, and the asset list sorts `_build` ahead of
|
||||
`TroubleshootingToolkit` before stopping at ten.
|
||||
|
||||
WHY THE FIX IS AT THE COMMIT AND NOT AT THE RELEASE
|
||||
|
||||
Gitea builds a release's "Source code (zip)" from the tree its tag points
|
||||
at, and that archive is the only thing on the page that downloads -
|
||||
measured, `Content-Disposition: attachment`, where every uploaded asset
|
||||
including a `.zip` comes back `inline`. So curating the asset list alone
|
||||
would leave the actual download full of scratch. Filtering at the commit
|
||||
cleans the repository, the assets and the archive together.
|
||||
|
||||
THE RISK THIS FILTER CARRIES, AND THE TWO GUARDS ON IT
|
||||
|
||||
Excluding by name can exclude the wrong thing. So: the patterns stay
|
||||
narrow enough that `find_duplicates.py` and `Check-AutoStartServices.ps1`
|
||||
- both real deliverables from real sessions - survive; a project that
|
||||
would otherwise publish nothing at all publishes everything instead,
|
||||
because then the tests were the job; and whatever is held back is named
|
||||
back to the agent in tool output.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(HERE, "..", "vm", "vmd"))
|
||||
|
||||
import gitea # noqa: E402
|
||||
|
||||
FAILS: list[str] = []
|
||||
|
||||
|
||||
def check(label: str, condition: bool, detail: str = "") -> None:
|
||||
if condition:
|
||||
print(f"ok {label}")
|
||||
else:
|
||||
FAILS.append(f"{label}: {detail}")
|
||||
print(f"FAIL {label}\n {detail}")
|
||||
|
||||
|
||||
def make_tree(root: str, names: list[str]) -> None:
|
||||
for name in names:
|
||||
full = os.path.join(root, name)
|
||||
os.makedirs(os.path.dirname(full), exist_ok=True)
|
||||
with open(full, "w") as handle:
|
||||
handle.write("x\n")
|
||||
|
||||
|
||||
def committed(repo: str) -> set[str]:
|
||||
result = gitea._git(repo, "ls-tree", "-r", "--name-only", "HEAD")
|
||||
return {n for n in result.stdout.split("\n") if n}
|
||||
|
||||
|
||||
# --- the session that went wrong, rebuilt ---------------------------------
|
||||
#
|
||||
# The real file list from that project, verbatim.
|
||||
TOOLKIT = "TroubleshootingToolkit.ps1"
|
||||
REAL_SESSION = [
|
||||
TOOLKIT,
|
||||
"_build.ps1", "_launch.bat", "_structcheck.ps1", "_w_diagnostics.ps1",
|
||||
"_w_network.ps1", "_w_performance.ps1", "_w_system.ps1",
|
||||
"check_disp.ps1", "check_disp2.ps1", "check_disp3.ps1",
|
||||
"csharp_out.txt", "csharp_thread.txt", "smoke.ps1", "t_out.txt",
|
||||
"test_concurrency.ps1", "test_cs.cs", "test_cs.exe", "test_job.ps1",
|
||||
"test_net.ps1", "test_thread.ps1", "test_pump.ps1",
|
||||
]
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
repo = os.path.join(tmp, "windows-troubleshooting-toolkit")
|
||||
os.makedirs(repo)
|
||||
make_tree(repo, REAL_SESSION)
|
||||
gitea.init(repo, "windows-troubleshooting-toolkit")
|
||||
|
||||
skipped = gitea.held_back(repo)
|
||||
check("the toolkit is not held back", TOOLKIT not in skipped, str(skipped[:5]))
|
||||
for name in ("_build.ps1", "_w_system.ps1", "test_cs.exe", "test_job.ps1",
|
||||
"smoke.ps1", "t_out.txt"):
|
||||
check(f"held back: {name}", name in skipped, f"held back: {skipped}")
|
||||
|
||||
sha = gitea.commit(repo, "the session's work")
|
||||
check("the commit happened", bool(sha), f"returned {sha!r}")
|
||||
tree = committed(repo)
|
||||
check("THE DELIVERABLE IS IN THE RELEASE", TOOLKIT in tree,
|
||||
"this is the exact failure: the release did not contain the toolkit")
|
||||
check("and the scratch is not",
|
||||
not ({"_build.ps1", "test_cs.exe", "smoke.ps1"} & tree),
|
||||
f"committed: {sorted(tree)}")
|
||||
|
||||
assets = gitea.tracked_files(repo, limit=gitea.RELEASE_ASSETS_MAX)
|
||||
# `check_disp*.ps1` and `csharp_thread.txt` survive, and that is the
|
||||
# deliberate half of the trade: the patterns stay narrow enough that
|
||||
# `check_disk.py` from a "check disk health" job would survive too.
|
||||
# What matters is that the deliverable can no longer be crowded out.
|
||||
check("THE DELIVERABLE IS AN ASSET", TOOLKIT in assets, str(assets))
|
||||
check("and the cap can no longer push it out",
|
||||
len(assets) < gitea.RELEASE_ASSETS_MAX, f"{len(assets)} assets")
|
||||
|
||||
check("scratch alone is not a reason to commit again",
|
||||
not gitea.has_changes(repo),
|
||||
"a tree holding only untracked scratch has nothing to publish")
|
||||
|
||||
# --- the guard against excluding a real deliverable ------------------------
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
repo = os.path.join(tmp, "dup-finder")
|
||||
os.makedirs(repo)
|
||||
# Both of these are real deliverables from real sessions, and both
|
||||
# read as scratch to a filter that is one pattern too greedy.
|
||||
make_tree(repo, ["find_duplicates.py", "Check-AutoStartServices.ps1",
|
||||
"README.md", "latest/notes.md", "src/main.py"])
|
||||
gitea.init(repo, "dup-finder")
|
||||
gitea.commit(repo, "work")
|
||||
tree = committed(repo)
|
||||
for name in ("find_duplicates.py", "Check-AutoStartServices.ps1",
|
||||
"README.md", "latest/notes.md", "src/main.py"):
|
||||
check(f"kept, because it is the work: {name}", name in tree, str(sorted(tree)))
|
||||
|
||||
# --- when the tests WERE the job ------------------------------------------
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
repo = os.path.join(tmp, "write-some-tests")
|
||||
os.makedirs(repo)
|
||||
make_tree(repo, ["test_parser.py", "test_lexer.py"])
|
||||
gitea.init(repo, "write-some-tests")
|
||||
gitea.commit(repo, "the tests are the deliverable")
|
||||
tree = committed(repo)
|
||||
check("a project with nothing but tests publishes them",
|
||||
{"test_parser.py", "test_lexer.py"} <= tree,
|
||||
f"published nothing at all: {sorted(tree)}")
|
||||
|
||||
# --- a project resumed after collecting scratch under the old rules --------
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
repo = os.path.join(tmp, "resumed")
|
||||
os.makedirs(repo)
|
||||
make_tree(repo, ["Toolkit.ps1", "test_old.ps1"])
|
||||
gitea.init(repo, "resumed")
|
||||
# Committed the way the previous version of this code would have.
|
||||
gitea._git(repo, "add", "-A", "--", ".")
|
||||
subprocess.run(["/usr/bin/git", "-C", repo, *gitea._SAFE_FLAGS,
|
||||
"commit", "--no-verify", "-q", "-m", "legacy"],
|
||||
capture_output=True,
|
||||
env={"PATH": "/usr/bin:/bin", "HOME": "/nonexistent", **gitea._SAFE_ENV},
|
||||
check=False)
|
||||
check("the legacy commit really does carry the scratch",
|
||||
"test_old.ps1" in committed(repo), "the premise of this case is wrong")
|
||||
check("and the next session notices", gitea.has_changes(repo),
|
||||
"already-published scratch has to count as something to change")
|
||||
make_tree(repo, ["Toolkit.ps1"])
|
||||
gitea.commit(repo, "this session")
|
||||
tree = committed(repo)
|
||||
check("a resumed project stops publishing its old scratch",
|
||||
"test_old.ps1" not in tree and "Toolkit.ps1" in tree, str(sorted(tree)))
|
||||
check("but the file is still on the array to work with",
|
||||
os.path.exists(os.path.join(repo, "test_old.ps1")),
|
||||
"--cached: it stops being published, it is not deleted")
|
||||
|
||||
print()
|
||||
if FAILS:
|
||||
print(f"{len(FAILS)} failed")
|
||||
for line in FAILS:
|
||||
print(" -", line)
|
||||
sys.exit(1)
|
||||
print("all release-contents checks passed")
|
||||
Reference in New Issue
Block a user