From 2791a9a8535afd9f4a2dd8c1ed3355841055c4c4 Mon Sep 17 00:00:00 2001 From: Astroyimin <149473134+Astroyimin@users.noreply.github.com> Date: Sat, 18 Jul 2026 03:18:11 +0800 Subject: [PATCH] Add repository safety controls --- .github/CODEOWNERS | 13 ++++ .github/workflows/ci.yml | 99 +++++++++++++++++++++++++++ AGENTS.md | 142 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/ci.yml create mode 100644 AGENTS.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..6bee5d4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,13 @@ +# Default ownership. Replace the individual owner with visible ABHModels teams +# when additional maintainers receive write access. +* @Astroyimin + +# High-risk scientific and repository-governance areas. +/source/ @Astroyimin +/maindaocl.cpp @Astroyimin +/Makefile @Astroyimin +/paper_data/ @Astroyimin +/.github/ @Astroyimin +/AGENTS.md @Astroyimin +/CHANGELOG.md @Astroyimin +/CITATION.cff @Astroyimin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..71445c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + fast-checks: + name: Fast checks + runs-on: ubuntu-24.04 + + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.13" + + - name: Install metadata validator + run: python -m pip install --disable-pip-version-check "PyYAML>=6.0,<7" + + - name: Check changed-file whitespace + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + if [ -n "$BASE_SHA" ]; then + git diff --check "$BASE_SHA"...HEAD + else + git diff-tree --check --root --no-commit-id -r HEAD + fi + + - name: Validate release metadata + run: | + python - <<'PY' + from pathlib import Path + import re + import yaml + + citation = yaml.safe_load(Path("CITATION.cff").read_text(encoding="utf-8")) + version = str(citation["version"]) + release_date = str(citation["date-released"]) + changelog = Path("CHANGELOG.md").read_text(encoding="utf-8") + match = re.search( + r"^## \[([^]]+)\] - (\d{4}-\d{2}-\d{2})$", + changelog, + flags=re.MULTILINE, + ) + if match is None: + raise SystemExit("CHANGELOG.md has no release heading") + if (version, release_date) != match.groups(): + raise SystemExit( + "CITATION.cff and the latest CHANGELOG.md release do not agree: " + f"citation={(version, release_date)!r}, changelog={match.groups()!r}" + ) + print(f"release metadata OK: v{version} ({release_date})") + PY + + - name: Reject tracked local or generated files + run: | + python - <<'PY' + from pathlib import Path + import subprocess + + tracked = subprocess.check_output( + ["git", "ls-files"], text=True + ).splitlines() + forbidden = [] + for name in tracked: + path = Path(name) + if name.endswith(".local.md"): + forbidden.append(name) + if path.parts and path.parts[0] in {"results", "kernel", "comp_cs"}: + forbidden.append(name) + if path.name in {"run.log", ".DS_Store"}: + forbidden.append(name) + if forbidden: + raise SystemExit("forbidden tracked files:\n" + "\n".join(sorted(set(forbidden)))) + print("tracked-file policy OK") + PY + + - name: Check Python syntax + run: git ls-files -z '*.py' | xargs -0 --no-run-if-empty python -m py_compile + + - name: Compile standalone Compton-kernel diagnostic + run: make test_kernel_norm diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..49205f6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,142 @@ +# DAO Repository Safety Rules + +These rules apply to every change, commit, tag, and remote operation in this +repository. Safety, reproducibility, and physical correctness take precedence +over convenience or a clean-looking history. + +## 1. Repository roles + +- `ABHModels/DAO:main` is the canonical public, stable, releasable branch. +- `DAO-Model/DAOv2:main` is a private mirror of the public stable branch. It is + not an independent development line. +- Private integration work belongs on `origin/dev` or short-lived branches. +- Task branches should use `codex/` unless the user requests another + name. +- Never allow the two `main` branches to accept unrelated direct development. + +## 2. Remote safety + +Before every fetch, push, tag publication, or release operation, run: + +```bash +git status --short --branch +git remote -v +git branch -vv +``` + +Expected remote ownership: + +- `origin` fetches from and pushes to `DAO-Model/DAOv2`. +- `abh` fetches from and pushes to `ABHModels/DAO`. + +Stop if a remote fetch URL and push URL refer to different repositories, or if +`origin` pushes to `ABHModels/DAO`. Do not silently repair remote configuration; +report the mismatch and get user authorization first. + +- Never configure one remote to push automatically to both the private and + public repositories. +- Never use `git push --all` or `git push --tags` against the public repository. +- Push only the named branch and, for a release, the one intended tag. +- SSH (`git@github.com:ABHModels/DAO.git`) is the preferred fallback when the + local HTTPS proxy is unavailable. Do not place tokens in remote URLs. +- `PUSH_NOTES.local.md` and all `*.local.md` files are local-only and must never + be committed. +- A public push requires an explicit user request. A normal code-change request + does not authorize publishing. + +## 3. Branch and pull-request workflow + +- Do not develop directly on `main`. +- Start public work from the current `abh/main`, not from a stale local branch. +- Use one branch per coherent change and keep commits reviewable. +- Prefer a pull request into public `main`; direct pushes to public `main` are + reserved for an explicit user-requested release or emergency action. +- Before integrating, inspect divergence with `git log --left-right` and verify + ancestry with `git merge-base --is-ancestor`. +- If branches diverge, preserve both histories with an inspected merge or a PR. + Never resolve divergence by force-pushing or silently dropping commits. +- Never merge private history into the public repository if that history may + contain secrets, unpublished material, or restricted data. Create a clean + branch from `abh/main` and import only the approved patch or commits. + +## 4. Required checks before a commit + +- Preserve unrelated user changes; stage only files that belong to the task. +- Review both `git diff` and `git diff --cached`. +- Run `git diff --check`. +- Validate `CITATION.cff` as YAML whenever it changes. +- Compile and run the most relevant available tests. +- For scientific or numerical changes, require regression evidence appropriate + to the affected physics: finite values, convergence, conservation checks, + limiting cases, and comparison with a trusted baseline using documented + tolerances. +- Treat `test_kernel_norm` as a diagnostic unless it contains explicit pass/fail + assertions. Do not report it as an accuracy pass merely because it exits 0. +- If the full Cloudy/HEASoft build cannot run locally, state that limitation in + the commit or PR summary; do not imply full validation. + +## 5. Public release workflow + +For a release `vX.Y.Z`: + +1. Confirm the worktree is clean and fetch the current public `main` and tags. +2. Create `release/vX.Y.Z` from the current `abh/main`. +3. Import only the approved, tested changes. +4. Update `CHANGELOG.md` and `CITATION.cff`; if a `VERSION` file exists, update + it too. All version strings and dates must agree. +5. Run the fast checks and the full physics/integration checks required by the + changed modules. +6. Push the release branch and merge it through a reviewed PR whenever possible. +7. Create the annotated release tag only after the final public merge commit is + known. Prefer a signed tag when signing is configured. +8. Verify that the tag peels to the exact intended commit, then push public + `main` and only that tag. +9. Verify the remote branch and tag independently, then synchronize the private + stable mirror. + +Published version tags are immutable in practice: + +- Never move, overwrite, or delete a published `v*` tag. +- Never use `--force` or `--force-with-lease` on public `main` or a release tag. +- If a release is wrong, publish the next patch version instead of rewriting the + existing release. + +## 6. Large files and generated data + +- Source code, small test fixtures, parameter files, and curated reference data + may be tracked. +- Runtime output, caches, temporary diagnostics, and reproducible intermediates + must remain ignored. +- `paper_data/` is for curated reproducibility artifacts, not raw run directories. +- Any newly added file larger than 1 MiB requires explicit justification in the + PR. Do not add files larger than 10 MiB to normal Git without user approval. +- Prefer Git LFS, a GitHub Release asset, or an archival data repository such as + Zenodo for necessary large datasets and binary artifacts. +- Do not rewrite existing public history merely to migrate data without an + explicit migration and backup plan. +- Before a public push, inspect newly tracked large files and confirm that no + `results/`, `kernel/`, `comp_cs/`, logs, local notes, or credentials are staged. + +## 7. GitHub protection policy + +The public repository should protect `main` with a GitHub ruleset requiring: + +- pull requests for normal changes; +- required CI/status checks; +- resolved review conversations; +- stale approval dismissal after new pushes; +- no branch deletion or force push; +- code-owner review when multiple maintainers are available. + +A separate `v*` tag ruleset should restrict tag creation, updates, and deletion. +Enable secret-scanning push protection. Formal releases should be published as +immutable GitHub Releases when available. + +## 8. Destructive-operation policy + +- Do not run `git reset --hard`, destructive checkout/restore commands, history + filters, or force pushes without explicit user authorization and a verified + backup. +- Prefer revert commits for published mistakes. +- Before any history migration, create and verify a mirror or `git bundle` + backup stored outside the working repository.