diff --git a/.github/workflows/VersionCheck.yml b/.github/workflows/VersionCheck.yml index 63396b0..ecedc3d 100644 --- a/.github/workflows/VersionCheck.yml +++ b/.github/workflows/VersionCheck.yml @@ -49,6 +49,45 @@ jobs: steps: - uses: actions/checkout@v7 + # `[sources]` entries that must never reach `main`: an absolute `path` is a + # machine-specific `Pkg.develop` leak (it resolves the dep from a path that + # exists only on the author's machine, staying green on CI where it is + # absent), and a `url` is a cross-repo branch pin that is fine mid-review but + # resolves the dep from an unregistered branch once merged. A relative `path` + # (e.g. a subproject pointing at "..") is legitimate and allowed. Failing in + # draft PRs is intended: the point is to block these from reaching `main`. + - name: "Reject non-portable [sources] entries in Project.toml" + run: | + python3 - <<'PY' + import os, subprocess, sys, tomllib + + files = subprocess.check_output( + ["git", "ls-files", "*Project.toml"], text=True + ).split() + bad = [] + for f in files: + with open(f, "rb") as fh: + data = tomllib.load(fh) + for name, spec in data.get("sources", {}).items(): + if not isinstance(spec, dict): + continue + path = spec.get("path") + if path is not None and os.path.isabs(path): + bad.append(f'{f}: [sources.{name}] has an absolute path = "{path}"') + if "url" in spec: + bad.append(f'{f}: [sources.{name}] has a url = "{spec["url"]}"') + if bad: + print( + "::error::These [sources] entries must not be merged to main: an " + "absolute path is machine-specific, and a url is a cross-repo " + 'branch pin. Use a relative path (e.g. "..") or drop the source.' + ) + for b in bad: + print(b) + sys.exit(1) + print(f"Checked {len(files)} Project.toml file(s); no non-portable [sources] entries.") + PY + - uses: ITensor/ITensorActions/.github/actions/classify-pr@main id: classify with: diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0970e9a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,20 @@ +# ITensorActions + +Reusable GitHub Actions workflows shared across the ITensor Julia packages. Caller +repos pin these workflows at the major-version tag (e.g. +`ITensor/ITensorActions/.github/workflows/Tests.yml@v2`). + +## Releasing (required after any behavior change) + +Callers consume these workflows at the mutable `v2` tag, which does **not** follow +`main`. A change merged to `main` therefore reaches no caller until a release moves +`v2`. So after merging a PR that changes workflow behavior, cut a release: + +- Run `.scripts/release.sh vX.Y.Z` from a clean `main` checkout. It tags the new + version and moves the `v2` pointer to it (and pins the released commit's internal + `@main` action references to a fixed SHA so the release is a stable bundle). +- Choose the bump per the SemVer rule in the README ("Versioning" section): patch + for fixes or internal hardening, minor for additive behavior or new inputs, major + for breaking caller-facing changes. +- Docs-only PRs (including edits to this file) need no release and must not move + `v2`. diff --git a/README.md b/README.md index 8c97204..f7ab1f0 100644 --- a/README.md +++ b/README.md @@ -673,6 +673,15 @@ The suffix is freeform; any non-empty prerelease tag triggers the skip behavior. `VersionCheck` also validates the bump shape pre-merge (patch+1 for non-breaking, minor+1 with patch=0 for pre-1.0 breaking, major+1 with minor=patch=0 for post-1.0 breaking) so malformed bumps fail the PR check directly rather than only after merge in `Registrator`. +### Project.toml `[sources]` check + +Independently of the version-bump check (it runs on every PR, regardless of scope or draft state), `VersionCheck` rejects `[sources]` entries in any tracked `Project.toml` that must not reach `main`: + +- an absolute `path`, which is a machine-specific `Pkg.develop` artifact: it resolves the dependency from a location that exists only on the author's machine, and stays green on CI, where the path is absent, so nothing else catches it; +- a `url`, which is a cross-repo branch pin: legitimate while a dependent PR is in review, but once merged it resolves the dependency from an unregistered branch. + +A relative `path` (for example a subproject pointing at `".."`) is legitimate and allowed. Both the `[sources.Name]` table and the inline `Name = {path = "..."}` forms are checked. Because it runs regardless of draft state, a dependent PR that pins an upstream branch shows a failing check until it drops the pin before merge. + ## Check Compat Bounds The `CheckCompatBounds` workflow instantiates the package and fails if any