build: Bump version to 0.9.0 #14
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Builds release artifacts for Linux and macOS: | |
| # - the GTK desktop app (`commedit`), one tarball per target, and | |
| # - `commedit-plugin.zip`, a Claude Code plugin bundling the `commedit-mcp` | |
| # server binary for every target (see plugin/README.md). | |
| # | |
| # The GTK app links dynamically against system GTK4 + GtkSourceView5, so those | |
| # tarballs are NOT self-contained. The end user must have a compatible runtime | |
| # (and `git` on PATH) installed — see the "Installing a binary release" section | |
| # of README.md: | |
| # - Linux: apt install git libgtk-4-1 libgtksourceview-5-0 libspelling-1-2 (or distro equivalent) | |
| # - macOS: brew install git gtk4 gtksourceview5 libspelling | |
| # The MCP server (and thus the plugin) only needs `git` on PATH. | |
| # | |
| # Push a tag like `v0.1.0` to cut a GitHub Release; or run manually via the | |
| # Actions tab to just produce downloadable build artifacts. | |
| # | |
| # The build/plugin jobs only upload workflow artifacts; a final `release` job | |
| # gathers them and publishes the release in one shot. It does so the way | |
| # GitHub's *immutable releases* require: create a DRAFT, attach every asset to | |
| # it, then flip it to published. A published immutable release is locked, so | |
| # the old approach (each matrix job appending its own asset to an | |
| # already-published release) cannot work — only the draft is still writable. | |
| name: release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: build (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| asset: commedit-linux-x86_64.tar.gz | |
| mcp_asset: commedit-mcp-linux-x86_64 | |
| - os: ubuntu-24.04-arm | |
| asset: commedit-linux-aarch64.tar.gz | |
| mcp_asset: commedit-mcp-linux-aarch64 | |
| - os: macos-latest | |
| asset: commedit-macos-aarch64.tar.gz | |
| mcp_asset: commedit-mcp-macos-aarch64 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # --- Linux: GTK from apt, Rust from rustup -------------------------- | |
| - name: Install GTK (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgtk-4-dev libgtksourceview-5-dev libspelling-1-dev | |
| # --- macOS: GTK from Homebrew, Rust from rustup --------------------- | |
| # The runner image pre-taps third-party repos (aws/tap, azure/bicep) we | |
| # never use. Opt into Homebrew's upcoming tap-trust default so it simply | |
| # ignores untrusted taps instead of printing the transition warning; | |
| # official taps are always trusted, so our formulae install unaffected. | |
| - name: Install GTK (macOS) | |
| if: runner.os == 'macOS' | |
| env: | |
| HOMEBREW_REQUIRE_TAP_TRUST: 1 | |
| run: brew install gtk4 gtksourceview5 libspelling pkg-config | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| # The MCP server has no GTK dependency, so building it alongside the app | |
| # reuses the same toolchain and compile cache for free. | |
| - name: Build | |
| run: cargo build --release --locked -p commedit-gtk -p commedit-mcp | |
| - name: Package app | |
| run: tar -C target/release -czf "${{ matrix.asset }}" commedit | |
| # Stage the MCP binary under its target-specific name, the name the | |
| # plugin launcher (plugin/bin/launch.sh) resolves at runtime. | |
| - name: Stage MCP binary | |
| run: cp target/release/commedit-mcp "${{ matrix.mcp_asset }}" | |
| # --- Publish -------------------------------------------------------- | |
| - name: Upload app artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.asset }} | |
| path: ${{ matrix.asset }} | |
| # Consumed by the `plugin` job, not attached to the release on its own. | |
| - name: Upload MCP binary artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.mcp_asset }} | |
| path: ${{ matrix.mcp_asset }} | |
| # Bundle the per-target MCP binaries into a single Claude Code plugin zip. | |
| plugin: | |
| name: package plugin | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # All three commedit-mcp-* binaries land directly in plugin/bin/, next to | |
| # the launcher that dispatches between them. | |
| - name: Download MCP binaries | |
| uses: actions/download-artifact@v7 | |
| with: | |
| pattern: commedit-mcp-* | |
| path: plugin/bin | |
| merge-multiple: true | |
| # On a tagged release, pin the plugin version to the tag (v0.3.0 -> 0.3.0) | |
| # so org-distributed installs see each release as a new version. | |
| - name: Set plugin version from tag | |
| if: startsWith(github.ref, 'refs/tags/') | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| tmp=$(mktemp) | |
| jq --arg v "$version" '.version = $v' \ | |
| plugin/.claude-plugin/plugin.json > "$tmp" | |
| mv "$tmp" plugin/.claude-plugin/plugin.json | |
| # zip stores the +x bits we set here (the launcher self-heals the binary | |
| # bit too, so the plugin still works if the channel drops permissions). | |
| - name: Assemble plugin zip | |
| run: | | |
| chmod +x plugin/bin/launch.sh plugin/bin/commedit-mcp-* | |
| (cd plugin && zip -r ../commedit-plugin.zip \ | |
| .claude-plugin .mcp.json bin skills agents README.md \ | |
| -x 'bin/.gitignore') | |
| unzip -l commedit-plugin.zip | |
| - name: Upload plugin artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: commedit-plugin.zip | |
| path: commedit-plugin.zip | |
| # Publish the release once, with every asset, via GitHub's immutable-release | |
| # flow: a draft is the only writable state, so we attach all assets to a | |
| # draft and then flip it to published (which locks it). Only on a tag. | |
| release: | |
| name: publish release | |
| needs: [build, plugin] | |
| if: startsWith(github.ref, 'refs/tags/') | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Pull every uploaded artifact flat into dist/. The bare MCP binaries | |
| # land here too but are filtered out below — only tarballs + the plugin | |
| # zip get attached, matching what the release carried before. | |
| - name: Download release assets | |
| uses: actions/download-artifact@v7 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Create draft release with all assets | |
| id: draft | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| draft: true | |
| files: | | |
| dist/*.tar.gz | |
| dist/commedit-plugin.zip | |
| fail_on_unmatched_files: true | |
| # Flipping draft -> published is the point the release becomes immutable, | |
| # with all assets already in place. This job has no checkout, so | |
| # `gh release edit` would shell out to git to discover the repo and die | |
| # with "not a git repository"; patch the release by its id (from the step | |
| # above) via `gh api`, whose fully-qualified path needs neither a git | |
| # checkout nor a tag lookup. | |
| - name: Publish release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh api --method PATCH \ | |
| "/repos/${{ github.repository }}/releases/${{ steps.draft.outputs.id }}" \ | |
| -F draft=false |