Skip to content

Commit 00c5808

Browse files
committed
Add CLI release workflow, devcontainer feature, and CI improvements
- release-cli.yml: tag-triggered (cli-v*) npm publish + GitHub release - codeforge-cli devcontainer feature: installs CLI globally via npm - Register codeforge-cli feature in devcontainer.json - Remove dead codeforge alias, add codeforge to cc-tools list - CI: cross-platform test matrix (ubuntu, windows, macos) - Fix docs changelog sync paths for monorepo structure
1 parent 9e40e83 commit 00c5808

File tree

8 files changed

+171
-14
lines changed

8 files changed

+171
-14
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ name: CI
33
on:
44
push:
55
branches: [main, staging]
6-
paths: ['container/**']
6+
paths: ['container/**', 'cli/**']
77
pull_request:
88
branches: [main, staging]
9-
paths: ['container/**']
9+
paths: ['container/**', 'cli/**']
1010

1111
jobs:
1212
test:
@@ -41,7 +41,10 @@ jobs:
4141
working-directory: container
4242

4343
test-cli:
44-
runs-on: ubuntu-latest
44+
runs-on: ${{ matrix.os }}
45+
strategy:
46+
matrix:
47+
os: [ubuntu-latest, windows-latest, macos-latest]
4548
steps:
4649
- uses: actions/checkout@v6
4750
- uses: oven-sh/setup-bun@v2

.github/workflows/release-cli.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Release CLI
2+
3+
on:
4+
push:
5+
tags: ['cli-v*']
6+
7+
jobs:
8+
validate:
9+
runs-on: ubuntu-latest
10+
outputs:
11+
version: ${{ steps.extract.outputs.version }}
12+
steps:
13+
- uses: actions/checkout@v6
14+
- id: extract
15+
name: Extract and validate version
16+
run: |
17+
TAG="${GITHUB_REF#refs/tags/cli-v}"
18+
PKG=$(node -p "require('./cli/package.json').version")
19+
echo "version=$TAG" >> "$GITHUB_OUTPUT"
20+
if [ "$TAG" != "$PKG" ]; then
21+
echo "::error::Tag cli-v${TAG} does not match cli/package.json version ${PKG}"
22+
exit 1
23+
fi
24+
25+
publish-and-release:
26+
needs: validate
27+
runs-on: ubuntu-latest
28+
permissions:
29+
contents: write
30+
steps:
31+
- uses: actions/checkout@v6
32+
33+
- uses: oven-sh/setup-bun@v2
34+
35+
- name: Install dependencies
36+
run: bun install
37+
working-directory: cli
38+
39+
- name: Run tests
40+
run: bun test
41+
working-directory: cli
42+
43+
- name: Build
44+
run: bun run build
45+
working-directory: cli
46+
47+
- uses: actions/setup-node@v6
48+
with:
49+
node-version: 18
50+
registry-url: https://registry.npmjs.org
51+
52+
- name: Publish to npm
53+
run: npm publish
54+
working-directory: cli
55+
env:
56+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
57+
58+
- name: Extract changelog section
59+
id: changelog
60+
run: |
61+
VERSION="${{ needs.validate.outputs.version }}"
62+
NOTES=$(sed -n "/^## v${VERSION}/,/^## v/{ /^## v${VERSION}/d; /^## v/d; p; }" cli/CHANGELOG.md)
63+
[ -z "$NOTES" ] && NOTES="CLI Release v${VERSION}"
64+
echo "$NOTES" > /tmp/release-notes.md
65+
66+
- name: Create GitHub Release
67+
env:
68+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69+
run: |
70+
VERSION="cli-v${{ needs.validate.outputs.version }}"
71+
gh release create "$VERSION" --title "$VERSION" --notes-file /tmp/release-notes.md

