Skip to content

docs: correct the csp --agent list in CLAUDE.md (#79) #46

docs: correct the csp --agent list in CLAUDE.md (#79)

docs: correct the csp --agent list in CLAUDE.md (#79) #46

on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
# release-please bumps package.json but does not know about bun.lock.
# A version-only bump usually leaves bun.lock unchanged, but a release PR
# can also carry dependency updates — re-resolve and commit the lockfile
# into the release PR so the tagged commit always installs cleanly with
# `bun install --frozen-lockfile`. Only commits when bun.lock changes.
- name: Checkout release PR branch
if: ${{ steps.release.outputs.pr }}
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
token: ${{ steps.app-token.outputs.token }}
ref: ${{ fromJson(steps.release.outputs.pr).headBranchName }}
- name: Setup Bun
if: ${{ steps.release.outputs.pr }}
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14
- name: Sync bun.lock
if: ${{ steps.release.outputs.pr }}
run: |
bun install --lockfile-only
if git diff --quiet bun.lock; then
echo "bun.lock already in sync"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add bun.lock
git commit -m "chore: sync bun.lock"
git push origin HEAD:${{ fromJson(steps.release.outputs.pr).headBranchName }}
fi
# release-please bumps the workspace version in Cargo.toml (via the
# x-release-please-version annotation) but does not touch Cargo.lock, so
# every workspace member stays at the previous version and the release
# build's `cargo build --locked` fails. `--workspace` re-locks only the
# local members by their real package names (code-search-please /
# csp-node) — it does not bump external deps. Don't name packages
# explicitly: `-p csp` broke once the lib crate was renamed to
# code-search-please and csp-node was added. Commit it into the release
# PR. Mirrors the bun.lock sync.
- name: Sync Cargo.lock
if: ${{ steps.release.outputs.pr }}
run: |
cargo update --workspace
if git diff --quiet Cargo.lock; then
echo "Cargo.lock already in sync"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.lock
git commit -m "chore: sync Cargo.lock"
git push origin HEAD:${{ fromJson(steps.release.outputs.pr).headBranchName }}
fi
# Build the Rust binaries (6 targets) and upload them to the release that
# release-please just created. Reuses release-rust.yml so the cross-compile
# matrix lives in one place (ADR-0003 cut-over — this replaces the former
# TypeScript `bun build src/cli.ts` job).
build-and-upload:
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
permissions:
contents: write
uses: ./.github/workflows/release-rust.yml
with:
tag: ${{ needs.release-please.outputs.tag_name }}
# Build + publish the napi-rs SDK (@pleaseai/csp-sdk) — the separate in-process
# native-addon channel. Reuses release-sdk.yml so its cross-compile matrix
# lives in one place. id-token: write is granted here (the caller) so the
# reusable workflow's publish job can use npm Trusted Publishing (OIDC).
build-and-publish-sdk:
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
permissions:
contents: read
id-token: write
uses: ./.github/workflows/release-sdk.yml
with:
publish: true
# Generate the per-platform npm packages from the released binaries and
# publish the wrapper + platform packages via npm Trusted Publishing (OIDC).
# No NPM_TOKEN needed — auth is the OIDC id-token, and provenance is generated
# automatically. Each package must have a trusted publisher configured on
# npmjs.com pointing at this repo + workflow (release-please.yml).
publish-npm:
needs: [release-please, build-and-upload]
# always(): publish whatever targets reached the release even if one build
# leg failed. The generator is partial-matrix-tolerant (only present targets
# are pinned in optionalDependencies) and fails loudly if nothing built.
if: ${{ always() && needs.release-please.outputs.release_created }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
# This job only reads + publishes to npm; it never pushes to git, so
# don't leave the checkout token persisted in .git/config.
persist-credentials: false
# Trusted Publishing requires npm >= 11.5.1; ubuntu-latest ships npm 10.x
# with Node 22, so upgrade the CLI before publishing. The global prefix
# (/usr/local) is root-owned, so installing a new global npm needs sudo —
# without it npm fails with EACCES on /usr/local/share/man.
- name: Upgrade npm for Trusted Publishing
run: sudo npm install -g npm@latest
- name: Download released binaries
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
mkdir -p assets
gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
--pattern 'csp-*' --dir assets
ls -lh assets
- name: Generate platform packages
env:
RELEASE_TAG: ${{ needs.release-please.outputs.tag_name }}
run: node npm/scripts/generate-platform-packages.mjs "${RELEASE_TAG#v}" assets
# Trusted Publishing supplies auth via OIDC and generates provenance
# automatically, so no token or --provenance flag is needed.
- name: Publish to npm
run: |
set -e
# Platform packages first, then the wrapper last (its
# optionalDependencies pin the platform packages by version).
for dir in npm/dist/@pleaseai__*; do
echo "publishing $dir"
npm publish "$dir" --access public
done
npm publish npm/dist/csp --access public
update-homebrew-formula:
needs: [release-please, build-and-upload]
# always(): the formula only needs the 4 darwin/linux-gnu assets, so a musl
# build failure shouldn't block it. Fails on its own if a core asset is missing.
if: ${{ always() && needs.release-please.outputs.release_created }}
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
repositories: |
code-search
homebrew-tap
- name: Checkout homebrew-tap repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
repository: pleaseai/homebrew-tap
token: ${{ steps.app-token.outputs.token }}
path: homebrew-tap
- name: Configure git
run: |
cd homebrew-tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Update Formula
run: |
VERSION=${{ needs.release-please.outputs.tag_name }}
VERSION_NO_V=${VERSION#v}
# Download checksums. -f makes curl fail (non-zero) on a 404 instead of
# writing an HTML error page into the .sha256 file — so when a core
# target is missing under always(), this job hard-fails rather than
# baking a corrupt checksum into the formula.
curl -fLsS -o darwin-x64.sha256 \
"https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-x64.sha256"
curl -fLsS -o darwin-arm64.sha256 \
"https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-arm64.sha256"
curl -fLsS -o linux-x64.sha256 \
"https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-linux-x64.sha256"
curl -fLsS -o linux-arm64.sha256 \
"https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-linux-arm64.sha256"
# Extract checksums
DARWIN_X64_SHA256=$(cat darwin-x64.sha256 | awk '{print $1}')
DARWIN_ARM64_SHA256=$(cat darwin-arm64.sha256 | awk '{print $1}')
LINUX_X64_SHA256=$(cat linux-x64.sha256 | awk '{print $1}')
LINUX_ARM64_SHA256=$(cat linux-arm64.sha256 | awk '{print $1}')
# Create or update Formula
cd homebrew-tap
cat > csp.rb << 'FORMULA_EOF'
class Csp < Formula
desc "Fast and accurate hybrid code search for agents"
homepage "https://github.com/pleaseai/code-search"
version "VERSION_PLACEHOLDER"
license "MIT"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/pleaseai/code-search/releases/download/vVERSION_PLACEHOLDER/csp-darwin-arm64"
sha256 "SHA256_DARWIN_ARM64_PLACEHOLDER"
else
url "https://github.com/pleaseai/code-search/releases/download/vVERSION_PLACEHOLDER/csp-darwin-x64"
sha256 "SHA256_DARWIN_X64_PLACEHOLDER"
end
end
on_linux do
if Hardware::CPU.arm?
url "https://github.com/pleaseai/code-search/releases/download/vVERSION_PLACEHOLDER/csp-linux-arm64"
sha256 "SHA256_LINUX_ARM64_PLACEHOLDER"
else
url "https://github.com/pleaseai/code-search/releases/download/vVERSION_PLACEHOLDER/csp-linux-x64"
sha256 "SHA256_LINUX_X64_PLACEHOLDER"
end
end
def install
if OS.mac?
if Hardware::CPU.arm?
bin.install "csp-darwin-arm64" => "csp"
else
bin.install "csp-darwin-x64" => "csp"
end
else
if Hardware::CPU.arm?
bin.install "csp-linux-arm64" => "csp"
else
bin.install "csp-linux-x64" => "csp"
end
end
end
test do
assert_match version.to_s, shell_output("#{bin}/csp --version")
end
end
FORMULA_EOF
# Replace placeholders
sed -i "s/VERSION_PLACEHOLDER/${VERSION_NO_V}/g" csp.rb
sed -i "s/SHA256_DARWIN_X64_PLACEHOLDER/${DARWIN_X64_SHA256}/" csp.rb
sed -i "s/SHA256_DARWIN_ARM64_PLACEHOLDER/${DARWIN_ARM64_SHA256}/" csp.rb
sed -i "s/SHA256_LINUX_X64_PLACEHOLDER/${LINUX_X64_SHA256}/" csp.rb
sed -i "s/SHA256_LINUX_ARM64_PLACEHOLDER/${LINUX_ARM64_SHA256}/" csp.rb
# Stage first, then check the *staged* diff. `git diff --quiet csp.rb`
# compares the working tree against the index and ignores untracked
# files, so on the very first run (csp.rb absent from the tap) it
# reported "no changes" and skipped the commit — which is why the
# formula was never published. `git diff --cached` sees the newly
# added file and still no-ops on an unchanged re-run.
git add csp.rb
if git diff --cached --quiet; then
echo "No changes to Formula"
exit 0
fi
# Commit and push
git commit -m "chore: update csp to ${VERSION}"
git push origin main
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
# Publish the `code-search-please` library crate to crates.io via Trusted
# Publishing (OIDC) — no CARGO_REGISTRY_TOKEN. Gated on the CRATES_IO_PUBLISH
# repo variable so it stays dormant until the one-time bootstrap is done:
# (1) claim the name with a manual `cargo publish` (crates.io TP can't be
# configured on a crate that doesn't exist yet), (2) add a GitHub Actions
# trusted publisher on crates.io (repo pleaseai/code-search, workflow
# release-please.yml), (3) set the repo variable CRATES_IO_PUBLISH=true.
# The CLI ships inside this same crate as the `csp` binary (default-on `cli`
# feature — the ripgrep model), so `cargo publish -p code-search-please` and
# `cargo install code-search-please` both cover the library and the CLI; there
# is no separate CLI crate to version-manage.
publish-crate:
needs: [release-please]
if: ${{ needs.release-please.outputs.release_created && vars.CRATES_IO_PUBLISH == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout the released tag
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Authenticate to crates.io (Trusted Publishing)
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
id: auth
- name: Publish code-search-please
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: cargo publish -p code-search-please --locked