diff --git a/.github/workflows/homebrew-cask-codexel.yml b/.github/workflows/homebrew-cask-codexel.yml
new file mode 100644
index 00000000000..eb26bfc2456
--- /dev/null
+++ b/.github/workflows/homebrew-cask-codexel.yml
@@ -0,0 +1,177 @@
+name: homebrew-cask-codexel
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Existing Codexel tag (e.g. codexel-v0.1.3)"
+ required: true
+ type: string
+
+jobs:
+ build-macos:
+ name: Build macOS assets - ${{ matrix.target }}
+ runs-on: ${{ matrix.runner }}
+ timeout-minutes: 45
+ defaults:
+ run:
+ working-directory: codex-rs
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - runner: macos-14
+ target: aarch64-apple-darwin
+ - runner: macos-13
+ target: x86_64-apple-darwin
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ ref: ${{ inputs.tag }}
+
+ - uses: dtolnay/rust-toolchain@1.90
+ with:
+ targets: ${{ matrix.target }}
+
+ - uses: actions/cache@v5
+ with:
+ path: |
+ ~/.cargo/bin/
+ ~/.cargo/registry/index/
+ ~/.cargo/registry/cache/
+ ~/.cargo/git/db/
+ ${{ github.workspace }}/codex-rs/target/
+ key: cargo-${{ matrix.runner }}-${{ matrix.target }}-release-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Cargo build
+ shell: bash
+ run: cargo build --target ${{ matrix.target }} --release --bin codexel
+
+ - name: Package tarball
+ shell: bash
+ run: |
+ set -euo pipefail
+ out_dir="${GITHUB_WORKSPACE}/dist"
+ mkdir -p "$out_dir"
+ bin_path="target/${{ matrix.target }}/release/codexel"
+ chmod +x "$bin_path"
+ tar -C "$(dirname "$bin_path")" -czf "$out_dir/codexel-${{ matrix.target }}.tar.gz" codexel
+
+ - uses: actions/upload-artifact@v6
+ with:
+ name: codexel-${{ matrix.target }}-tarball
+ path: dist/codexel-${{ matrix.target }}.tar.gz
+ if-no-files-found: error
+
+ publish-homebrew:
+ name: Upload assets + update tap cask
+ needs: build-macos
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ env:
+ TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
+ steps:
+ - name: Validate tag
+ shell: bash
+ run: |
+ set -euo pipefail
+ [[ "${{ inputs.tag }}" =~ ^codexel-v[0-9]+\.[0-9]+\.[0-9]+(-((alpha|beta)\.[0-9]+))?$ ]] \
+ || { echo "Tag '${{ inputs.tag }}' doesn't match expected format"; exit 1; }
+
+ - name: Download tarballs
+ uses: actions/download-artifact@v7
+ with:
+ path: dist
+
+ - name: Move tarballs into dist/
+ shell: bash
+ run: |
+ set -euo pipefail
+ shopt -s nullglob
+ for tarball in dist/*/codexel-*-apple-darwin.tar.gz; do
+ mv "$tarball" dist/
+ done
+ ls -la dist
+
+ - name: Upload macOS assets to GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ gh release upload "${{ inputs.tag }}" dist/codexel-*-apple-darwin.tar.gz \
+ --repo Ixe1/codexel \
+ --clobber
+
+ - name: Compute sha256
+ id: shas
+ shell: bash
+ run: |
+ set -euo pipefail
+ sha_arm=$(sha256sum dist/codexel-aarch64-apple-darwin.tar.gz | cut -d' ' -f1)
+ sha_intel=$(sha256sum dist/codexel-x86_64-apple-darwin.tar.gz | cut -d' ' -f1)
+ echo "arm=$sha_arm" >> "$GITHUB_OUTPUT"
+ echo "intel=$sha_intel" >> "$GITHUB_OUTPUT"
+
+ - name: Verify Homebrew tap token configured
+ shell: bash
+ run: |
+ set -euo pipefail
+ if [[ -z "${TAP_TOKEN}" ]]; then
+ echo "Missing HOMEBREW_TAP_GITHUB_TOKEN secret; cannot update Ixe1/homebrew-tap." >&2
+ exit 1
+ fi
+
+ - name: Checkout Homebrew tap
+ if: ${{ env.TAP_TOKEN != '' }}
+ uses: actions/checkout@v6
+ with:
+ repository: Ixe1/homebrew-tap
+ token: ${{ env.TAP_TOKEN }}
+ path: homebrew-tap
+
+ - name: Update cask
+ if: ${{ env.TAP_TOKEN != '' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ version="${{ inputs.tag }}"
+ version="${version#codexel-v}"
+ sha_arm="${{ steps.shas.outputs.arm }}"
+ sha_intel="${{ steps.shas.outputs.intel }}"
+
+ mkdir -p homebrew-tap/Casks
+ cat > homebrew-tap/Casks/codexel.rb <&2
exit 1
@@ -302,7 +331,8 @@ jobs:
shell: bash
run: |
set -euo pipefail
- version="${GITHUB_REF_NAME#codexel-v}"
+ version="${{ inputs.tag }}"
+ version="${version#codexel-v}"
prerelease="false"
if [[ "${version}" == *-* ]]; then
prerelease="true"
@@ -314,18 +344,63 @@ jobs:
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.release_meta.outputs.version }}
- tag_name: ${{ github.ref_name }}
+ tag_name: ${{ inputs.tag }}
files: dist/**
prerelease: ${{ steps.release_meta.outputs.prerelease }}
generate_release_notes: true
+ - name: Checkout Homebrew tap
+ if: ${{ inputs.update_homebrew && env.TAP_TOKEN != '' }}
+ uses: actions/checkout@v6
+ with:
+ repository: ${{ env.HOMEBREW_TAP_REPO }}
+ token: ${{ env.TAP_TOKEN }}
+ path: homebrew-tap
+
+ - name: Update Homebrew cask
+ if: ${{ inputs.update_homebrew && env.TAP_TOKEN != '' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ version="${{ steps.release_meta.outputs.version }}"
+ sha_arm=$(sha256sum "dist/codexel-aarch64-apple-darwin.tar.gz" | cut -d' ' -f1)
+ sha_intel=$(sha256sum "dist/codexel-x86_64-apple-darwin.tar.gz" | cut -d' ' -f1)
+
+ mkdir -p homebrew-tap/Casks
+ cat > homebrew-tap/Casks/codexel.rb <
npm i -g @ixe1/codexel
- brew install --cask codexel
+ brew install --cask Ixe1/tap/codexel
or download from GitHub Releases
@@ -48,7 +48,7 @@ npm install -g @ixe1/codexel
Alternatively, if you use Homebrew:
```shell
-brew install --cask codexel
+brew install --cask Ixe1/tap/codexel
```
Then run `codexel`:
@@ -71,7 +71,7 @@ Each GitHub Release contains many executables, but in practice, you likely want
- x86_64: `codexel-x86_64-unknown-linux-musl.tar.gz`
- arm64: `codexel-aarch64-unknown-linux-musl.tar.gz`
-Each archive contains a single entry with the platform baked into the name (e.g., `codexel-x86_64-unknown-linux-musl`), so you likely want to rename it to `codexel` after extracting it.
+Each archive contains a single `codexel` binary.
diff --git a/codex-cli/scripts/verify-vendor.mjs b/codex-cli/scripts/verify-vendor.mjs
index 78c1d6e8601..05582ea39cc 100644
--- a/codex-cli/scripts/verify-vendor.mjs
+++ b/codex-cli/scripts/verify-vendor.mjs
@@ -10,6 +10,8 @@ const vendorRoot = path.join(packageRoot, "vendor");
const targets = [
"aarch64-unknown-linux-musl",
"x86_64-unknown-linux-musl",
+ "aarch64-apple-darwin",
+ "x86_64-apple-darwin",
"aarch64-pc-windows-msvc",
"x86_64-pc-windows-msvc",
];
diff --git a/codex-rs/README.md b/codex-rs/README.md
index c945b12b62a..44c5118c5c4 100644
--- a/codex-rs/README.md
+++ b/codex-rs/README.md
@@ -11,7 +11,7 @@ npm i -g @ixe1/codexel
codexel
```
-You can also install via Homebrew (`brew install --cask codexel`) or download a platform-specific release directly from [GitHub Releases](../../releases).
+You can also install via Homebrew (`brew install --cask Ixe1/tap/codexel`) or download a platform-specific release directly from [GitHub Releases](../../releases).
## Documentation quickstart
diff --git a/codex-rs/tui/src/updates.rs b/codex-rs/tui/src/updates.rs
index bfd9ae9edbe..b6137654fb8 100644
--- a/codex-rs/tui/src/updates.rs
+++ b/codex-rs/tui/src/updates.rs
@@ -64,7 +64,7 @@ const NPM_LATEST_URL: &str = "https://registry.npmjs.org/@ixe1%2Fcodexel/latest"
const NPM_SOURCE_KEY: &str = "npm:@ixe1/codexel";
// We use the latest version from the cask if installation is via homebrew - homebrew does not immediately pick up the latest release and can lag behind.
const HOMEBREW_CASK_URL: &str =
- "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/c/codexel.rb";
+ "https://raw.githubusercontent.com/Ixe1/homebrew-tap/HEAD/Casks/codexel.rb";
const HOMEBREW_SOURCE_KEY: &str = "brew:codexel";
const LATEST_RELEASE_URL: &str = "https://api.github.com/repos/Ixe1/codexel/releases/latest";
const GITHUB_SOURCE_KEY: &str = "github:Ixe1/codexel";
@@ -221,7 +221,7 @@ fn extract_version_from_cask(cask_contents: &str) -> anyhow::Result {
fn extract_version_from_latest_tag(latest_tag_name: &str) -> anyhow::Result {
latest_tag_name
- .strip_prefix("rust-v")
+ .strip_prefix("codexel-v")
.map(str::to_owned)
.ok_or_else(|| anyhow::anyhow!("Failed to parse latest tag name '{latest_tag_name}'"))
}
@@ -295,7 +295,7 @@ mod tests {
#[test]
fn extracts_version_from_latest_tag() {
assert_eq!(
- extract_version_from_latest_tag("rust-v1.5.0").expect("failed to parse version"),
+ extract_version_from_latest_tag("codexel-v1.5.0").expect("failed to parse version"),
"1.5.0"
);
}
diff --git a/codex-rs/tui2/src/updates.rs b/codex-rs/tui2/src/updates.rs
index bfd9ae9edbe..b6137654fb8 100644
--- a/codex-rs/tui2/src/updates.rs
+++ b/codex-rs/tui2/src/updates.rs
@@ -64,7 +64,7 @@ const NPM_LATEST_URL: &str = "https://registry.npmjs.org/@ixe1%2Fcodexel/latest"
const NPM_SOURCE_KEY: &str = "npm:@ixe1/codexel";
// We use the latest version from the cask if installation is via homebrew - homebrew does not immediately pick up the latest release and can lag behind.
const HOMEBREW_CASK_URL: &str =
- "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/c/codexel.rb";
+ "https://raw.githubusercontent.com/Ixe1/homebrew-tap/HEAD/Casks/codexel.rb";
const HOMEBREW_SOURCE_KEY: &str = "brew:codexel";
const LATEST_RELEASE_URL: &str = "https://api.github.com/repos/Ixe1/codexel/releases/latest";
const GITHUB_SOURCE_KEY: &str = "github:Ixe1/codexel";
@@ -221,7 +221,7 @@ fn extract_version_from_cask(cask_contents: &str) -> anyhow::Result {
fn extract_version_from_latest_tag(latest_tag_name: &str) -> anyhow::Result {
latest_tag_name
- .strip_prefix("rust-v")
+ .strip_prefix("codexel-v")
.map(str::to_owned)
.ok_or_else(|| anyhow::anyhow!("Failed to parse latest tag name '{latest_tag_name}'"))
}
@@ -295,7 +295,7 @@ mod tests {
#[test]
fn extracts_version_from_latest_tag() {
assert_eq!(
- extract_version_from_latest_tag("rust-v1.5.0").expect("failed to parse version"),
+ extract_version_from_latest_tag("codexel-v1.5.0").expect("failed to parse version"),
"1.5.0"
);
}
diff --git a/docs/faq.md b/docs/faq.md
index 6384e0cd3c5..c9d616649bb 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -45,11 +45,12 @@ Follow the quick setup in [Install & build](./install.md) and then jump into [Ge
### `brew upgrade codexel` isn't upgrading me
-If you're running Codexel v0.46.0 or older, `brew upgrade codexel` will not move you to the latest version because we migrated from a Homebrew formula to a cask. To upgrade, uninstall the existing oudated formula and then install the new cask:
+If Homebrew isn't picking up the latest version, ensure you're using the Codexel tap and reinstall the cask:
```bash
-brew uninstall --formula codexel
-brew install --cask codexel
+brew update
+brew uninstall --cask codexel
+brew install --cask Ixe1/tap/codexel
```
After reinstalling, `brew upgrade --cask codexel` will keep future releases up to date.
diff --git a/docs/releasing.md b/docs/releasing.md
index 5387052e9e5..df65eb6a9eb 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -10,6 +10,7 @@ Codexel is published as `@ixe1/codexel` with prebuilt native binaries bundled in
- Create and push a tag:
- Stable: `codexel-vX.Y.Z`
- Pre-release: `codexel-vX.Y.Z-alpha.N` or `codexel-vX.Y.Z-beta.N`
+- In GitHub Actions, run the `npm-publish-codexel` workflow and pass the tag you just pushed.
### What the workflow does
@@ -19,9 +20,19 @@ The `npm-publish-codexel` workflow:
- Assembles `codex-cli/vendor//codex/codexel(.exe)`.
- Packs an npm tarball and runs a smoke test (`codexel --help`).
- Creates a GitHub Release containing the per-target binaries and npm tarball.
-- Publishes `@ixe1/codexel` using npm Trusted Publishing (OIDC).
+- Publishes `@ixe1/codexel` using npm Trusted Publishing (OIDC) when `publish_npm` is enabled.
+- If `HOMEBREW_TAP_GITHUB_TOKEN` is set, updates the `codexel` cask in `Ixe1/homebrew-tap`.
+
+If you only want to publish/update Homebrew artifacts (no npm), run the `homebrew-cask-codexel` workflow manually with an existing `codexel-v*` tag.
### One-time setup
Before the first publish, configure npm Trusted Publishing for `@ixe1/codexel`
to trust this repository and the `npm-publish-codexel` workflow in the npm UI.
+
+If you want Homebrew installs (`brew install --cask Ixe1/tap/codexel`) to track releases:
+
+- Create the tap repository `Ixe1/homebrew-tap`.
+- Add a repository secret `HOMEBREW_TAP_GITHUB_TOKEN` (a GitHub token with permission to push to `Ixe1/homebrew-tap`).
+ - Recommended: a fine-grained Personal Access Token scoped to `Ixe1/homebrew-tap` with `Contents: Read and write`.
+ - Create it in GitHub: `Settings -> Developer settings -> Personal access tokens -> Fine-grained tokens`.