diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81efb3a..37a4c6f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, macos-14, macos-15-intel, windows-2022] + os: [ubuntu-22.04, macos-14, windows-2022] defaults: run: shell: bash @@ -57,8 +57,7 @@ jobs: version=${{ env.GCC_V }} brew_prefix="$(brew --prefix)" libpath="$brew_prefix/opt/gcc@$version/lib/gcc/$version" - # libgcc_s.1.1 has no static counterpart on Intel; only hide it on ARM - [[ "$(uname -m)" == "arm64" ]] && mv $libpath/libgcc_s.1.1.dylib $libpath/libgcc_s.1.1.dylib.bak + mv $libpath/libgcc_s.1.1.dylib $libpath/libgcc_s.1.1.dylib.bak mv $libpath/libgfortran.5.dylib $libpath/libgfortran.5.dylib.bak mv $libpath/libquadmath.0.dylib $libpath/libquadmath.0.dylib.bak mv $libpath/libstdc++.6.dylib $libpath/libstdc++.6.dylib.bak @@ -112,6 +111,14 @@ jobs: 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 + - name: Patch code.json with fetched program versions + run: | + ostag="${{ steps.ostag.outputs.ostag }}" + pixi run --manifest-path modflow6/pixi.toml python scripts/patch_code_json.py \ + --manifest releases.json \ + --ostag $ostag \ + --zip $ostag.zip + - name: Show programs run: | ostag="${{ steps.ostag.outputs.ostag }}" diff --git a/scripts/patch_code_json.py b/scripts/patch_code_json.py new file mode 100644 index 0000000..b5a21c3 --- /dev/null +++ b/scripts/patch_code_json.py @@ -0,0 +1,117 @@ +"""Patch code.json with correct version info for programs fetched from releases.json. + +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. + +Usage: + python patch_code_json.py --manifest releases.json --ostag linux --zip linux.zip + python patch_code_json.py --manifest releases.json --ostag linux code.json +""" + +import argparse +import datetime +import json +import sys +import urllib.request +import zipfile +from pathlib import Path + +GITHUB_URL = "https://github.com/{repo}/releases/download/{tag}/{asset}" + + +def _get_url_date(url): + """Fetch Last-Modified date from a URL's response headers.""" + try: + req = urllib.request.Request(url, method="HEAD") + with urllib.request.urlopen(req, timeout=10) as resp: + for key in ("Last-Modified", "Date"): + val = resp.headers.get(key) + if val: + dt = datetime.datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z") + return dt.strftime("%m/%d/%Y") + except Exception: + pass + return None + + +def patch(manifest_path, code_json_path, ostag, zip_path=None): + with open(manifest_path) as f: + manifest = json.load(f) + + code_json_path = Path(code_json_path) + if not code_json_path.exists(): + print(f"code.json not found: {code_json_path}", file=sys.stderr) + sys.exit(1) + + with open(code_json_path) as f: + code = json.load(f) + + changed = [] + for entry in manifest: + repo = entry["repo"] + tag = entry["tag"] + version = tag.lstrip("v") + asset = entry["assets"].get(ostag) + url = GITHUB_URL.format(repo=repo, tag=tag, asset=asset) if asset else None + + for prog_name in entry["programs"]: + if prog_name not in code: + continue + prev_version = code[prog_name].get("version") + if prev_version == version: + continue + code[prog_name]["version"] = version + if url: + code[prog_name]["url"] = url + date = _get_url_date(url) + if date: + code[prog_name]["url_download_asset_date"] = date + changed.append(f" {prog_name}: {prev_version!r} -> {version!r}") + + if not changed: + 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 zip_path and Path(zip_path).exists(): + with zipfile.ZipFile(zip_path, "a", zipfile.ZIP_DEFLATED) as zf: + zf.write(code_json_path, "code.json") + print(f"updated code.json in {zip_path}") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.split("\n")[0]) + parser.add_argument( + "code_json", + nargs="?", + default="code.json", + help="path to code.json to patch (default: code.json)", + ) + parser.add_argument( + "--manifest", + default="releases.json", + help="path to releases.json manifest (default: releases.json)", + ) + parser.add_argument( + "--ostag", + required=True, + help="platform tag (linux, mac, macarm, win64)", + ) + parser.add_argument( + "--zip", + dest="zip_path", + help="zip file to update with the patched code.json", + ) + args = parser.parse_args() + patch(args.manifest, args.code_json, args.ostag, args.zip_path) + + +if __name__ == "__main__": + main()