container/.devcontainer/devcontainer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"ghcr.io/devcontainers/features/docker-outside-of-docker",
5959
"ghcr.io/devcontainers-extra/features/uv",
6060
"ghcr.io/rails/devcontainer/features/bun",
61+
"./features/codeforge-cli",
6162
"./features/claude-code-native",
6263
"./features/tmux",
6364
"./features/agent-browser",
@@ -149,6 +150,7 @@
149150
"./features/shellcheck": { "version": "none" },
150151
"./features/hadolint": { "version": "none" },
151152
"./features/biome": {},
153+
"./features/codeforge-cli": {},
152154
"./features/notify-hook": {
153155
"enableBell": true,
154156
"enableOsc": true
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# CodeForge CLI (codeforge-cli)
2+
3+
Installs the [CodeForge CLI](https://github.com/AnExiledDev/CodeForge/tree/main/cli) globally via npm. Provides the `codeforge` command for code review, session search, plugin management, and configuration.
4+
5+
Requires Node.js (for npm install) and Bun (runtime for the CLI binary).
6+
7+
## Options
8+
9+
| Option | Type | Default | Description |
10+
|--------|------|---------|-------------|
11+
| `version` | string | `latest` | Version to install. Use a specific semver or `'none'` to skip. |
12+
13+
## Usage
14+
15+
```jsonc
16+
// devcontainer.json
17+
"features": {
18+
"./features/codeforge-cli": {}
19+
}
20+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"id": "codeforge-cli",
3+
"version": "1.0.0",
4+
"name": "CodeForge CLI",
5+
"description": "Installs the CodeForge CLI for code review, session search, plugin management, and configuration",
6+
"documentationURL": "https://github.com/AnExiledDev/CodeForge/tree/main/cli",
7+
"options": {
8+
"version": {
9+
"type": "string",
10+
"description": "Version to install ('latest' or a specific semver). Use 'none' to skip.",
11+
"default": "latest"
12+
}
13+
},
14+
"installsAfter": [
15+
"ghcr.io/devcontainers/features/node",
16+
"ghcr.io/rails/devcontainer/features/bun"
17+
]
18+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-3.0-only
3+
# Copyright (c) 2026 Marcus Krueger
4+
set -euo pipefail
5+
6+
VERSION="${VERSION:-latest}"
7+
8+
# Skip installation if version is "none"
9+
if [ "${VERSION}" = "none" ]; then
10+
echo "[codeforge-cli] Skipping installation (version=none)"
11+
exit 0
12+
fi
13+
14+
echo "[codeforge-cli] Starting installation..."
15+
echo "[codeforge-cli] Version: ${VERSION}"
16+
17+
# Source NVM if available
18+
if [ -f /usr/local/share/nvm/nvm.sh ]; then
19+
set +u
20+
source /usr/local/share/nvm/nvm.sh
21+
set -u
22+
fi
23+
24+
# Validate npm is available
25+
if ! command -v npm &>/dev/null; then
26+
echo "[codeforge-cli] ERROR: npm not found. Ensure Node.js is installed." >&2
27+
exit 1
28+
fi
29+
30+
# Install CodeForge CLI globally via npm
31+
if [ "${VERSION}" = "latest" ]; then
32+
npm install -g codeforge-dev-cli
33+
else
34+
npm install -g "codeforge-dev-cli@${VERSION}"
35+
fi
36+
npm cache clean --force 2>/dev/null || true
37+
38+
# Verify installation
39+
codeforge --version
40+
41+
echo "[codeforge-cli] Installation complete"

container/.devcontainer/scripts/setup-aliases.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ cc-tools() {
103103
echo "━━━━━━━━━━━━━━━━━━━━━━━━"
104104
printf " %-20s %s\n" "COMMAND" "STATUS"
105105
echo " ────────────────────────────────────"
106-
for cmd in claude cc ccw ccraw cc-orc ccusage ccburn claude-monitor \\
106+
for cmd in claude cc ccw ccraw cc-orc codeforge ccusage ccburn claude-monitor \\
107107
ccms ct cargo ruff biome dprint shfmt shellcheck hadolint \\
108108
ast-grep tree-sitter pyright typescript-language-server \\
109109
agent-browser gh docker git jq tmux bun go infocmp; do
@@ -117,7 +117,6 @@ cc-tools() {
117117
}
118118
119119
alias check-setup='bash ${DEVCONTAINER_SCRIPTS}/check-setup.sh'
120-
alias codeforge='node \${WORKSPACE_ROOT}/setup.js'
121120
${BLOCK_END}
122121
BLOCK_EOF
123122

docs/scripts/sync-changelog.mjs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@
66
* source of truth — never edit the generated docs page directly.
77
*/
88

9-
import { readFileSync, writeFileSync } from 'node:fs';
10-
import { resolve, dirname } from 'node:path';
11-
import { fileURLToPath } from 'node:url';
9+
import { readFileSync, writeFileSync } from "node:fs";
10+
import { dirname, resolve } from "node:path";
11+
import { fileURLToPath } from "node:url";
1212

1313
const __dirname = dirname(fileURLToPath(import.meta.url));
14-
const source = resolve(__dirname, '../../container/.devcontainer/CHANGELOG.md');
15-
const dest = resolve(__dirname, '../src/content/docs/reference/changelog.md');
14+
const source = resolve(__dirname, "../../container/.devcontainer/CHANGELOG.md");
15+
const dest = resolve(__dirname, "../src/content/docs/reference/changelog.md");
1616

17-
const content = readFileSync(source, 'utf-8');
17+
const content = readFileSync(source, "utf-8");
1818

1919
// Strip the H1 heading — Starlight generates one from the frontmatter title
20-
const body = content.replace(/^# .+\n+/, '');
20+
const body = content.replace(/^# .+\n+/, "");
2121

2222
// Convert [vX.Y.Z] link-style headings to plain ## vX.Y.Z headings
2323
// Source uses ## [v1.14.0] - 2026-02-24, docs need ## v1.14.0
24-
const cleaned = body.replace(/^(##) \[v([\d.]+)\] - (\d{4}-\d{2}-\d{2})/gm, '$1 v$2\n\n**Release date:** $3');
24+
const cleaned = body.replace(
25+
/^(##) \[v([\d.]+)\] - (\d{4}-\d{2}-\d{2})/gm,
26+
"$1 v$2\n\n**Release date:** $3",
27+
);
2528

2629
const frontmatter = `---
2730
title: Changelog
@@ -75,4 +78,4 @@ For minor and patch updates, you can usually just rebuild the container. Check t
7578
`;
7679

7780
writeFileSync(dest, frontmatter + cleaned);
78-
console.log('✓ Changelog synced from container/.devcontainer/CHANGELOG.md');
81+
console.log("✓ Changelog synced from container/.devcontainer/CHANGELOG.md");

0 commit comments

Comments
 (0)