diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..dfecbb2 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,264 @@ +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.10 + + - 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 + + build-binaries: + needs: release-please + if: ${{ needs.release-please.outputs.release_created }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # csp embeds platform-specific native addons (onnxruntime-node, + # @kreuzberg/tree-sitter-language-pack). `bun build --compile` bundles + # the *host* platform's .node files, so each target MUST build on a + # native runner — cross-compiling with `--target` produces a binary + # with the wrong-arch addons ("bad CPU type in executable"). + include: + - os: macos-13 # Intel + target: darwin-x64 + - os: macos-14 # Apple Silicon + target: darwin-arm64 + - os: ubuntu-latest # x64 + target: linux-x64 + - os: ubuntu-24.04-arm # arm64 + target: linux-arm64 + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.3.10 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build binary (native target) + run: | + bun build src/cli.ts --compile --outfile csp-${{ matrix.target }} + + - name: Smoke test binary + run: | + ./csp-${{ matrix.target }} --version + + - name: Generate checksum + run: | + shasum -a 256 csp-${{ matrix.target }} > csp-${{ matrix.target }}.sha256 + + - name: Upload artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: csp-${{ matrix.target }} + path: | + csp-${{ matrix.target }} + csp-${{ matrix.target }}.sha256 + + upload-release-assets: + needs: [release-please, build-binaries] + if: ${{ needs.release-please.outputs.release_created }} + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Download all artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + path: artifacts + + - name: Prepare release assets + run: | + mkdir -p release + find artifacts -type f -exec cp {} release/ \; + ls -lh release/ + + - name: Upload release artifacts + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload ${{ needs.release-please.outputs.tag_name }} \ + release/* \ + --clobber + + update-homebrew-formula: + needs: [release-please, upload-release-assets] + if: ${{ 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 + curl -L -o darwin-x64.sha256 \ + "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-x64.sha256" + curl -L -o darwin-arm64.sha256 \ + "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-arm64.sha256" + curl -L -o linux-x64.sha256 \ + "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-linux-x64.sha256" + curl -L -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 + + # Check if there are changes + if git diff --quiet csp.rb 2>/dev/null; then + echo "No changes to Formula" + exit 0 + fi + + # Commit and push + git add csp.rb + git commit -m "chore: update csp to ${VERSION}" + git push origin main + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..e18ee07 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.0" +} diff --git a/README.ko.md b/README.ko.md index 9762784..0d831ee 100644 --- a/README.ko.md +++ b/README.ko.md @@ -71,11 +71,17 @@ codex plugin add csp@pleaseai 에이전트의 컨텍스트에 `csp` 사용법을 추가해 언제 어떻게 CLI를 호출할지 알 수 있도록 합니다. 먼저 `csp` CLI를 설치한 뒤, 아래 스니펫을 `AGENTS.md` 또는 `CLAUDE.md`에 추가하세요. ```bash +# Homebrew (macOS / Linux) — Node/Bun 없이 동작하는 독립 실행 바이너리 +brew install pleaseai/tap/csp + +# 또는 JavaScript 패키지 매니저로 설치 (PATH에 Bun 또는 Node 22+ 필요) bun add -g @pleaseai/csp # bun으로 설치 (권장) npm install -g @pleaseai/csp # 또는 npm pnpm add -g @pleaseai/csp # 또는 pnpm ``` +> Homebrew formula는 `bun build --compile`로 만든 자체 완결형 바이너리를 제공합니다(tree-sitter·임베딩 런타임 내장). 인덱스는 `~/.csp/`에 캐시됩니다([ADR 0002](.please/docs/decisions/0002-index-storage-cache-model.md) 참고). +
AGENTS.md / CLAUDE.md 스니펫 diff --git a/README.md b/README.md index 981ffe7..c85a0fa 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,17 @@ It bundles the `csp` MCP server (`search`, `find_related`) plus a `csp-search` s Add `csp` usage instructions to your agent's context so it knows when and how to call the CLI. Install the `csp` CLI, then add the snippet below to your `AGENTS.md` or `CLAUDE.md`: ```bash -bun add -g @pleaseai/csp # Install with bun (recommended) +# Homebrew (macOS / Linux) — standalone binary, no Node/Bun required +brew install pleaseai/tap/csp + +# Or via a JavaScript package manager (needs Bun or Node 22+ on your PATH) +bun add -g @pleaseai/csp # Install with bun (recommended) npm install -g @pleaseai/csp # Or with npm pnpm add -g @pleaseai/csp # Or with pnpm ``` +> The Homebrew formula ships a self-contained binary built with `bun build --compile` (tree-sitter and embedding runtimes are bundled). Indexes are cached under `~/.csp/` (see [ADR 0002](.please/docs/decisions/0002-index-storage-cache-model.md)). +
AGENTS.md / CLAUDE.md snippet diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..8225fbe --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "package-name": "@pleaseai/csp", + "include-component-in-tag": false, + "include-v-in-tag": true, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation" }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "chore", "section": "Miscellaneous Chores", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "build", "section": "Build System", "hidden": true }, + { "type": "ci", "section": "Continuous Integration", "hidden": true } + ], + "extra-files": [ + { + "type": "generic", + "path": "src/version.ts" + } + ] + } + } +} diff --git a/src/cli.ts b/src/cli.ts index 04e8b83..d7c7382 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -12,6 +12,7 @@ import { serve } from './mcp/server.ts' import { clearSavings, formatSavingsReport } from './stats.ts' import { ContentType } from './types.ts' import { formatResults, isGitUrl, resolveChunk } from './utils.ts' +import { version } from './version.ts' export enum Agent { Antigravity = 'antigravity', @@ -375,6 +376,11 @@ export async function runCli(argv: string[], options: RunOptions = {}): Promise< return 0 } + if (argv[0] === '-V' || argv[0] === '--version') { + process.stdout.write(`csp ${version}\n`) + return 0 + } + try { const { command, positional, flags } = parseArgs(argv) diff --git a/src/mcp/server.ts b/src/mcp/server.ts index a9f579f..90bc44d 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -7,6 +7,7 @@ import { loadOrBuildIndex } from '../indexing/cache.ts' import { CspIndex, loadModel } from '../indexing/index.ts' import { ContentType } from '../types.ts' import { formatResults, isGitUrl, resolveChunk } from '../utils.ts' +import { version } from '../version.ts' const REPO_DESCRIPTION = 'https:// or http:// git URL (e.g. https://github.com/org/repo) or local directory path to index and search. ' @@ -497,7 +498,7 @@ export async function createServer( } const underlying = new McpServer( - { name: 'csp', version: '0.0.0' }, + { name: 'csp', version }, { instructions: SERVER_INSTRUCTIONS }, ) diff --git a/src/version.ts b/src/version.ts index 88ac6c4..cb0faa2 100644 --- a/src/version.ts +++ b/src/version.ts @@ -5,6 +5,7 @@ // * `package.json#version` is the source of truth for npm publishing. // * Bun/tsdown don't read Python-style triples; reconstructing one would // just be dead code. -// A future integration PR will keep this in sync with `package.json#version` -// (e.g. via a generated file or a build-time replacement). -export const version = '0.0.0' +// Kept in sync with `package.json#version` by release-please via the +// `x-release-please-version` annotation below (see release-please-config.json +// `extra-files`). +export const version = '0.0.0' // x-release-please-version