Skip to content

docs: make Method Factory README reader-first #40

docs: make Method Factory README reader-first

docs: make Method Factory README reader-first #40

Workflow file for this run

name: release-gate
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12"]
steps:
# Explicitly check out the ACTUAL pull-request HEAD SHA, not GitHub's
# synthetic merge ref (actions/checkout defaults to the merge ref on
# pull_request). This makes the CI evidence literal-commit proof: the
# tested tree is exactly the branch head, not a candidate-tree-
# equivalent merge.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 (pinned SHA)
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
# Full history required: migration tests generate fixtures from the
# exact public v0.1.2 commit fb5641c (tag v0.1.2-integrity), which
# is not present in a depth-1 checkout of this branch.
fetch-depth: 0
# Literal-commit identity assertion: the checked-out HEAD MUST equal the
# event's expected SHA. A mismatch fails CI. This is the proof that a
# successful run tested the exact commit, not a synthetic merge tree.
- name: Assert checkout identity
run: |
ACTUAL="$(git rev-parse HEAD)"
EXPECTED="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}"
echo "actual HEAD: $ACTUAL"
echo "expected SHA: $EXPECTED"
if [ "$ACTUAL" != "$EXPECTED" ]; then
echo "::error::checkout identity mismatch (actual != expected)"; exit 1
fi
echo "checkout identity OK: tested tree is the exact expected commit"
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 (pinned SHA)
with:
python-version: ${{ matrix.python-version }}
- name: Install with test extras
run: pip install -e ".[test]"
- name: Canonical unit tests
run: python -m unittest discover -s methodfactory/tests -t .
- name: Packaging smoke — mf entry point
run: |
mf --version
python -m methodfactory --version
test "$(mf --version)" = "methodfactory 2.0.0rc1"
test "$(python -m methodfactory --version)" = "methodfactory 2.0.0rc1"
# RC1 packaging proof — build the distributable WHEEL from the EXACT
# candidate source into a disposable directory OUTSIDE the repo, so the
# tracked-worktree and artifact-scan steps below stay clean. No new
# build dependency: pip wheel uses the declared setuptools backend.
# (sdist is NOT claimed by CI; any sdist evidence is local-only
# supplemental unless independently CI-produced.)
- name: Build distributable wheel
run: |
set -euo pipefail
export MF_DIST="$RUNNER_TEMP/mf-dist"
mkdir -p "$MF_DIST" "$RUNNER_TEMP/mf-src"
git archive HEAD | tar -x -C "$RUNNER_TEMP/mf-src"
cd "$RUNNER_TEMP/mf-src"
python -m pip wheel . --no-deps -w "$MF_DIST"
ls -la "$MF_DIST"
python - <<'EOF'
import hashlib, os, pathlib
dist = pathlib.Path(os.environ["MF_DIST"])
wheels = sorted(dist.glob("methodfactory-2.0.0rc1-*.whl"))
assert len(wheels) == 1, f"expected exactly one rc1 wheel, got {[p.name for p in dist.iterdir()]}"
for p in wheels:
print(p.name, hashlib.sha256(p.read_bytes()).hexdigest())
EOF
- name: Isolated wheel install + version proof
run: |
set -euo pipefail
export MF_VENV="$RUNNER_TEMP/mf-venv"
export MF_DIST="$RUNNER_TEMP/mf-dist"
python -m venv "$MF_VENV"
"$MF_VENV/bin/python" -m pip install --no-deps "$MF_DIST"/*.whl
# Run OUTSIDE the repo: a heredoc through `python -` puts CWD on
# sys.path, which would shadow the installed package with the
# checkout copy and break the import-provenance proof.
cd "$RUNNER_TEMP"
# import provenance: must resolve from the venv site-packages, not
# the development checkout.
"$MF_VENV/bin/python" - <<'EOF'
import methodfactory, os, pathlib
print("import file:", methodfactory.__file__)
venv = pathlib.Path(os.environ["MF_VENV"]).resolve()
assert pathlib.Path(methodfactory.__file__).resolve().is_relative_to(venv), \
"methodfactory resolved outside the isolated venv"
assert methodfactory.__version__ == "2.0.0rc1", methodfactory.__version__
EOF
test "$("$MF_VENV/bin/mf" --version)" = "methodfactory 2.0.0rc1"
test "$("$MF_VENV/bin/python" -m methodfactory --version)" = "methodfactory 2.0.0rc1"
# CLI surface: only migrate-store + export may be present.
"$MF_VENV/bin/mf" --help
for banned in create apply status summary review trial ship triage; do
if "$MF_VENV/bin/mf" --help | grep -qE "^\s+$banned\b"; then
echo "::error::forbidden lifecycle command exposed: $banned"; exit 1
fi
done
# Packaged functional smoke: canonical exact-fb5641c fixture -> installed
# mf migrate-store -> authoritative validation -> installed mf export
# (both formats) -> legacy evidence revalidated by the frozen reader;
# source must remain unchanged.
- name: Packaged migration/export smoke (isolated env)
run: |
set -euo pipefail
export MF_SMOKE="$RUNNER_TEMP/mf-smoke"
export MF_STORE="$RUNNER_TEMP/mf-store"
export MF_LEGACY_REVAL="$RUNNER_TEMP/mf-legacy-revalidate"
export MF_VENV="$RUNNER_TEMP/mf-venv"
python - <<'EOF'
# Generate the canonical fixture through the sanctioned mechanism
# (exact public fb5641c code via the repo's disposable worktree).
# Runs from the checkout on purpose: the editable install provides
# methodfactory.tests._fixtures.
import os, pathlib, shutil
from methodfactory.tests._fixtures import generate_fixture, standard_workflow
root = pathlib.Path(os.environ["MF_SMOKE"])
shutil.rmtree(root, ignore_errors=True)
generate_fixture(str(root), workflow=standard_workflow)
EOF
mkdir -p "$MF_STORE"
chmod 700 "$MF_STORE"
# source immutability baseline
sha256sum "$MF_SMOKE"/events/*.events.jsonl > "$MF_STORE/source.before"
# Run the installed tooling OUTSIDE the repo (CWD on sys.path would
# shadow the installed package with the checkout copy).
cd "$RUNNER_TEMP"
# installed migrate-store
"$MF_VENV/bin/mf" migrate-store --source "$MF_SMOKE" --dest "$MF_STORE/methodfactory.sqlite3"
# authoritative validation via installed primitives
"$MF_VENV/bin/python" - <<'EOF'
import os
from methodfactory.storage.store import SqliteManifestStore
store = SqliteManifestStore(os.environ["MF_STORE"])
try:
for pkg in store.list_package_ids():
result = store.validate_chain(pkg, verify_artifacts=True)
assert result["valid"], result
print("validated", pkg, result)
finally:
store.close()
EOF
# installed exports (both formats)
"$MF_VENV/bin/mf" export --store "$MF_STORE" --output "$MF_STORE/events-v1.jsonl" --format method-factory-events-v1
"$MF_VENV/bin/mf" export --store "$MF_STORE" --output "$MF_STORE/legacy.jsonl" --format legacy-v012-jsonl
test -s "$MF_STORE/events-v1.jsonl"
test -s "$MF_STORE/legacy.jsonl"
# legacy evidence export revalidates under the frozen legacy reader
"$MF_VENV/bin/python" - <<'EOF'
import os, pathlib, shutil
from methodfactory.migrations.v012_jsonl import LegacySource
lr = pathlib.Path(os.environ["MF_LEGACY_REVAL"])
store = pathlib.Path(os.environ["MF_STORE"])
shutil.rmtree(lr, ignore_errors=True)
(lr / "events").mkdir(parents=True)
(lr / "packages").mkdir()
(lr / "artifacts" / "blobs").mkdir(parents=True)
shutil.copy(store / "legacy.jsonl", lr / "events" / "pkg_demo_001.events.jsonl")
for blob in (store / "blobs").iterdir():
shutil.copy(blob, lr / "artifacts" / "blobs" / blob.name)
LegacySource(lr).validate()
print("legacy evidence export revalidated OK")
EOF
# source unchanged after migration/export
sha256sum "$MF_SMOKE"/events/*.events.jsonl > "$MF_STORE/source.after"
diff "$MF_STORE/source.before" "$MF_STORE/source.after"
echo "source unchanged OK"
# Honest terminology (Finding 4 item 2): this proves a CLEAN TRACKED
# WORKTREE (no uncommitted/untracked tracked-tree changes). Generated
# runtime/build files are gitignored, so their absence is proven by the
# explicit artifact scan below, not by git status alone.
- name: Clean tracked worktree
run: |
test -z "$(git status --porcelain)" || { echo "::error::dirty tracked worktree"; git status --porcelain; exit 1; }
# Explicit runtime/build-artifact scan with a documented allowlist
# (Finding 4 item 2): runtime store artifacts and SQLite files must not
# exist in the filesystem. methodfactory.egg-info IS allowlisted here:
# it is a build byproduct of the `pip install -e ".[test]"` step above
# (generated + gitignored), not a runtime store artifact. build/, dist/,
# and .pytest_cache are checked when they would be produced by this job.
- name: Runtime/build-artifact scan (explicit)
run: |
FAIL=0
for p in .mf build dist .pytest_cache; do
if [ -e "$p" ]; then echo "::error::forbidden generated artifact present: $p"; FAIL=1; fi
done
# SQLite rollback-journal files are forbidden (Finding 4 item 3).
found=$(find . -name '*.sqlite3' -o -name '*.sqlite' -o -name '*.db' \
-o -name '*.wal' -o -name '*.shm' -o -name '*.journal' 2>/dev/null | grep -v '^\./\.git/' || true)
if [ -n "$found" ]; then echo "::error::forbidden SQLite/journal artifact:"; echo "$found"; FAIL=1; fi
exit $FAIL