Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ jobs:
fetched=$(pixi run --manifest-path modflow6/pixi.toml python scripts/fetch_releases.py --manifest releases.json --list)
pixi run --manifest-path modflow6/pixi.toml make-program : --appdir $ostag --exclude "$fetched" --keep --zip $ostag.zip --verbose
pixi run --manifest-path modflow6/pixi.toml make-program mf2005,mfusg,mfnwt,mflgr --appdir $ostag --double --keep --zip $ostag.zip --verbose
pixi run --manifest-path modflow6/pixi.toml make-code-json --appdir $ostag --zip $ostag.zip --verbose
pixi run --manifest-path modflow6/pixi.toml make-code-json --appdir $ostag --verbose

- name: Patch code.json with fetched program versions
- name: Patch code.json and code.md with fetched program versions, add code.json to zip
run: |
ostag="${{ steps.ostag.outputs.ostag }}"
pixi run --manifest-path modflow6/pixi.toml python scripts/patch_code_json.py \
Expand Down
9 changes: 7 additions & 2 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,13 @@ The file `releases.json` in the repository root is a manifest listing programs t
During a release build, the workflow:

1. Runs `scripts/fetch_releases.py` to download pre-built binaries for the current platform from each repository listed in `releases.json`.
2. Runs pymake to compile the remaining programs, excluding those already fetched.
3. Combines everything into the platform zip alongside pymake-generated metadata.
2. Runs pymake to compile the remaining programs, excluding those already fetched, and adds them to the platform zip.
3. Runs pymake's `make-code-json` to generate `code.json` and `code.md` metadata. These come from pymake's internal program catalog, which may list different versions than were actually fetched.
4. Runs `scripts/patch_code_json.py` to correct the version, URL, and date in `code.json` for each program in `releases.json`, regenerate `code.md` from the corrected `code.json`, and add `code.json` to the platform zip.

`make-code-json` is deliberately run without `--zip`, so the patch script is the only thing that writes `code.json` into the zip. If the zip already contains a `code.json`, the patch script fails rather than adding a second, conflicting entry. The `code.json` that `make-code-json` also writes into the program directory is not corrected, but that directory is not distributed, only the zip is.

The top-level `code.json` and `code.md` are uploaded with the build artifacts. The release job uses `code.md` for the release notes and for the program table in `README.md`.

The fetch script handles platform-specific file extensions (`.exe`, `.dll`, `.dylib`, `.so`) and supports renaming programs when the archive filename differs from the distribution name (e.g., `mfusgt` in the archive becomes `mfusg_gsi` in the distribution).

Expand Down
75 changes: 62 additions & 13 deletions scripts/patch_code_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
make-code-json reads versions from pymake's internal program catalog, which may
be out of date relative to the versions actually fetched. This script overwrites
the version (and url) for each program listed in releases.json with the values
from the releases.json manifest.
from the releases.json manifest, then regenerates code.md (also written by
make-code-json from the same stale data) from the patched code.json.

If --zip is given, the patched code.json is added to the archive. This script
is meant to be the only thing that puts code.json in the zip, so make-code-json
should be run *without* --zip; if the archive already contains a code.json the
script fails rather than writing a duplicate entry.

Usage:
python patch_code_json.py --manifest releases.json --ostag linux --zip linux.zip
Expand Down Expand Up @@ -36,7 +42,29 @@ def _get_url_date(url):
return None


def patch(manifest_path, code_json_path, ostag, zip_path=None):
def write_code_md(code, code_md_path):
"""Write the code.md table in the same format as pymake's make-code-json."""
lines = [
"| Program | Version | UTC Date |",
"| ------- | ------- | ---- |",
]
for target in sorted(code):
target_dict = code[target]
date = target_dict.get("url_download_asset_date")
line = f"| {target} | {target_dict['version']} |"
line += f" {date} |" if date else " |"
lines.append(line)
Path(code_md_path).write_text("\n".join(lines) + "\n")
print(f"wrote {code_md_path}")


def patch(
manifest_path,
code_json_path,
ostag,
zip_path=None,
code_md_path=None,
):
with open(manifest_path) as f:
manifest = json.load(f)

Expand Down Expand Up @@ -70,20 +98,28 @@ def patch(manifest_path, code_json_path, ostag, zip_path=None):
code[prog_name]["url_download_asset_date"] = date
changed.append(f" {prog_name}: {prev_version!r} -> {version!r}")

if not changed:
if changed:
for line in changed:
print(line)
code_json_path.write_text(json.dumps(code, indent=4) + "\n")
print(f"wrote {code_json_path}")
else:
print("code.json already up to date")
return

for line in changed:
print(line)

code_json_path.write_text(json.dumps(code, indent=4) + "\n")
print(f"wrote {code_json_path}")
if code_md_path:
write_code_md(code, code_md_path)

if zip_path and Path(zip_path).exists():
if zip_path:
with zipfile.ZipFile(zip_path, "a", zipfile.ZIP_DEFLATED) as zf:
if "code.json" in zf.namelist():
print(
f"{zip_path} already contains code.json; run make-code-json "
"without --zip so this script is the only writer",
file=sys.stderr,
)
sys.exit(1)
zf.write(code_json_path, "code.json")
print(f"updated code.json in {zip_path}")
print(f"added code.json to {zip_path}")


def main():
Expand All @@ -107,10 +143,23 @@ def main():
parser.add_argument(
"--zip",
dest="zip_path",
help="zip file to update with the patched code.json",
help="zip file to add the patched code.json to",
)
parser.add_argument(
"--code-md",
dest="code_md_path",
default="code.md",
help="path to code.md to regenerate from the patched code.json "
"(default: code.md; pass an empty string to skip)",
)
args = parser.parse_args()
patch(args.manifest, args.code_json, args.ostag, args.zip_path)
patch(
args.manifest,
args.code_json,
args.ostag,
args.zip_path,
args.code_md_path or None,
)


if __name__ == "__main__":
Expand Down
Loading