diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b4c3a1..65e83f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,8 @@ jobs: version: ${{ steps.version.outputs.version }} channel: ${{ steps.version.outputs.channel }} is_mandatory: ${{ steps.version.outputs.is_mandatory }} + has_apple: ${{ steps.credentials.outputs.has_apple }} + has_npm: ${{ steps.credentials.outputs.has_npm }} steps: - uses: actions/checkout@v7 - uses: pnpm/action-setup@v6 @@ -56,6 +58,25 @@ jobs: - run: pnpm docs:check - run: pnpm release:check + # Which publication legs can even run. A job that cannot possibly succeed + # should be skipped, not failed — a red release for a missing credential + # teaches people to ignore red releases. + - name: Detect available publishing credentials + id: credentials + run: | + if [ -n "${{ secrets.APPLE_ID }}" ] && [ -n "${{ secrets.CSC_LINK }}" ]; then + echo "has_apple=true" >> "$GITHUB_OUTPUT" + else + echo "has_apple=false" >> "$GITHUB_OUTPUT" + echo "::warning::Apple signing secrets absent — skipping the Mac client build. See docs/RELEASING.md." + fi + if [ -n "${{ secrets.NPM_TOKEN }}" ]; then + echo "has_npm=true" >> "$GITHUB_OUTPUT" + else + echo "has_npm=false" >> "$GITHUB_OUTPUT" + echo "::warning::NPM_TOKEN absent — skipping the npm publish. See docs/RELEASING.md." + fi + - name: Install Chromium run: pnpm --filter @deepcode/desktop exec playwright install --with-deps chromium @@ -93,10 +114,13 @@ jobs: # Publish CLI to npm # ---------------------------------------------------------------------- publish-cli: - name: Publish deepcode-cli to npm + name: Publish @deepcode/cli to npm # Avoid a partial release: do not publish npm until both installable # desktop/editor artifacts have built successfully. needs: [validate, build-vscode, build-mac] + # The anti-partial-release rule stands: npm still waits on both installable + # artifacts. It additionally needs a token to exist at all. + if: needs.validate.outputs.has_npm == 'true' runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -209,6 +233,7 @@ jobs: build-mac: name: Build + sign + notarize Mac client (Tauri) needs: validate + if: needs.validate.outputs.has_apple == 'true' runs-on: macos-14 timeout-minutes: 30 steps: @@ -333,6 +358,16 @@ jobs: github-release: name: Publish GitHub Release needs: [validate, publish-cli, build-vscode, build-mac] + # Ship what was actually built. `always()` is required because a skipped + # dependency would otherwise skip this job too — but the guard still refuses + # to publish when something tried and *failed*, so a broken build never + # becomes a release. + if: >- + always() + && needs.validate.result == 'success' + && needs.build-vscode.result == 'success' + && needs.build-mac.result != 'failure' + && needs.publish-cli.result != 'failure' runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -346,6 +381,7 @@ jobs: - run: pnpm install --frozen-lockfile - name: Download Mac DMG + if: needs.build-mac.result == 'success' uses: actions/download-artifact@v8 with: name: mac-release @@ -367,6 +403,14 @@ jobs: FROM=$(git rev-list --max-parents=0 HEAD) fi npx tsx scripts/gen-release-notes.ts "$FROM" HEAD > release-notes.md + # A release page missing an artifact reads as "there is no Mac build" + # rather than "it was not produced this time". Say which. + if [ "${{ needs.build-mac.result }}" != "success" ]; then + printf '\n---\n\n> **No macOS build in this release.** Apple signing credentials were not configured when it was cut; see `docs/RELEASING.md`.\n' >> release-notes.md + fi + if [ "${{ needs.publish-cli.result }}" != "success" ]; then + printf '\n> **Not published to npm.** `NPM_TOKEN` was not configured when this release was cut; install from source or the VSIX.\n' >> release-notes.md + fi cat release-notes.md - name: Create GitHub Release diff --git a/CHANGELOG.md b/CHANGELOG.md index 253a8b9..9899a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ adopted, what was rejected, and where the implementation diverged from the plan. ### ⚠️ Breaking +- **The CLI is published as `@deepcode/cli`, not `deepcode-cli`.** Install with + `npm i -g @deepcode/cli`. The unscoped name on npm belongs to an unrelated + project, so it was never ours to publish to. The binary is still `deepcode` + and nothing about the tool's behaviour changes. (#249) - **Unattended runs no longer inherit a permissive permission mode.** A `permissions.defaultMode` of `bypassPermissions` or `acceptEdits` — chosen for interactive convenience — is clamped to `default` for scheduled jobs, which diff --git a/README.md b/README.md index 02b663b..6fb53c2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ DeepCode 让 DeepSeek 可以在本地代码库中执行读取、编辑、命令 ```bash # 1. 装 CLI -npm i -g deepcode-cli +npm i -g @deepcode/cli # 2. 设 DeepSeek key(首次启动会引导) deepcode @@ -82,7 +82,7 @@ packages/ core/ # @deepcode/core — agent loop, providers, tools, MCP, sandbox, hooks (UI-agnostic) shared-ui/ # @deepcode/shared-ui — types shared between CLI + Mac client + VS Code apps/ - cli/ # deepcode-cli — Node.js CLI (npm publishable) + cli/ # @deepcode/cli — Node.js CLI (npm publishable) desktop/ # @deepcode/desktop — Tauri 2 + React Mac client vscode/ # deepcode — VS Code extension (app-server protocol client) lsp/ # @deepcode/lsp — LSP bridge for Neovim/Emacs/Sublime (v1.1) diff --git a/apps/cli/README.md b/apps/cli/README.md index cc3a1b4..9138435 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -1,11 +1,11 @@ -# deepcode-cli +# @deepcode/cli DeepCode CLI — Node.js 一键安装的命令行 AI 编程代理。 ## Install (post-v1) ```bash -npm i -g deepcode-cli +npm i -g @deepcode/cli deepcode --help ``` diff --git a/apps/cli/package.json b/apps/cli/package.json index 52e3eb6..7e1c9f2 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,5 +1,5 @@ { - "name": "deepcode-cli", + "name": "@deepcode/cli", "version": "0.3.0", "description": "DeepCode CLI — DeepSeek-powered AI coding agent for real codebases", "license": "MIT", diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts index 955d119..2ea2a0b 100644 --- a/apps/cli/src/cli.ts +++ b/apps/cli/src/cli.ts @@ -78,7 +78,7 @@ async function main(): Promise { return doctor(); } if (args.upgrade) { - process.stdout.write(`Run: npm i -g deepcode-cli@latest\n`); + process.stdout.write(`Run: npm i -g @deepcode/cli@latest\n`); process.stdout.write(`(The Mac client updates itself; only the CLI needs this.)\n`); return 0; } diff --git a/apps/cli/src/commands.ts b/apps/cli/src/commands.ts index cd5b8b8..41bd3c8 100644 --- a/apps/cli/src/commands.ts +++ b/apps/cli/src/commands.ts @@ -1151,7 +1151,7 @@ export const UpgradeCommand: SlashCommand = { run() { return [ `DeepCode CLI v${VERSION}`, - 'Update the CLI: npm i -g deepcode-cli@latest', + 'Update the CLI: npm i -g @deepcode/cli@latest', 'The macOS desktop app auto-updates via GitHub Releases.', ]; }, diff --git a/apps/cli/src/parity-commands.test.ts b/apps/cli/src/parity-commands.test.ts index be25438..f92ccc0 100644 --- a/apps/cli/src/parity-commands.test.ts +++ b/apps/cli/src/parity-commands.test.ts @@ -139,7 +139,7 @@ describe('/upgrade + /privacy-settings', () => { it('/upgrade shows the version + update instructions', async () => { const out = (await reg.match('/upgrade')!.cmd.run([], ctx())).join('\n'); expect(out).toMatch(/DeepCode CLI v\d/); - expect(out).toMatch(/npm i -g deepcode-cli@latest/); + expect(out).toContain('npm i -g @deepcode/cli@latest'); }); it('/privacy-settings shows data locations + the DeepSeek endpoint', async () => { diff --git a/docs/MIGRATION_FROM_CLAUDE_CODE.md b/docs/MIGRATION_FROM_CLAUDE_CODE.md index 4b6ecf7..670a5d3 100644 --- a/docs/MIGRATION_FROM_CLAUDE_CODE.md +++ b/docs/MIGRATION_FROM_CLAUDE_CODE.md @@ -25,7 +25,7 @@ The copy below is only needed if you want DeepCode to own its own copies. ```bash # 1. Install DeepCode CLI -npm install -g deepcode-cli +npm install -g @deepcode/cli # 2. Set your DeepSeek key mkdir -p ~/.deepcode diff --git a/docs/RELEASING.md b/docs/RELEASING.md index f8232b2..281a6fc 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -10,7 +10,19 @@ build failure cannot create an avoidable partial release. ### 1. GitHub Actions secrets Set these in repo settings → Secrets and variables → Actions → New -repository secret. All six are required for the complete release graph. +repository secret. All six are required for the _complete_ release graph. + +**A release without them still works, partially.** `validate` detects which +credential sets are present and skips the legs it cannot run: + +| Missing | Effect | +| ------------- | ---------------------------------------------------------------- | +| Apple secrets | `build-mac` is **skipped**; no DMG, and the release notes say so | +| `NPM_TOKEN` | `publish-cli` is **skipped**; nothing is published to npm | +| Neither | GitHub Release still ships with the VSIX and source | + +Skipped, not failed — a red release for a missing credential teaches people to +ignore red releases. A job that actually _fails_ still blocks the release. | Secret | Purpose | | ----------------------------- | ----------------------------------------------------------------- | @@ -19,7 +31,7 @@ repository secret. All six are required for the complete release graph. | `APPLE_TEAM_ID` | 10-character team ID (from developer.apple.com → membership) | | `CSC_LINK` | Base64-encoded `.p12` of the Developer ID Application cert | | `CSC_KEY_PASSWORD` | Password used when exporting the `.p12` | -| `NPM_TOKEN` | npm access token with `publish` scope | +| `NPM_TOKEN` | npm access token with `publish` scope for the `@deepcode` scope | ### 2. Export the Developer ID certificate @@ -48,6 +60,11 @@ App-specific passwords → Generate. Save the 16-char password as [npmjs.com](https://www.npmjs.com) → account → Access Tokens → Generate new token → **Automation** (CI-friendly) → save as `NPM_TOKEN`. +The CLI publishes as **`@deepcode/cli`**. The unscoped `deepcode-cli` on npm +belongs to an unrelated project, so the `@deepcode` org must exist and the token +must be able to publish into it. The workflow already passes `--access public`, +which scoped packages need in order not to default to private. + ## Releasing ```bash @@ -151,7 +168,7 @@ download manually; the "Relaunch to update" flow lights up once the feed exists. ## After a release -- Verify: `npm view deepcode-cli@` shows the new version +- Verify: `npm view @deepcode/cli@` shows the new version - Verify: `https://github.com/oratis/deepcode/releases/tag/v` has the DMG and version-matched VSIX attached - Optional: announce in the README / homepage diff --git a/docs/cli-flags.md b/docs/cli-flags.md index 2424c61..d8e80c1 100644 --- a/docs/cli-flags.md +++ b/docs/cli-flags.md @@ -20,7 +20,7 @@ deepcode upgrade # CLI self-update | `-h`, `--help` | Print usage | M2 ✅ | | `-v`, `--version` | Print version | M2 ✅ | | `doctor` | Health check (runtime, credentials, config provenance/trust/issues) | M2 ✅ | -| `upgrade` | Print `npm i -g deepcode-cli@latest` hint | M2 ✅ | +| `upgrade` | Print `npm i -g @deepcode/cli@latest` hint | M2 ✅ | | `-p`, `--print ` | Headless one-shot | M8 | ## Session shaping diff --git a/docs/quickstart.md b/docs/quickstart.md index 8ed5221..ef339e6 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -15,7 +15,7 @@ permission, and recovery behavior is still being unified; see the ```bash # 1. Install -npm i -g deepcode-cli +npm i -g @deepcode/cli # 2. Start the REPL — the first run walks you through setting your DeepSeek key deepcode diff --git a/packages/core/src/config/claude-compat.test.ts b/packages/core/src/config/claude-compat.test.ts index 6473d2a..8045d2c 100644 --- a/packages/core/src/config/claude-compat.test.ts +++ b/packages/core/src/config/claude-compat.test.ts @@ -2,7 +2,7 @@ // // The migration guide asked people to `mv ~/.claude/... ~/.deepcode/...` before // DeepCode would see anything they had. That is five steps of grit in front of -// `npm i -g deepcode-cli && deepcode`, and it is the first thing a migrating +// `npm i -g @deepcode/cli && deepcode`, and it is the first thing a migrating // user hits. import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';