From eb41decdb5a978ec760ceffd327ae59c849cbcc6 Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 13:38:18 +0800 Subject: [PATCH 01/17] Add GitHub Actions release CI and make install.sh public-ready - Generate .github/workflows/release.yml via cargo-dist for cross-platform builds on macOS arm64/x86, Linux arm64/x86, and Windows x86 - Remove allow-dirty = ["ci"] from dist-workspace.toml so dist can manage CI - Revert install.sh TEMP pre-launch blocks: replace gh CLI calls with curl for tag lookup, binary download, and source tarball download Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 304 ++++++++++++++++++++++++++++++++++ dist-workspace.toml | 2 - install.sh | 18 +- 3 files changed, 308 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5da9745 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,304 @@ +# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist +# +# Copyright 2022-2024, axodotdev +# SPDX-License-Identifier: MIT or Apache-2.0 +# +# CI that: +# +# * checks for a Git Tag that looks like a release +# * builds artifacts with dist (archives, installers, hashes) +# * uploads those artifacts to temporary workflow zip +# * on success, uploads the artifacts to a GitHub Release +# +# Note that the GitHub Release will be created with a generated +# title/body based on your changelogs. + +name: Release +permissions: + "contents": "write" + +# This task will run whenever you push a git tag that looks like a version +# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. +# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where +# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION +# must be a Cargo-style SemVer Version (must have at least major.minor.patch). +# +# If PACKAGE_NAME is specified, then the announcement will be for that +# package (erroring out if it doesn't have the given version or isn't dist-able). +# +# If PACKAGE_NAME isn't specified, then the announcement will be for all +# (dist-able) packages in the workspace with that version (this mode is +# intended for workspaces with only one dist-able package, or with all dist-able +# packages versioned/released in lockstep). +# +# If you push multiple tags at once, separate instances of this workflow will +# spin up, creating an independent announcement for each one. However, GitHub +# will hard limit this to 3 tags per commit, as it will assume more tags is a +# mistake. +# +# If there's a prerelease-style suffix to the version, then the release(s) +# will be marked as a prerelease. +on: + pull_request: + push: + tags: + - '**[0-9]+.[0-9]+.[0-9]+*' + +jobs: + # Run 'dist plan' (or host) to determine what tasks we need to do + plan: + runs-on: "ubuntu-22.04" + outputs: + val: ${{ steps.plan.outputs.manifest }} + tag: ${{ !github.event.pull_request && github.ref_name || '' }} + tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} + publishing: ${{ !github.event.pull_request }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive + - name: Install dist + # we specify bash to get pipefail; it guards against the `curl` command + # failing. otherwise `sh` won't catch that `curl` returned non-0 + shell: bash + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh" + - name: Cache dist + uses: actions/upload-artifact@v6 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/dist + # sure would be cool if github gave us proper conditionals... + # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible + # functionality based on whether this is a pull_request, and whether it's from a fork. + # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* + # but also really annoying to build CI around when it needs secrets to work right.) + - id: plan + run: | + dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json + echo "dist ran successfully" + cat plan-dist-manifest.json + echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" + - name: "Upload dist-manifest.json" + uses: actions/upload-artifact@v6 + with: + name: artifacts-plan-dist-manifest + path: plan-dist-manifest.json + + # Build and packages all the platform-specific things + build-local-artifacts: + name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) + # Let the initial task tell us to not run (currently very blunt) + needs: + - plan + if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} + strategy: + fail-fast: false + # Target platforms/runners are computed by dist in create-release. + # Each member of the matrix has the following arguments: + # + # - runner: the github runner + # - dist-args: cli flags to pass to dist + # - install-dist: expression to run to install dist on the runner + # + # Typically there will be: + # - 1 "global" task that builds universal installers + # - N "local" tasks that build each platform's binaries and platform-specific installers + matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} + runs-on: ${{ matrix.runner }} + container: ${{ matrix.container && matrix.container.image || null }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json + permissions: + "attestations": "write" + "contents": "read" + "id-token": "write" + steps: + - name: enable windows longpaths + run: | + git config --global core.longpaths true + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive + - name: Install Rust non-interactively if not already installed + if: ${{ matrix.container }} + run: | + if ! command -v cargo > /dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: Install dist + run: ${{ matrix.install_dist.run }} + # Get the dist-manifest + - name: Fetch local artifacts + uses: actions/download-artifact@v7 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - name: Install dependencies + run: | + ${{ matrix.packages_install }} + - name: Build artifacts + run: | + # Actually do builds and make zips and whatnot + dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json + echo "dist ran successfully" + - name: Attest + uses: actions/attest-build-provenance@v3 + with: + subject-path: "target/distrib/*${{ join(matrix.targets, ', ') }}*" + - id: cargo-dist + name: Post-build + # We force bash here just because github makes it really hard to get values up + # to "real" actions without writing to env-vars, and writing to env-vars has + # inconsistent syntax between shell and powershell. + shell: bash + run: | + # Parse out what we just built and upload it to scratch storage + echo "paths<> "$GITHUB_OUTPUT" + dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + cp dist-manifest.json "$BUILD_MANIFEST_NAME" + - name: "Upload artifacts" + uses: actions/upload-artifact@v6 + with: + name: artifacts-build-local-${{ join(matrix.targets, '_') }} + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} + + # Build and package all the platform-agnostic(ish) things + build-global-artifacts: + needs: + - plan + - build-local-artifacts + runs-on: "ubuntu-22.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive + - name: Install cached dist + uses: actions/download-artifact@v7 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - run: chmod +x ~/.cargo/bin/dist + # Get all the local artifacts for the global tasks to use (for e.g. checksums) + - name: Fetch local artifacts + uses: actions/download-artifact@v7 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - id: cargo-dist + shell: bash + run: | + dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json + echo "dist ran successfully" + + # Parse out what we just built and upload it to scratch storage + echo "paths<> "$GITHUB_OUTPUT" + jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + cp dist-manifest.json "$BUILD_MANIFEST_NAME" + - name: "Upload artifacts" + uses: actions/upload-artifact@v6 + with: + name: artifacts-build-global + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} + # Determines if we should publish/announce + host: + needs: + - plan + - build-local-artifacts + - build-global-artifacts + # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + runs-on: "ubuntu-22.04" + outputs: + val: ${{ steps.host.outputs.manifest }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive + - name: Install cached dist + uses: actions/download-artifact@v7 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - run: chmod +x ~/.cargo/bin/dist + # Fetch artifacts from scratch-storage + - name: Fetch artifacts + uses: actions/download-artifact@v7 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - id: host + shell: bash + run: | + dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json + echo "artifacts uploaded and released successfully" + cat dist-manifest.json + echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" + - name: "Upload dist-manifest.json" + uses: actions/upload-artifact@v6 + with: + # Overwrite the previous copy + name: artifacts-dist-manifest + path: dist-manifest.json + # Create a GitHub Release while uploading all files to it + - name: "Download GitHub Artifacts" + uses: actions/download-artifact@v7 + with: + pattern: artifacts-* + path: artifacts + merge-multiple: true + - name: Cleanup + run: | + # Remove the granular manifests + rm -f artifacts/*-dist-manifest.json + - name: Create GitHub Release + env: + PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" + ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" + ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" + RELEASE_COMMIT: "${{ github.sha }}" + run: | + # Write and read notes from a file to avoid quoting breaking things + echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt + + gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + + announce: + needs: + - plan + - host + # use "always() && ..." to allow us to wait for all publish jobs while + # still allowing individual publish jobs to skip themselves (for prereleases). + # "host" however must run to completion, no skipping allowed! + if: ${{ always() && needs.host.result == 'success' }} + runs-on: "ubuntu-22.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive diff --git a/dist-workspace.toml b/dist-workspace.toml index 515dd85..0483f30 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -17,8 +17,6 @@ pr-run-mode = "plan" unix-archive = ".tar.gz" # Whether to enable GitHub Attestations github-attestations = true -# Skip checking whether the specified configuration files are up to date -allow-dirty = ["ci"] # Path that installers should place binaries in install-path = "CARGO_HOME" # Whether to install an updater program diff --git a/install.sh b/install.sh index 2d0eecd..9426143 100755 --- a/install.sh +++ b/install.sh @@ -95,13 +95,7 @@ install_skills() { main() { target=$(get_target) - # TEMP: pre-launch, repo is private — use `gh` for auth. - # Revert this block to the curl version (see git history) once public. - if ! command -v gh >/dev/null 2>&1; then - echo "Error: 'gh' CLI required for pre-launch install (repo is private)" >&2 - exit 1 - fi - tag=$(gh release view --repo "$REPO" --json tagName --jq .tagName) + tag=$(curl -sSfL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/') if [ -z "$tag" ]; then echo "Error: could not determine latest release" >&2 exit 1 @@ -116,10 +110,8 @@ main() { tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' EXIT - # TEMP: pre-launch, use `gh release download` instead of curl. - gh release download "$tag" --repo "$REPO" \ - --pattern "$tarball" --pattern "$checksums" \ - --dir "$tmpdir" >/dev/null + curl -sSfL "https://github.com/${REPO}/releases/download/${tag}/${tarball}" -o "$tmpdir/$tarball" + curl -sSfL "https://github.com/${REPO}/releases/download/${tag}/${checksums}" -o "$tmpdir/$checksums" expected_hash=$(grep "$tarball" "$tmpdir/$checksums" | awk '{print $1}') if [ -z "$expected_hash" ]; then @@ -173,9 +165,7 @@ main() { # Install skills (non-fatal if it fails) src_tarball="$tmpdir/source.tar.gz" - # TEMP: use gh api for private repo. When public, replace with: - # curl -sSfL "https://github.com/${REPO}/archive/refs/tags/${tag}.tar.gz" -o "$src_tarball" - if gh api "repos/${REPO}/tarball/${tag}" > "$src_tarball" 2>/dev/null; then + if curl -sSfL "https://github.com/${REPO}/archive/refs/tags/${tag}.tar.gz" -o "$src_tarball" 2>/dev/null; then install_skills "$src_tarball" || echo "Warning: skill installation failed (skipping)" else echo "" From bfe3e775b227de57042780381c460e548849a6ae Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 14:18:57 +0800 Subject: [PATCH 02/17] Add CI workflow to run unit tests on push and PRs Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..11d1645 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + - run: cargo test From b0eb2e9f37155d00270616a9310802192659c8ef Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 15:29:00 +0800 Subject: [PATCH 03/17] Fix CI: install libdbus-1-dev for keyring crate on Linux Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11d1645..cd2dfae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - run: sudo apt-get install -y libdbus-1-dev - uses: actions/cache@v4 with: path: | From f61410f0c55b222af2098dad974e0ecbb1c72f81 Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:01:04 +0800 Subject: [PATCH 04/17] Add macOS code signing via cargo-dist Sets macos-sign = true in dist-workspace.toml; regenerates release.yml to pass CODESIGN_CERTIFICATE, CODESIGN_CERTIFICATE_PASSWORD, and CODESIGN_IDENTITY secrets to macOS build runners. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 3 +++ dist-workspace.toml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5da9745..e05b0d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,6 +112,9 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json + CODESIGN_CERTIFICATE: ${{ secrets.CODESIGN_CERTIFICATE }} + CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }} + CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }} permissions: "attestations": "write" "contents": "read" diff --git a/dist-workspace.toml b/dist-workspace.toml index 0483f30..0520087 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -21,3 +21,5 @@ github-attestations = true install-path = "CARGO_HOME" # Whether to install an updater program install-updater = false +# macOS code signing (uses APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_TEAM_ID secrets) +macos-sign = true From 6e7ff6bcd6742c066364ca8e85022d9145b0ba6d Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:07:18 +0800 Subject: [PATCH 05/17] Map CODESIGN_* to APPLE_* secret names in release workflow Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e05b0d9..1596639 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,9 +112,9 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json - CODESIGN_CERTIFICATE: ${{ secrets.CODESIGN_CERTIFICATE }} - CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }} - CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }} + CODESIGN_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} permissions: "attestations": "write" "contents": "read" From f054b7f026e9d53ff645d19d09252ddd2daf667f Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:09:52 +0800 Subject: [PATCH 06/17] Temporarily set pr-run-mode to upload for build test Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 4 ++++ dist-workspace.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1596639..6154c6a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -134,6 +134,10 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi + - uses: swatinem/rust-cache@v2 + with: + key: ${{ join(matrix.targets, '-') }} + cache-provider: ${{ matrix.cache_provider }} - name: Install dist run: ${{ matrix.install_dist.run }} # Get the dist-manifest diff --git a/dist-workspace.toml b/dist-workspace.toml index 0520087..c9de5c2 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -12,7 +12,7 @@ installers = ["shell", "powershell"] # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Which actions to run on pull requests -pr-run-mode = "plan" +pr-run-mode = "upload" # The archive format to use for non-windows builds (defaults .tar.xz) unix-archive = ".tar.gz" # Whether to enable GitHub Attestations From 36e324d286083b624dae88c18c50bd54bb1c9813 Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:12:21 +0800 Subject: [PATCH 07/17] Add allow-dirty = ["ci"] to skip dist staleness check on custom secret names Co-Authored-By: Claude Sonnet 4.6 (1M context) --- dist-workspace.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dist-workspace.toml b/dist-workspace.toml index c9de5c2..ec4566d 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -23,3 +23,5 @@ install-path = "CARGO_HOME" install-updater = false # macOS code signing (uses APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_TEAM_ID secrets) macos-sign = true +# CI file has custom secret name mappings, opt out of dist's staleness check +allow-dirty = ["ci"] From 3d6e89e700715cd4d4d8f95e2fa0ac2f283e696f Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:27:26 +0800 Subject: [PATCH 08/17] Declare libdbus-1-dev system dependency for Linux builds Co-Authored-By: Claude Sonnet 4.6 (1M context) --- dist-workspace.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dist-workspace.toml b/dist-workspace.toml index ec4566d..0cda2a9 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -25,3 +25,6 @@ install-updater = false macos-sign = true # CI file has custom secret name mappings, opt out of dist's staleness check allow-dirty = ["ci"] + +[dist.dependencies] +apt = ["libdbus-1-dev"] From cd9ff42737586f62bd9ae9723940031bd1fa4d3d Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:34:44 +0800 Subject: [PATCH 09/17] Re-trigger build after secret update From 2c7888e30c119e6eb2b31455042f7ac1a5310f08 Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Thu, 14 May 2026 18:35:47 +0800 Subject: [PATCH 10/17] Fix dist.dependencies syntax for libdbus-1-dev Co-Authored-By: Claude Sonnet 4.6 (1M context) --- dist-workspace.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist-workspace.toml b/dist-workspace.toml index 0cda2a9..38d6f36 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -26,5 +26,5 @@ macos-sign = true # CI file has custom secret name mappings, opt out of dist's staleness check allow-dirty = ["ci"] -[dist.dependencies] -apt = ["libdbus-1-dev"] +[dist.dependencies.apt] +libdbus-1-dev = "*" From 72d76d0331882aa8cc9ddb0b60112275a1c6fc7e Mon Sep 17 00:00:00 2001 From: Shing Yuen Date: Fri, 15 May 2026 09:46:55 +0800 Subject: [PATCH 11/17] Pin aarch64-apple-darwin to macos-15 runner for PKCS12 compatibility macOS 14 rejects OpenSSL 3.x p12 format; macos-15 handles it correctly. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- dist-workspace.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dist-workspace.toml b/dist-workspace.toml index 38d6f36..d1408f5 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -28,3 +28,6 @@ allow-dirty = ["ci"] [dist.dependencies.apt] libdbus-1-dev = "*" + +[dist.github-custom-runners] +aarch64-apple-darwin = "macos-15" From 9c1558982c50910a7a865c4921f18bb06d80083c Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 10:48:26 +0800 Subject: [PATCH 12/17] Add macOS notarization, revert pr-run-mode to plan, bump to 1.0.0-alpha.1 - Add APPLE_NOTARIZE_* secrets and notarize step to release workflow; step runs only on macOS targets and only on tag releases - Revert pr-run-mode from upload back to plan - Bump version to 1.0.0-alpha.1 for pre-release testing Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 15 +++++++++++++++ Cargo.toml | 2 +- dist-workspace.toml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6154c6a..eeb74cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -115,6 +115,9 @@ jobs: CODESIGN_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} + APPLE_NOTARIZE_ID: ${{ secrets.APPLE_NOTARIZE_ID }} + APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} + APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} permissions: "attestations": "write" "contents": "read" @@ -155,6 +158,18 @@ jobs: # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" + - name: Notarize macOS binary + if: ${{ contains(join(matrix.targets, ','), 'apple-darwin') && needs.plan.outputs.publishing == 'true' }} + shell: bash + run: | + binary=$(find target/dist -name "bitmex" -not -path "*/build/*" -not -path "*/deps/*" | head -1) + zip_path="${binary}.zip" + ditto -c -k --keepParent "$binary" "$zip_path" + xcrun notarytool submit "$zip_path" \ + --apple-id "$APPLE_NOTARIZE_ID" \ + --password "$APPLE_NOTARIZE_PASSWORD" \ + --team-id "$APPLE_NOTARIZE_TEAM_ID" \ + --wait - name: Attest uses: actions/attest-build-provenance@v3 with: diff --git a/Cargo.toml b/Cargo.toml index a767b18..26adce1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitmex-cli" -version = "1.0.0" +version = "1.0.0-alpha.1" edition = "2024" description = "BitMEX CLI — trade, query, and manage your BitMEX account from the terminal" license = "MIT" diff --git a/dist-workspace.toml b/dist-workspace.toml index d1408f5..1d5af3a 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -12,7 +12,7 @@ installers = ["shell", "powershell"] # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Which actions to run on pull requests -pr-run-mode = "upload" +pr-run-mode = "plan" # The archive format to use for non-windows builds (defaults .tar.xz) unix-archive = ".tar.gz" # Whether to enable GitHub Attestations From ccbba1b8eedf1f53a28934da38f09275258c69fb Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 10:53:34 +0800 Subject: [PATCH 13/17] Reuse APPLE_TEAM_ID for notarization team ID Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eeb74cc..ee6b701 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,7 +117,7 @@ jobs: CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} APPLE_NOTARIZE_ID: ${{ secrets.APPLE_NOTARIZE_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} - APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} + APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} permissions: "attestations": "write" "contents": "read" From b1d31705a41dfd4806236ddf0af70590f934090b Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 11:30:56 +0800 Subject: [PATCH 14/17] Fix notarize step: extract binary from dist tarball Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee6b701..43c6567 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -162,10 +162,13 @@ jobs: if: ${{ contains(join(matrix.targets, ','), 'apple-darwin') && needs.plan.outputs.publishing == 'true' }} shell: bash run: | - binary=$(find target/dist -name "bitmex" -not -path "*/build/*" -not -path "*/deps/*" | head -1) - zip_path="${binary}.zip" - ditto -c -k --keepParent "$binary" "$zip_path" - xcrun notarytool submit "$zip_path" \ + target="${{ join(matrix.targets, '') }}" + tarball="target/distrib/bitmex-cli-${target}.tar.gz" + tmpdir=$(mktemp -d) + tar xzf "$tarball" -C "$tmpdir" + binary="$tmpdir/bitmex-cli-${target}/bitmex" + ditto -c -k --keepParent "$binary" "$tmpdir/notarize.zip" + xcrun notarytool submit "$tmpdir/notarize.zip" \ --apple-id "$APPLE_NOTARIZE_ID" \ --password "$APPLE_NOTARIZE_PASSWORD" \ --team-id "$APPLE_NOTARIZE_TEAM_ID" \ From 457e94c4ea0f3aa903afbc36d1ffaec888cb8542 Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 11:48:39 +0800 Subject: [PATCH 15/17] Enable hardened runtime for notarization (CODESIGN_OPTIONS=runtime) Apple rejects notarization submissions without hardened runtime enabled. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43c6567..821d45b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -115,6 +115,7 @@ jobs: CODESIGN_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} + CODESIGN_OPTIONS: runtime APPLE_NOTARIZE_ID: ${{ secrets.APPLE_NOTARIZE_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} From 5bd5ccd0ff2fb930c2ed08c436dda58cc5f23c93 Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 11:51:46 +0800 Subject: [PATCH 16/17] Bump version to 1.0.0-alpha.2 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 26adce1..2145a30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitmex-cli" -version = "1.0.0-alpha.1" +version = "1.0.0-alpha.2" edition = "2024" description = "BitMEX CLI — trade, query, and manage your BitMEX account from the terminal" license = "MIT" From 8178aebc896fa11e06cb2c227e0f017b1d694b5e Mon Sep 17 00:00:00 2001 From: Shing Yuen <94164655+byshing@users.noreply.github.com> Date: Fri, 15 May 2026 12:19:49 +0800 Subject: [PATCH 17/17] Revert version to 1.0.0 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2145a30..a767b18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitmex-cli" -version = "1.0.0-alpha.2" +version = "1.0.0" edition = "2024" description = "BitMEX CLI — trade, query, and manage your BitMEX account from the terminal" license = "MIT"