mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-10-27 07:52:03 +00:00
Fix three CI failures: a stale black, my scratchpad path, an impossible check
First push of this branch in a while, and CI found three things. BLACK VERSION DRIFT. The repo pins black ^24.3 (24.10.0 locked) and the local environment has 26.1.0, whose stable style differs. An earlier commit titled "satisfy the black gate" reformatted five tui_v3 screens with 26.1.0, which UNformatted them as far as CI was concerned — the fix made the gate worse. Everything is now formatted with 24.10.0, the pinned version, reproducing CI's six files exactly before and zero after. scripts/mutation_test.py is in there because I wrote it this session and never ran black on it at all. MY SCRATCHPAD PATH IN requirements.lock. pip-compile records the command that generated a lockfile in its header, and when I regenerated it during the dependency-vulnerability fix I compiled to a temp path and copied the result in. That baked an absolute path from my machine into a committed file, so CI's regenerate-and-diff could never match. Regenerated with CI's exact command on Python 3.11; only the two header lines change, so the dependency set was never wrong. AN UNSATISFIABLE LOCKFILE CHECK. Separately, and not caused by me: the step regenerated the lockfile and diffed it on every matrix entry, including Windows on 3.10 while Linux and macOS use 3.11. pip-compile resolves version-conditional dependencies per interpreter, so those two produce genuinely different files — 135 lines apart here, argon2-cffi-bindings among them. One committed lockfile cannot equal both outputs, so whichever version it was built on, the other job failed. Verification now runs once on Linux/3.11, the canonical generator; every platform still installs from the lockfile with --require-hashes, which is the part that had to stay everywhere. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
This commit is contained in:
1 parent
bc6ee2288e
commit
4204a23cab
8 files changed
+67
-27
No files matched your search
@@ -76,12 +76,25 @@ jobs:
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Verify lockfile and install dependencies
|
||||
# Regenerating the lockfile and diffing it proves the committed file
|
||||
# really came from src/requirements.txt. It can only be done on ONE
|
||||
# interpreter: pip-compile resolves version-conditional dependencies
|
||||
# per Python version, so 3.10 and 3.11 produce genuinely different
|
||||
# files (argon2-cffi-bindings among others), and a single committed
|
||||
# lockfile cannot equal both. Run as written across this matrix the
|
||||
# check was unsatisfiable -- whichever version the lockfile was built
|
||||
# on, the other job failed. Linux/3.11 is the canonical generator, so
|
||||
# the verification lives there.
|
||||
- name: Verify the lockfile matches src/requirements.txt
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pip-tools
|
||||
pip-compile --generate-hashes --output-file=requirements.lock src/requirements.txt
|
||||
git diff --exit-code requirements.lock
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --require-hashes -r requirements.lock
|
||||
- name: Run dependency scan
|
||||
run: scripts/dependency_scan.sh --ignore-vuln GHSA-wj6h-64fc-37mp --ignore-vuln CVE-2025-62727
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file=/tmp/claude-1000/-home-user/4efd790e-427f-430a-b379-ef8391e6744f/scratchpad/req.lock src/requirements.txt
|
||||
# pip-compile --generate-hashes --output-file=requirements.lock src/requirements.txt
|
||||
#
|
||||
aiohappyeyeballs==2.7.1 \
|
||||
--hash=sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d \
|
||||
|
||||
+27
-18
@@ -77,6 +77,7 @@ def acquire_lock() -> None:
|
||||
def release_lock() -> None:
|
||||
LOCK.unlink(missing_ok=True)
|
||||
|
||||
|
||||
# Each target names the source file and the tests that should defend it.
|
||||
# Running only the relevant suites keeps a full pass tractable; a mutant that
|
||||
# survives its own suite but would be caught by another is still a finding,
|
||||
@@ -123,8 +124,7 @@ SECURITY_HINTS = re.compile(
|
||||
# `kinds` list is half of what it may reach -- and leaving it out hid the
|
||||
# shape check that stops a string `kinds` becoming a substring match.
|
||||
# `mnemonic`/`seed` guard the material everything else protects.
|
||||
r"kind|mnemonic|seed"
|
||||
r")",
|
||||
r"kind|mnemonic|seed" r")",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
@@ -192,9 +192,7 @@ def generate(source: str) -> list[Mutant]:
|
||||
mutated = line.replace(old, new, 1)
|
||||
if mutated == line:
|
||||
continue
|
||||
mutants.append(
|
||||
Mutant(i, line, mutated, f"{old.strip()} -> {new.strip()}")
|
||||
)
|
||||
mutants.append(Mutant(i, line, mutated, f"{old.strip()} -> {new.strip()}"))
|
||||
# Force a guard to always allow. This is the single most informative
|
||||
# mutation for security code: if no test notices a check being removed
|
||||
# entirely, that check is unverified.
|
||||
@@ -221,7 +219,10 @@ def generate(source: str) -> list[Mutant]:
|
||||
if (
|
||||
inline
|
||||
and not line.rstrip().endswith("{")
|
||||
and (SECURITY_HINTS.search(inline.group(2)) or REFUSAL.search(inline.group(3)))
|
||||
and (
|
||||
SECURITY_HINTS.search(inline.group(2))
|
||||
or REFUSAL.search(inline.group(3))
|
||||
)
|
||||
):
|
||||
mutants.append(
|
||||
Mutant(
|
||||
@@ -247,9 +248,7 @@ def run(cmd: list[str], cwd: Path, timeout: int) -> tuple[int, str]:
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--target", default="all", choices=[*sorted(TARGETS), "all"]
|
||||
)
|
||||
parser.add_argument("--target", default="all", choices=[*sorted(TARGETS), "all"])
|
||||
parser.add_argument(
|
||||
"--limit", type=int, default=0, help="cap mutants per target (0 = all)"
|
||||
)
|
||||
@@ -335,12 +334,12 @@ def main() -> int:
|
||||
lines[mutant.line_no - 1] = mutant.mutated + ending
|
||||
path.write_text("".join(lines))
|
||||
|
||||
code, _ = run(
|
||||
["npx", "tsc", "--noEmit"], CLI, timeout=args.timeout
|
||||
)
|
||||
code, _ = run(["npx", "tsc", "--noEmit"], CLI, timeout=args.timeout)
|
||||
if code != 0:
|
||||
overall_counts["invalid"] += 1
|
||||
print(f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: invalid (does not typecheck)")
|
||||
print(
|
||||
f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: invalid (does not typecheck)"
|
||||
)
|
||||
continue
|
||||
|
||||
code, _ = run(
|
||||
@@ -348,11 +347,15 @@ def main() -> int:
|
||||
)
|
||||
if code != 0:
|
||||
overall_counts["killed"] += 1
|
||||
print(f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: killed ({mutant.description})")
|
||||
print(
|
||||
f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: killed ({mutant.description})"
|
||||
)
|
||||
else:
|
||||
overall_counts["survived"] += 1
|
||||
overall_survivors.append((f"{rel}:{mutant.line_no}", mutant))
|
||||
print(f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: SURVIVED ({mutant.description})")
|
||||
print(
|
||||
f" [{idx}/{len(all_mutants)}] line {mutant.line_no}: SURVIVED ({mutant.description})"
|
||||
)
|
||||
finally:
|
||||
restore()
|
||||
# Prove the restore worked rather than assuming it: a mutant left
|
||||
@@ -360,7 +363,9 @@ def main() -> int:
|
||||
# tree waiting to be committed.
|
||||
if path.read_text() != original:
|
||||
release_lock()
|
||||
raise SystemExit(f"FAILED TO RESTORE {rel} -- fix this before committing")
|
||||
raise SystemExit(
|
||||
f"FAILED TO RESTORE {rel} -- fix this before committing"
|
||||
)
|
||||
|
||||
release_lock()
|
||||
|
||||
@@ -372,7 +377,9 @@ def main() -> int:
|
||||
f"{overall_counts['invalid']} invalid (of {total})"
|
||||
)
|
||||
if viable:
|
||||
print(f"mutation score: {overall_counts['killed'] / viable:.0%} of viable mutants caught")
|
||||
print(
|
||||
f"mutation score: {overall_counts['killed'] / viable:.0%} of viable mutants caught"
|
||||
)
|
||||
|
||||
if unmeasured:
|
||||
print(
|
||||
@@ -383,7 +390,9 @@ def main() -> int:
|
||||
print(f" {where}")
|
||||
|
||||
if overall_survivors:
|
||||
print(f"\nSURVIVORS — each is either a gap or an equivalent mutant, and needs judging:\n")
|
||||
print(
|
||||
f"\nSURVIVORS — each is either a gap or an equivalent mutant, and needs judging:\n"
|
||||
)
|
||||
for where, mutant in overall_survivors:
|
||||
print(f" {where} [{mutant.description}]")
|
||||
print(f" was: {mutant.original.strip()[:110]}")
|
||||
|
||||
@@ -53,7 +53,9 @@ def render_wayfinder(payload: dict) -> str:
|
||||
|
||||
|
||||
class AtlasWayfinderScreen(Screen):
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
#atlas-wayfinder-shell { background: #101010; }
|
||||
#atlas-wayfinder-actions {
|
||||
height: auto;
|
||||
@@ -71,6 +73,7 @@ class AtlasWayfinderScreen(Screen):
|
||||
margin: 1 2;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def __init__(self, payload: dict) -> None:
|
||||
super().__init__()
|
||||
|
||||
@@ -21,7 +21,9 @@ class ProfileManagementScreen(Screen):
|
||||
Binding("escape", "app.pop_screen", "Back", show=True),
|
||||
]
|
||||
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
ProfileManagementScreen {
|
||||
background: #999999;
|
||||
}
|
||||
@@ -38,6 +40,7 @@ class ProfileManagementScreen(Screen):
|
||||
min-width: 14;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("SeedPass ◈ Profile Management", classes="maintenance-title")
|
||||
|
||||
@@ -17,7 +17,9 @@ class NostrPubkeyScreen(Screen):
|
||||
Binding("c", "copy_pubkey", "Copy npub", show=True),
|
||||
]
|
||||
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
NostrPubkeyScreen {
|
||||
background: #999999;
|
||||
}
|
||||
@@ -34,6 +36,7 @@ class NostrPubkeyScreen(Screen):
|
||||
min-width: 16;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("SeedPass ◈ Active Profile npub", classes="maintenance-title")
|
||||
|
||||
@@ -24,7 +24,9 @@ class RelaysScreen(Screen):
|
||||
("s", "sync_now", "Sync Now"),
|
||||
]
|
||||
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
RelaysScreen {
|
||||
background: #999999;
|
||||
}
|
||||
@@ -51,6 +53,7 @@ class RelaysScreen(Screen):
|
||||
min-width: 16;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("SeedPass ◈ Nostr Relay Management", classes="maintenance-title")
|
||||
|
||||
@@ -17,7 +17,9 @@ class ChangePasswordScreen(Screen):
|
||||
Binding("escape", "app.pop_screen", "Cancel", show=True),
|
||||
]
|
||||
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
ChangePasswordScreen {
|
||||
background: #999999;
|
||||
}
|
||||
@@ -29,6 +31,7 @@ class ChangePasswordScreen(Screen):
|
||||
min-width: 18;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("SeedPass ◈ Change Vault Password", classes="maintenance-title")
|
||||
@@ -107,7 +110,9 @@ class BackupParentSeedScreen(Screen):
|
||||
Binding("escape", "app.pop_screen", "Cancel", show=True),
|
||||
]
|
||||
|
||||
CSS = MAINTENANCE_CSS + """
|
||||
CSS = (
|
||||
MAINTENANCE_CSS
|
||||
+ """
|
||||
BackupParentSeedScreen {
|
||||
background: #999999;
|
||||
}
|
||||
@@ -119,6 +124,7 @@ class BackupParentSeedScreen(Screen):
|
||||
min-width: 18;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("SeedPass ◈ Backup Parent Seed", classes="maintenance-title")
|
||||
|
||||
Reference in new issue
Block a user