diff --git a/.claude/skills/release-manager/SKILL.md b/.claude/skills/release-manager/SKILL.md index a0a331f37..e33fc94a4 100644 --- a/.claude/skills/release-manager/SKILL.md +++ b/.claude/skills/release-manager/SKILL.md @@ -153,11 +153,17 @@ Before a release, check finos/morphir-elm for a newer `extension/elm/v*` tag and finos/morphir-scala for a newer `v*` release, and move the pins. **WASM extensions (Avro, OpenAPI, Python, Rust).** finos/morphir does not build -the guests. finos/morphir-rust owns the check that a new bundle works with the -released CLI. This repository owns the check that a CLI change does not break the -bundles users already installed: CI downloads the bundles pinned in +the guests. The repository that owns an extension checks that a new bundle works +with the released CLI: finos/morphir-rust for Avro, OpenAPI and Rust, and +finos/morphir-python for Python (crate `rust/crates/morphir-python-binding`, +release tags `extension/python/v`). Python bundles up to +`extension/python/v0.4.0` stay in the finos/morphir-rust releases. This +repository owns the check that a CLI change does not break the bundles users +already installed: CI downloads the bundles pinned in `.config/published-extension-bundles.toml` and runs the ignored extension tests -against them. Run the same check with the release binary: +against them. A `[bundles.]` pin names the releasing repository in +`repository`; a pin without one comes from finos/morphir-rust. Run the same check +with the release binary: ```bash mise run ci:fetch-published-bundles @@ -169,10 +175,12 @@ MORPHIR_PYTHON_BUNDLE="$B/python" MORPHIR_RUST_BUNDLE="$B/rust" \ --test python_extension --test rust_extension -- --ignored ``` -Before a release, check finos/morphir-rust releases for newer `extension//v*` -tags and move the pins. When the CLI changes the extension protocol on purpose, -publish a compatible bundle from finos/morphir-rust first, then move the pin in -the same pull request as the CLI change. +Before a release, check the releases of each owning repository for newer +`extension//v*` tags and move the pins: finos/morphir-rust for Avro, OpenAPI +and Rust, finos/morphir-python for Python. A pin that moves to another repository +changes `repository` with its tag, artifact and `sha256`. When the CLI changes the +extension protocol on purpose, publish a compatible bundle from the owning +repository first, then move the pin in the same pull request as the CLI change. Then prove one bundle by hand in a clean home. `generate` needs a project configuration, so the steps run inside a directory with a `morphir.toml`: diff --git a/.config/published-extension-bundles.toml b/.config/published-extension-bundles.toml index 0ace74768..61bc2bd82 100644 --- a/.config/published-extension-bundles.toml +++ b/.config/published-extension-bundles.toml @@ -1,9 +1,12 @@ -# Published finos/morphir-rust extension bundles that CI runs the CLI against. +# Published WASM extension bundles that CI runs the CLI against. # # These are the bundles users install. A CLI change that breaks one of them turns CI red. +# Each bundle is downloaded from the releases of the repository that owns it. `repository` names +# that repository as `owner/name`; a pin without one comes from finos/morphir-rust. # `sha256` is the digest of the guest `.wasm`. It is checked against the download, so a release -# asset that someone replaces does not run. For a deliberate protocol change, publish a compatible bundle from finos/morphir-rust first, -# then move the pin here in the same pull request as the CLI change. +# asset that someone replaces does not run. For a deliberate protocol change, publish a compatible +# bundle from the repository that owns it first, then move the pin here in the same pull request +# as the CLI change. [bundles.avro] tag = "extension/avro/v0.2.0" @@ -15,10 +18,13 @@ tag = "extension/openapi/v0.2.0" artifact = "morphir-openapi-extension-0.2.0" sha256 = "ccef0ebcc8f4fff5579c5c28ee4cb44cdec46a6f4ec993a8e6ceb029996f5ec0" +# finos/morphir-python owns the Python extension. Bundles up to extension/python/v0.4.0 remain in +# the finos/morphir-rust releases. [bundles.python] -tag = "extension/python/v0.4.0" -artifact = "morphir-python-binding-0.4.0" -sha256 = "e5bc0d1593eb1b3f7d33d6a627e07c14c0fd21c9274abc22bd326318c602c36b" +repository = "finos/morphir-python" +tag = "extension/python/v0.4.1" +artifact = "morphir-python-binding-0.4.1" +sha256 = "a04722e16115667fc7c332177805dcb049aef563afcee0e5f80c5fea302f5b4e" [bundles.rust] tag = "extension/rust/v0.3.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40c351551..43efe8515 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -311,9 +311,10 @@ jobs: - name: Fetch the published extensions # The extensions users install, at the tags and digests pinned in - # .config/published-extension-bundles.toml: four WASM bundles from - # finos/morphir-rust, the Elm process extension from finos/morphir-elm - # and the Scala Elm provider from finos/morphir-scala. The owning + # .config/published-extension-bundles.toml: four WASM bundles from the + # repositories that release them, the Elm process extension from + # finos/morphir-elm and the Scala Elm provider from + # finos/morphir-scala. The owning # repository checks that a new release # works with the released CLI; this job checks that a CLI change does # not break the published releases. diff --git a/AGENTS.md b/AGENTS.md index deb929789..465b6570c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,7 @@ Current `mise` releases auto-trust project configuration in normal mode; the exp - **[finos/morphir-rust](https://github.com/finos/morphir-rust)** — Rust libraries (`morphir-core`, `morphir-common`, etc.) used by the CLI - **[finos/morphir-examples](https://github.com/finos/morphir-examples)** — Example Morphir projects - **[finos/morphir-moonbit](https://github.com/finos/morphir-moonbit)** — MoonBit implementation of Morphir tooling -- **[finos/morphir-python](https://github.com/finos/morphir-python)** — Python implementation of Morphir tooling +- **[finos/morphir-python](https://github.com/finos/morphir-python)** — Python implementation of Morphir tooling, and the owner of the Python compiler and the `morphir-python` WASM extension (new `extension/python/v*` releases; bundles up to `v0.4.0` stay in finos/morphir-rust releases) - **[finos/morphir-scala](https://github.com/finos/morphir-scala)** — Scala implementation of Morphir tooling - **[finos/morphir-ui](https://github.com/finos/morphir-ui)** — User-interface work for the Morphir project diff --git a/CHANGELOG.md b/CHANGELOG.md index 0748c8c04..ad9882458 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- The `morphir-python` extension now comes from finos/morphir-python, which maintains the + Python compiler. CI tests the CLI against its `extension/python/v0.4.1` bundle; bundles up to + `extension/python/v0.4.0` remain in the finos/morphir-rust releases. Extension bundle pins + can name the repository that owns them. + ## [0.4.0-beta.10] - 2026-10-21 ### Added diff --git a/INSTALLING.md b/INSTALLING.md index a3b2f8886..3af83b5c7 100644 --- a/INSTALLING.md +++ b/INSTALLING.md @@ -85,17 +85,32 @@ if ($actual -ne $expected) { throw "Checksum verification failed" } ## Install the Python extension -The CLI ships separately from language extensions. With CLI `0.4.0-beta.7` or -later, install the `morphir-python` WASM bundle `extension/python/v0.4.0` to compile -supported Python ADTs, fixed tuples, conditional functions, typed calls and unary -lambdas to Morphir IR v3 or v4 and generate Python. Its version-2 descriptor needs -CLI `0.4.0-beta.7` to publish; with CLI `0.4.0-beta.1` to `0.4.0-beta.6`, use -`extension/python/v0.3.0`. The earlier `v0.1.0` bundle does not compile with this CLI. -See the [Python extension installation guide](https://github.com/finos/morphir-rust/blob/main/docs/tutorials/python-extension.md) +The CLI ships separately from language extensions. The `morphir-python` WASM +extension compiles supported Python ADTs, fixed tuples, conditional functions, +typed calls and unary lambdas to Morphir IR v3 or v4 and generates Python. + +[finos/morphir-python](https://github.com/finos/morphir-python) owns the +extension. It holds the compiler crate, +[`morphir-python-binding`](https://github.com/finos/morphir-python/blob/main/rust/crates/morphir-python-binding/README.md), +and publishes new `extension/python/v` bundles in its releases. The +bundles up to `extension/python/v0.4.0` were released from finos/morphir-rust and +stay in the [finos/morphir-rust releases](https://github.com/finos/morphir-rust/releases). + +With CLI `0.4.0-beta.7` or later, install the bundle `extension/python/v0.4.0`. Its +version-2 descriptor needs CLI `0.4.0-beta.7` to publish; with CLI `0.4.0-beta.1` +to `0.4.0-beta.6`, use `extension/python/v0.3.0`. The earlier `v0.1.0` bundle does +not compile with this CLI. See the +[Python extension guide](https://github.com/finos/morphir-python/blob/main/docs/python-extension.md) for download, repository publication and installation commands, and the -[binding README](https://github.com/finos/morphir-rust/blob/main/crates/morphir-python-binding/README.md) +[binding README](https://github.com/finos/morphir-python/blob/main/rust/crates/morphir-python-binding/README.md) for the supported subset and conformance limits. +finos/morphir-python also packages the same compiler for Python. From the first +`morphir-python` release on PyPI that includes it, `pip install morphir-python` +gives a Python API and a `morphir-python` command that compile and generate +without the Morphir CLI. See the +[Python compiler guide](https://github.com/finos/morphir-python/blob/main/docs/python-compiler.md). + ## Build from source Building from source requires Git, [mise](https://mise.jdx.dev/), and the diff --git a/README.md b/README.md index a81dd843c..6891a4930 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Morphir consists of a few projects based on the features they provide. - **[morphir-bosque](https://github.com/finos/morphir-bosque)** - Integration with the Bosque language. - **[morphir-dotnet](https://github.com/finos/morphir-dotnet)** - Integration with .NET via F#. - **[morphir-rust](https://github.com/finos/morphir-rust)** - Integration with Rust -- **[morphir-python](https://github.com/finos/morphir-python)** - Integration with Python +- **[morphir-python](https://github.com/finos/morphir-python)** - Integration with Python: the IR library and SDK, the Python compiler and the `morphir-python` extension for the Morphir CLI - **[morphir-moonbit](https://github.com/finos/morphir-moonbit)** - MoonBit implementation of Morphir tooling - **[morphir-go](https://github.com/finos/morphir-go)** - Go implementation of Morphir tooling (CLI, WIT pipeline, Go backends) - **[morphir-ui](https://github.com/finos/morphir-ui)** - User-interface work for the Morphir project diff --git a/crates/morphir/tests/host_compatibility.rs b/crates/morphir/tests/host_compatibility.rs index aa1a12b06..42cf8459b 100644 --- a/crates/morphir/tests/host_compatibility.rs +++ b/crates/morphir/tests/host_compatibility.rs @@ -305,7 +305,8 @@ fn openapi() { #[test] #[ignore = "requires MORPHIR_PUBLISHED_BUNDLES"] fn python() { - // morphir-python-binding/tests/pipeline.rs: backend_accepts_an_independently_authored_sum. + // finos/morphir-python, rust/crates/morphir-python-binding/tests/pipeline.rs: + // backend_accepts_an_independently_authored_sum. wasm( "python", serde_json::from_str(include_str!( diff --git a/ecosystem/AGENTS.md b/ecosystem/AGENTS.md index 6439ff0a6..72e3e93e0 100644 --- a/ecosystem/AGENTS.md +++ b/ecosystem/AGENTS.md @@ -8,7 +8,7 @@ This directory holds **git submodules** for Morphir ecosystem repositories. Use - **morphir-rust** – Rust workspace (morphir-core, morphir-common, morphir-daemon, morphir-ext, etc.). The **morphir** CLI binary lives in this repo under `crates/morphir`, not in the submodule; it depends on morphir-rust crates via path. - **morphir-examples** – Example Morphir projects. - **morphir-moonbit** – MoonBit implementation of Morphir tooling. -- **morphir-python** – Python implementation of Morphir tooling. +- **morphir-python** – Python implementation of Morphir tooling. Owns the Python compiler and the `morphir-python` WASM extension. - **morphir-scala** – Scala implementation of Morphir tooling. Mill build, cross-compiled to JVM, JS, and Scala Native. - **morphir-ui** – Morphir's UI monorepo: the morphir-desktop (Electron) and morphir-web apps sharing one Svelte + Effect application (moonrepo + mise + bun toolchain). - **morphir-typescript** – TypeScript binding for the Morphir IR: the reference semantic model, the version and profile codecs, and its MCK adapter. Shared IR and package runners live in the parent Rust CLI, `morphir mck`. TypeScript retains independent adapters/implementations. PKG-2 moves draft.3 corpus/fixture support to parent Rust MCK and bounded runtime helpers to `morphir-package` in [#852](https://github.com/finos/morphir/issues/852). See [spec/mck/migration.md](../spec/mck/migration.md). Bun workspace; runtime-neutral (Node, Bun, Deno, browser). @@ -62,6 +62,8 @@ Valid package names: `morphir-sdk`, `morphir-core`, `morphir-moonbit-bindings` Python implementation of Morphir tooling. Uses `uv` for package management and `behave` for BDD tests. +It also owns the Python compiler, the Rust crate `rust/crates/morphir-python-binding`, and the `morphir-python` WASM extension built from it (extension id `morphir-python`, short id `python`, artifact `morphir-python-binding-`, release tags `extension/python/v`). Bundles up to `extension/python/v0.4.0` were released from finos/morphir-rust and stay in its releases. finos/morphir does not build the bundle: `[bundles.python]` in `.config/published-extension-bundles.toml` pins a published release and names its repository. The guides are `docs/python-extension.md` and `docs/python-compiler.md` in that repository. + ```bash cd ecosystem/morphir-python && uv sync && uv run behave ``` diff --git a/ecosystem/README.md b/ecosystem/README.md index 11f4e64b0..b00aa402b 100644 --- a/ecosystem/README.md +++ b/ecosystem/README.md @@ -10,7 +10,7 @@ This directory contains [git submodules](https://git-scm.com/book/en/v2/Git-Tool | **morphir-rust** | [finos/morphir-rust](https://github.com/finos/morphir-rust) | main | Rust libraries (morphir-core, morphir-common, etc.) used by the morphir CLI in this repo | | **morphir-examples** | [finos/morphir-examples](https://github.com/finos/morphir-examples) | main | Example Morphir projects; used for docs, tests, and reference | | **morphir-moonbit** | [finos/morphir-moonbit](https://github.com/finos/morphir-moonbit) | main | MoonBit implementation of Morphir tooling | -| **morphir-python** | [finos/morphir-python](https://github.com/finos/morphir-python) | main | Python implementation of Morphir tooling | +| **morphir-python** | [finos/morphir-python](https://github.com/finos/morphir-python) | main | Python implementation of Morphir tooling; owns the Python compiler and the `morphir-python` WASM extension | | **morphir-scala** | [finos/morphir-scala](https://github.com/finos/morphir-scala) | main | Scala implementation of Morphir tooling; Mill build cross-compiled to JVM, JS, and Scala Native | | **morphir-ui** | [finos/morphir-ui](https://github.com/finos/morphir-ui) | main | Morphir's UI monorepo: the morphir-desktop (Electron) and morphir-web apps sharing one Svelte + Effect application | @@ -66,7 +66,7 @@ mise run submodules:status - **morphir-rust**: The morphir CLI (in `crates/`) depends on morphir-rust crates via Cargo path dependencies (e.g. `morphir-core`, `morphir-common`). The submodule is required to build those crates. - **morphir-examples**: Used for examples, documentation, and tests. See each submodule's own README for build and usage. - **morphir-moonbit**: MoonBit implementation with packages for SDK, core types, and WASM bindings. See below for build commands. -- **morphir-python**: Python implementation of Morphir tooling. See the submodule's own README for build and usage. +- **morphir-python**: Python implementation of Morphir tooling. It owns the Python compiler (the Rust crate `rust/crates/morphir-python-binding`) and the `morphir-python` WASM extension that the CLI installs, and releases new `extension/python/v` bundles. Bundles up to `extension/python/v0.4.0` were released from finos/morphir-rust and stay there. The CLI in this repository does not build the extension; CI runs it against the bundle pinned in `.config/published-extension-bundles.toml`. See the submodule's own README, `docs/python-extension.md` and `docs/python-compiler.md` for build and usage. - **morphir-scala**: Scala implementation of Morphir tooling, built with Mill. See below for build commands. - **morphir-ui**: Morphir's UI monorepo — the morphir-desktop (Electron) and morphir-web apps sharing one Svelte + Effect application, with `morphir-ir.json` browsing, MORPHIR_HOME-backed configuration, and GitHub token capture. Build from within the submodule (it has its own mise toolchain): `cd ecosystem/morphir-ui && mise install && bun install && moon run :build` (see that repo's README for the full getting-started guide). diff --git a/tests/ci/test_ci_rust_optimizations.py b/tests/ci/test_ci_rust_optimizations.py index 22162fe7b..455b4da03 100644 --- a/tests/ci/test_ci_rust_optimizations.py +++ b/tests/ci/test_ci_rust_optimizations.py @@ -42,8 +42,8 @@ def test_setup_rust_ci_action_shares_cargo_cache(self) -> None: self.assertIn("link-arg=-fuse-ld=mold", self.setup_rust_ci_action) def test_ci_tests_published_bundles_and_builds_no_wasm_guest(self) -> None: - # finos/morphir-rust owns the guest builds. This repository checks that a CLI change - # does not break the bundles users already installed. + # The repository that owns each extension builds its guest. This repository checks + # that a CLI change does not break the bundles users already installed. self.assertIn("mise run ci:fetch-published-bundles", self.ci_workflow) self.assertNotIn("--target wasm32-unknown-unknown", self.ci_workflow) self.assertNotIn("package_extension.py", self.ci_workflow) diff --git a/tools/fetch-published-bundles.test.ts b/tools/fetch-published-bundles.test.ts index 0d1805c35..aaf412306 100644 --- a/tools/fetch-published-bundles.test.ts +++ b/tools/fetch-published-bundles.test.ts @@ -5,6 +5,7 @@ import { join } from "node:path"; import { archiveUrl, assetUrls, + DEFAULT_BUNDLE_REPOSITORY, parseExecutablePins, parsePins, pinnedVersion, @@ -13,6 +14,7 @@ import { const root = join(import.meta.dir, ".."); const pinsText = readFileSync(join(root, ".config/published-extension-bundles.toml"), "utf8"); +const digest = `sha256 = "${"0".repeat(64)}"\n`; describe("published extension bundle pins", () => { test("every pin names a release tag, its artifact and the guest digest", () => { @@ -34,13 +36,57 @@ describe("published extension bundle pins", () => { test("the assets are the guest, its checksum file and the descriptor", () => { const base = "https://github.com/finos/morphir-rust/releases/download/extension/avro/v0.1.1/"; - expect(assetUrls("extension/avro/v0.1.1", "morphir-avro-extension-0.1.1")).toEqual({ + expect( + assetUrls({ + repository: "finos/morphir-rust", + tag: "extension/avro/v0.1.1", + artifact: "morphir-avro-extension-0.1.1", + }), + ).toEqual({ guest: `${base}morphir-avro-extension-0.1.1.wasm`, checksum: `${base}morphir-avro-extension-0.1.1.wasm.sha256`, descriptor: `${base}morphir-avro-extension-0.1.1.release.json`, }); }); + test("a bundle pin without a repository comes from finos/morphir-rust", () => { + const pins = parsePins(`[bundles.avro]\ntag = "extension/avro/v0.1.1"\nartifact = "a-0.1.1"\n${digest}`); + expect(pins.avro?.repository).toBe("finos/morphir-rust"); + expect(DEFAULT_BUNDLE_REPOSITORY).toBe("finos/morphir-rust"); + }); + + test("a bundle is fetched from the repository its pin names", () => { + const pins = parsePins( + `[bundles.python]\nrepository = "finos/morphir-python"\ntag = "extension/python/v0.4.1"\n` + + `artifact = "morphir-python-binding-0.4.1"\n${digest}`, + ); + const pin = pins.python; + if (pin === undefined) { + throw new Error("the parsed pins have no python entry"); + } + expect(pin.repository).toBe("finos/morphir-python"); + expect(assetUrls(pin).guest).toBe( + "https://github.com/finos/morphir-python/releases/download/extension/python/v0.4.1/" + + "morphir-python-binding-0.4.1.wasm", + ); + }); + + test("a bundle pin with an empty or malformed repository is refused", () => { + const rest = `tag = "extension/avro/v0.1.1"\nartifact = "a-0.1.1"\n${digest}`; + for (const repository of ['""', '"finos"', '"finos/"', '"/morphir-rust"', '"finos/morphir-rust/x"', + '"https://github.com/finos/morphir-rust"', '"finos/.."', '"finos/morphir rust"', "1"]) { + expect(() => parsePins(`[bundles.avro]\nrepository = ${repository}\n${rest}`)).toThrow( + /bundles\.avro.*repository/, + ); + } + }); + + test("every bundle pin names the repository that released it", () => { + for (const pin of Object.values(parsePins(pinsText))) { + expect(pin.repository).toMatch(/^finos\/morphir-(rust|python)$/); + } + }); + test("a guest is checked against the digest pinned in this repository", () => { const guest = Buffer.from("guest"); const digest = createHash("sha256").update(guest).digest("hex"); @@ -74,6 +120,13 @@ describe("published extension bundle pins", () => { ); }); + test("an executable pin with a malformed repository is refused", () => { + const pin = `tag = "v1.0.0"\nasset = "a"\nexecutable = "a"\n${digest}`; + expect(() => parseExecutablePins(`[executables.x]\nrepository = "finos"\n${pin}`)).toThrow( + /executables\.x.*repository/, + ); + }); + test("a pin of a raw executable names an asset and no archive", () => { // finos/morphir-scala releases morphir-scala-elm as the executable itself, with its v* release. const scala = parseExecutablePins(pinsText)["scala-elm"]; diff --git a/tools/fetch-published-bundles.ts b/tools/fetch-published-bundles.ts index c3b757e7e..2eaf941be 100644 --- a/tools/fetch-published-bundles.ts +++ b/tools/fetch-published-bundles.ts @@ -4,6 +4,8 @@ // CI runs the CLI built from a pull request against these bundles, which are the ones users // install. Each guest is checked against the sha256 pinned in this repository, not against the // .sha256 asset of the same release, so replaced release assets fail instead of running. +// A bundle is downloaded from the releases of the repository its pin names, finos/morphir-rust +// when the pin names none. // Each bundle directory holds exactly what `extension repository publish` expects: the // descriptor named release.json, the guest and its checksum file. A process extension is pinned // under [executables.] and unpacked to one executable. @@ -11,9 +13,20 @@ import { createHash } from "node:crypto"; import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { join, resolve } from "node:path"; -const RELEASES = "https://github.com/finos/morphir-rust/releases/download"; +/** The repository a [bundles.*] pin downloads from when it names none. */ +export const DEFAULT_BUNDLE_REPOSITORY = "finos/morphir-rust"; -export type Pin = { tag: string; artifact: string; sha256: string }; +// A GitHub `owner/name`. It becomes part of a download URL, so nothing else is accepted. +const REPOSITORY = /^[A-Za-z0-9][A-Za-z0-9-]*\/(?!\.\.?$)[A-Za-z0-9._-]+$/; + +function checkRepository(where: string, repository: unknown): string { + if (typeof repository !== "string" || !REPOSITORY.test(repository)) { + throw new Error(`${where} has no repository of the form owner/name: ${JSON.stringify(repository)}`); + } + return repository; +} + +export type Pin = { repository: string; tag: string; artifact: string; sha256: string }; export function parsePins(text: string): Record { const bundles = (Bun.TOML.parse(text) as { bundles?: Record> }).bundles ?? {}; @@ -24,7 +37,11 @@ export function parsePins(text: string): Record { throw new Error(`bundles.${shortId} has no ${key}`); } } - pins[shortId] = pin as Pin; + const repository = + "repository" in pin + ? checkRepository(`bundles.${shortId}`, pin.repository) + : DEFAULT_BUNDLE_REPOSITORY; + pins[shortId] = { ...(pin as Pin), repository }; } return pins; } @@ -52,6 +69,7 @@ export function parseExecutablePins(text: string): Record throw new Error(`executables.${shortId} has no ${key}`); } } + checkRepository(`executables.${shortId}`, pin.repository); const named = [pin.archive, pin.asset].filter((name) => typeof name === "string" && name !== ""); if (named.length !== 1) { throw new Error(`executables.${shortId} must name exactly one of archive or asset`); @@ -74,8 +92,8 @@ export function archiveUrl(pin: Pick) { + const base = `https://github.com/${pin.repository}/releases/download/${pin.tag}/${pin.artifact}`; return { guest: `${base}.wasm`, checksum: `${base}.wasm.sha256`, descriptor: `${base}.release.json` }; } @@ -95,7 +113,7 @@ async function download(url: string): Promise { } async function fetchBundle(shortId: string, pin: Pin, output: string): Promise { - const urls = assetUrls(pin.tag, pin.artifact); + const urls = assetUrls(pin); const guest = await download(urls.guest); verifyGuest(shortId, guest, pin.sha256); const bundle = join(output, shortId); @@ -104,7 +122,7 @@ async function fetchBundle(shortId: string, pin: Pin, output: string): Promise ${bundle}`); + console.log(`${shortId}: ${pin.repository} ${pin.tag} -> ${bundle}`); } async function fetchExecutable(shortId: string, pin: ExecutablePin, output: string): Promise {