From 95ad7072c1c7397e3a2f9164e5e18764bfe156c1 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 5 Oct 2026 11:33:54 +1300 Subject: [PATCH 1/3] Adopt the canonical Bake publishing workflow. --- .github/workflows/publish.yml | 234 ++-------------------------------- Cargo.lock | 12 +- 2 files changed, 19 insertions(+), 227 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c8fc2f7..b01c33c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,98 +25,17 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: clippy, rustfmt + - name: Install Bake launcher + run: cargo install socketry-cargo-bake --locked - name: Detect a versioned release change id: detect env: - BAKE_BEFORE: ${{ github.event.before || github.event.pull_request.base.sha }} + BAKE_BEFORE: ${{ github.event.pull_request.base.sha || github.event.before }} BAKE_SHA: ${{ github.sha }} - run: | - cargo metadata --format-version 1 --no-deps --locked > "$RUNNER_TEMP/bake-metadata.json" - python3 - <<'PY' - import json - import os - import re - import subprocess - import tomllib - from pathlib import Path - - metadata = json.loads((Path(os.environ["RUNNER_TEMP"]) / "bake-metadata.json").read_text()) - root = Path(metadata["workspace_root"]) - members = set(metadata["workspace_members"]) - packages = [] - for package in metadata["packages"]: - if package["id"] not in members: - continue - publish = package.get("publish") - if publish is False or isinstance(publish, list) and "crates-io" not in publish: - continue - packages.append(package) - if not packages: - raise SystemExit("Workspace has no publishable packages") - - versions = {package["version"] for package in packages} - if len(versions) != 1: - raise SystemExit(f"Publishable workspace packages must share one version: {sorted(versions)}") - version = versions.pop() - pattern = re.compile(r"(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\Z") - match = pattern.fullmatch(version) - if match is None: - raise SystemExit(f"Release version must use stable MAJOR.MINOR.PATCH form: {version}") - current = tuple(map(int, match.groups())) - - before = os.environ["BAKE_BEFORE"] - prior_versions = [] - if before and set(before) != {"0"}: - root_manifest = subprocess.run( - ["git", "show", f"{before}:Cargo.toml"], check=True, capture_output=True, text=True - ).stdout - prior_root = tomllib.loads(root_manifest) - workspace_version = prior_root.get("workspace", {}).get("package", {}).get("version") - for package in packages: - manifest = Path(package["manifest_path"]).relative_to(root).as_posix() - result = subprocess.run( - ["git", "show", f"{before}:{manifest}"], capture_output=True, text=True - ) - if result.returncode != 0: - continue - document = tomllib.loads(result.stdout) - package_version = document.get("package", {}).get("version") - if isinstance(package_version, dict) and package_version.get("workspace") is True: - package_version = workspace_version - if package_version is not None: - prior_versions.append(package_version) - - if prior_versions: - prior_set = set(prior_versions) - if len(prior_set) != 1: - raise SystemExit(f"Previous publishable packages did not share one version: {sorted(prior_set)}") - prior = next(iter(prior_set)) - prior_match = pattern.fullmatch(prior) - if prior_match is None: - raise SystemExit(f"Previous workspace version is not stable MAJOR.MINOR.PATCH: {prior}") - previous = tuple(map(int, prior_match.groups())) - if current < previous: - raise SystemExit(f"Workspace version {version} is lower than previous version {prior}") - if current == previous: - with open(os.environ["GITHUB_OUTPUT"], "a") as output: - output.write("release=false\nversion=\n") - raise SystemExit(0) - - release_notes = (root / "releases.md").read_text() - heading = re.compile(rf"^## v{re.escape(version)}(?:\s|$)", re.MULTILINE) - if heading.search(release_notes) is None: - raise SystemExit(f"releases.md must contain a '## v{version}' heading") - - tag = f"v{version}" - existing = subprocess.run( - ["git", "rev-list", "-n", "1", f"refs/tags/{tag}"], capture_output=True, text=True - ) - if existing.returncode == 0 and existing.stdout.strip() != os.environ["BAKE_SHA"]: - raise SystemExit(f"Tag {tag} already exists at a different commit") - - with open(os.environ["GITHUB_OUTPUT"], "a") as output: - output.write(f"release=true\nversion={version}\n") - PY + run: cargo bake --locked cargo:release:detect --base "$BAKE_BEFORE" --sha "$BAKE_SHA" + - name: Validate release candidate + if: steps.detect.outputs.release == 'true' + run: cargo bake --locked cargo:release - name: Check formatting run: cargo fmt --all -- --check - name: Run Clippy @@ -139,147 +58,20 @@ jobs: with: fetch-depth: 0 - uses: dtolnay/rust-toolchain@stable + - name: Install Bake launcher + run: cargo install socketry-cargo-bake --locked - name: Find workspace packages that need publishing id: packages env: BAKE_VERSION: ${{ needs.check.outputs.version }} - run: | - cargo metadata --format-version 1 --no-deps --locked > "$RUNNER_TEMP/bake-metadata.json" - python3 - <<'PY' - import json - import os - from pathlib import Path - from urllib.error import HTTPError, URLError - from urllib.parse import quote - from urllib.request import Request, urlopen - - metadata = json.loads((Path(os.environ["RUNNER_TEMP"]) / "bake-metadata.json").read_text()) - members = set(metadata["workspace_members"]) - packages = [] - for package in metadata["packages"]: - if package["id"] not in members: - continue - publish = package.get("publish") - if publish is False or isinstance(publish, list) and "crates-io" not in publish: - continue - if package["version"] != os.environ["BAKE_VERSION"]: - raise SystemExit(f"{package['name']} has version {package['version']}, expected {os.environ['BAKE_VERSION']}") - packages.append(package) - if not packages: - raise SystemExit("Workspace has no publishable packages") - - existing = [] - for package in packages: - name = package["name"] - url = f"https://crates.io/api/v1/crates/{quote(name, safe='')}/versions" - request = Request(url, headers={"User-Agent": "socketry-bake-publish-workflow"}) - try: - with urlopen(request, timeout=30) as response: - registry = json.load(response) - except HTTPError as error: - if error.code == 404: - continue - raise SystemExit(f"Could not check crates.io for {name}: HTTP {error.code}") - except (URLError, TimeoutError, json.JSONDecodeError) as error: - raise SystemExit(f"Could not check crates.io for {name}: {error}") - versions = registry.get("versions") - if not isinstance(versions, list): - raise SystemExit(f"Unexpected crates.io response while checking {name}") - if any(item.get("num") == os.environ["BAKE_VERSION"] for item in versions if isinstance(item, dict)): - existing.append(name) - - missing = [package["name"] for package in packages if package["name"] not in existing] - with open(os.environ["GITHUB_OUTPUT"], "a") as output: - output.write(f"exclude={json.dumps(existing)}\n") - output.write(f"has_packages={'true' if missing else 'false'}\n") - PY + run: cargo bake --locked cargo:publish:pending --version "$BAKE_VERSION" - uses: rust-lang/crates-io-auth-action@v1 id: auth if: steps.packages.outputs.has_packages == 'true' - - name: Publish workspace packages - if: steps.packages.outputs.has_packages == 'true' + - name: Publish workspace and create GitHub Release env: - BAKE_EXCLUDE: ${{ steps.packages.outputs.exclude }} CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} - run: | - python3 - <<'PY' - import json - import os - import subprocess - - arguments = ["cargo", "publish", "--workspace", "--locked"] - for package in json.loads(os.environ["BAKE_EXCLUDE"]): - arguments.extend(["--exclude", package]) - subprocess.run(arguments, check=True) - PY - - name: Create release tag after successful publication - env: - BAKE_VERSION: ${{ needs.check.outputs.version }} - BAKE_SHA: ${{ github.sha }} - run: | - tag="v${BAKE_VERSION}" - if git rev-parse --verify --quiet "refs/tags/${tag}"; then - if [ "$(git rev-list -n 1 "${tag}")" != "${BAKE_SHA}" ]; then - echo "Tag ${tag} already exists at a different commit" >&2 - exit 1 - fi - exit 0 - fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git tag -a "${tag}" "${BAKE_SHA}" -m "Release ${tag}" - git push origin "refs/tags/${tag}" - - name: Generate GitHub Release notes - env: - BAKE_VERSION: ${{ needs.check.outputs.version }} - run: | - cargo install socketry-cargo-bake --locked - cargo bake releases:notes "v${BAKE_VERSION}" > "$RUNNER_TEMP/release-notes.md" - - name: Create or update GitHub Release - env: GH_TOKEN: ${{ github.token }} BAKE_VERSION: ${{ needs.check.outputs.version }} - BAKE_RELEASE_NOTES: ${{ runner.temp }}/release-notes.md - run: | - python3 - <<'PY' - import json - import os - import subprocess - from pathlib import Path - - tag = f"v{os.environ['BAKE_VERSION']}" - notes = Path(os.environ["BAKE_RELEASE_NOTES"]).read_text() - existing = subprocess.run( - ["gh", "release", "view", tag, "--json", "tagName,name,body,isDraft,url"], - capture_output=True, - text=True, - ) - - if existing.returncode == 0: - release = json.loads(existing.stdout) - if ( - release.get("tagName") == tag - and release.get("name") == tag - and release.get("body", "") == notes - and release.get("isDraft") is False - ): - print(release["url"]) - raise SystemExit(0) - action = "edit" - elif existing.stderr.strip() == "release not found": - action = "create" - else: - raise SystemExit(f"Could not inspect GitHub release {tag}: {existing.stderr.strip()}") - - arguments = ["gh", "release", action, tag, "--title", tag, "--notes-file", "-", "--verify-tag"] - if action == "edit": - arguments.append("--draft=false") - - result = subprocess.run(arguments, input=notes, capture_output=True, text=True) - if result.returncode != 0: - raise SystemExit(f"GitHub release {action} failed: {result.stderr.strip()}") - url = result.stdout.strip() if action == "create" else release["url"] - if not url: - raise SystemExit(f"GitHub release {action} returned no URL") - print(url) - PY + BAKE_SHA: ${{ github.sha }} + run: cargo bake --locked cargo:release:publish --version "$BAKE_VERSION" --sha "$BAKE_SHA" diff --git a/Cargo.lock b/Cargo.lock index 79b7ec9..b265ec7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,9 +92,9 @@ dependencies = [ [[package]] name = "bake-cargo" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65773524aaf038da8f0eda00e606e3566ff64d05810271110405113d1edc6de4" +checksum = "b29fac66b2f6541b43c2f296910d71666c4286181bc66d16b971e64b6f96ce67" dependencies = [ "bake", "bake-license", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "bake-license" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8afbd6315e3ffd25d1546a11ca828d2c9724a3eccbe70877e599febd8365dc7" +checksum = "4877b435fe237311d88e546e8fb2b4a9021aff3550b625b8286e27da0ec693fd" dependencies = [ "bake", "socketry-markdown", @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "bake-releases" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253eb860363267c807f3d634c9c95abf060d7c22600d9fb0369ae815dfbd271" +checksum = "8439a25fe3a3192a666570d3d907cfb5bff8fdb5df17c92e495bcd4132ce0283" dependencies = [ "bake", "socketry-markdown", From cd7f0925c62ae6582d99caf27b5fbe63571e8a2e Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 5 Oct 2026 11:48:53 +1300 Subject: [PATCH 2/3] Use Bake 0.18 for shared task registration. --- Cargo.lock | 20 ++++++++++---------- bake/Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b265ec7..54ed60b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bake" -version = "0.17.4" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6869a189d61a37055ab2404f5423ff6dc9bc65d81438a83df9408d4aaa6b72c2" +checksum = "cbed34a1e40a3a5893a12c47de45006de3ae53112db64c0919a6e49c1a38d7e0" dependencies = [ "bake-macros", "linkme", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "bake-agent-context" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22477c95d0a8f746a57d5044e8e9fcb45827c08a5dc0b5b353473e236aeeffa9" +checksum = "386910c08614146515e754bf6208aedba73abdb9a2767e8bd5c2d997845c84ef" dependencies = [ "bake", "serde", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "bake-macros" -version = "0.17.4" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c895e7c17c9da57d980358ac5a367ad44912599f167f0e78238fd5c399ecf0" +checksum = "5b801275922e3bbc727ecdbb809f1a82591744cad907c9e915bdfa9b28bf001f" dependencies = [ "proc-macro2", "quote", @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "bake-readme" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8c791e3a2f2492b225c03f1bcf527612bc0d1c07ecfcb69d7eced9400af787" +checksum = "9cbf2eb3710006c27705edd4186c53bc85bcca4fa80ea927a3718660e59833f2" dependencies = [ "bake", "serde_json", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "bake-test-rust" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c27b23073ffd24e48016e88e221982115ad780f563b0d8e95ba8af1a73eaf4" +checksum = "1a9c5b715c668d525d144fedae81c64c1871de6f4d01bb9ce76376ce85240ef8" dependencies = [ "bake", "serde_json", diff --git a/bake/Cargo.toml b/bake/Cargo.toml index eb1cbce..9176ac4 100644 --- a/bake/Cargo.toml +++ b/bake/Cargo.toml @@ -5,5 +5,5 @@ edition.workspace = true publish = false [dependencies] -bake = "0.17.0" +bake = "0.18.0" socketry-project = "0.3" From 1aa6e533ad4ab8e5b443a0f3aa6de496c546695e Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 5 Oct 2026 15:18:01 +1300 Subject: [PATCH 3/3] Require Socketry Project 0.3.4 for release task detection. --- Cargo.lock | 8 ++++---- bake/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54ed60b..5c6a5cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,9 +92,9 @@ dependencies = [ [[package]] name = "bake-cargo" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b29fac66b2f6541b43c2f296910d71666c4286181bc66d16b971e64b6f96ce67" +checksum = "ddad3c2027b3e69bbee137e5c3799755232d85ef4503f7f0c525c1ca1f211369" dependencies = [ "bake", "bake-license", @@ -978,9 +978,9 @@ dependencies = [ [[package]] name = "socketry-project" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c5a54ff8afc1eb90dfff374dad7550205d28956993b32c1bb7ae49776e1d3c" +checksum = "d5f3a4391b29d2fcfe7522e3fdf651aaff9fd3de6c530a90412060b6bd1ad4e4" dependencies = [ "bake", "bake-agent-context", diff --git a/bake/Cargo.toml b/bake/Cargo.toml index 9176ac4..6360540 100644 --- a/bake/Cargo.toml +++ b/bake/Cargo.toml @@ -6,4 +6,4 @@ publish = false [dependencies] bake = "0.18.0" -socketry-project = "0.3" +socketry-project = ">=0.3.4"