Skip to content

Commit 4592133

Browse files
authored
Add cli support for linux (#11167)
1 parent e90e617 commit 4592133

File tree

5 files changed

+786
-739
lines changed

5 files changed

+786
-739
lines changed

.github/workflows/cli-release.yml

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
name: CLI Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Version to release (e.g., 0.1.0). Leave empty to use package.json version.'
8+
required: false
9+
type: string
10+
dry_run:
11+
description: 'Dry run (build and test but do not create release).'
12+
required: false
13+
type: boolean
14+
default: false
15+
16+
jobs:
17+
# Build CLI for each platform.
18+
build:
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
include:
23+
- os: macos-latest
24+
platform: darwin-arm64
25+
runs-on: macos-latest
26+
- os: macos-13
27+
platform: darwin-x64
28+
runs-on: macos-13
29+
- os: ubuntu-latest
30+
platform: linux-x64
31+
runs-on: ubuntu-latest
32+
33+
runs-on: ${{ matrix.runs-on }}
34+
35+
steps:
36+
- name: Checkout code
37+
uses: actions/checkout@v4
38+
with:
39+
fetch-depth: 0
40+
41+
- name: Setup Node.js and pnpm
42+
uses: ./.github/actions/setup-node-pnpm
43+
44+
- name: Get version
45+
id: version
46+
run: |
47+
if [ -n "${{ inputs.version }}" ]; then
48+
VERSION="${{ inputs.version }}"
49+
else
50+
VERSION=$(node -p "require('./apps/cli/package.json').version")
51+
fi
52+
echo "version=$VERSION" >> $GITHUB_OUTPUT
53+
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
54+
echo "Using version: $VERSION"
55+
56+
- name: Build extension bundle
57+
run: pnpm bundle
58+
59+
- name: Build CLI
60+
run: pnpm --filter @roo-code/cli build
61+
62+
- name: Create release tarball
63+
id: tarball
64+
env:
65+
VERSION: ${{ steps.version.outputs.version }}
66+
PLATFORM: ${{ matrix.platform }}
67+
run: |
68+
RELEASE_DIR="roo-cli-${PLATFORM}"
69+
TARBALL="roo-cli-${PLATFORM}.tar.gz"
70+
71+
# Clean up any previous build.
72+
rm -rf "$RELEASE_DIR"
73+
rm -f "$TARBALL"
74+
75+
# Create directory structure.
76+
mkdir -p "$RELEASE_DIR/bin"
77+
mkdir -p "$RELEASE_DIR/lib"
78+
mkdir -p "$RELEASE_DIR/extension"
79+
80+
# Copy CLI dist files.
81+
echo "Copying CLI files..."
82+
cp -r apps/cli/dist/* "$RELEASE_DIR/lib/"
83+
84+
# Create package.json for npm install.
85+
echo "Creating package.json..."
86+
node -e "
87+
const pkg = require('./apps/cli/package.json');
88+
const newPkg = {
89+
name: '@roo-code/cli',
90+
version: '$VERSION',
91+
type: 'module',
92+
dependencies: {
93+
'@inkjs/ui': pkg.dependencies['@inkjs/ui'],
94+
'@trpc/client': pkg.dependencies['@trpc/client'],
95+
'commander': pkg.dependencies.commander,
96+
'fuzzysort': pkg.dependencies.fuzzysort,
97+
'ink': pkg.dependencies.ink,
98+
'p-wait-for': pkg.dependencies['p-wait-for'],
99+
'react': pkg.dependencies.react,
100+
'superjson': pkg.dependencies.superjson,
101+
'zustand': pkg.dependencies.zustand
102+
}
103+
};
104+
console.log(JSON.stringify(newPkg, null, 2));
105+
" > "$RELEASE_DIR/package.json"
106+
107+
# Copy extension bundle.
108+
echo "Copying extension bundle..."
109+
cp -r src/dist/* "$RELEASE_DIR/extension/"
110+
111+
# Add package.json to extension directory for CommonJS.
112+
echo '{"type": "commonjs"}' > "$RELEASE_DIR/extension/package.json"
113+
114+
# Find and copy ripgrep binary.
115+
echo "Looking for ripgrep binary..."
116+
RIPGREP_PATH=$(find node_modules -path "*/@vscode/ripgrep/bin/rg" -type f 2>/dev/null | head -1)
117+
if [ -n "$RIPGREP_PATH" ] && [ -f "$RIPGREP_PATH" ]; then
118+
echo "Found ripgrep at: $RIPGREP_PATH"
119+
mkdir -p "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin"
120+
cp "$RIPGREP_PATH" "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/"
121+
chmod +x "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/rg"
122+
mkdir -p "$RELEASE_DIR/bin"
123+
cp "$RIPGREP_PATH" "$RELEASE_DIR/bin/"
124+
chmod +x "$RELEASE_DIR/bin/rg"
125+
else
126+
echo "Warning: ripgrep binary not found"
127+
fi
128+
129+
# Create the wrapper script
130+
echo "Creating wrapper script..."
131+
cat > "$RELEASE_DIR/bin/roo" << 'WRAPPER_EOF'
132+
#!/usr/bin/env node
133+
134+
import { fileURLToPath } from 'url';
135+
import { dirname, join } from 'path';
136+
137+
const __filename = fileURLToPath(import.meta.url);
138+
const __dirname = dirname(__filename);
139+
140+
// Set environment variables for the CLI
141+
process.env.ROO_CLI_ROOT = join(__dirname, '..');
142+
process.env.ROO_EXTENSION_PATH = join(__dirname, '..', 'extension');
143+
process.env.ROO_RIPGREP_PATH = join(__dirname, 'rg');
144+
145+
// Import and run the actual CLI
146+
await import(join(__dirname, '..', 'lib', 'index.js'));
147+
WRAPPER_EOF
148+
149+
chmod +x "$RELEASE_DIR/bin/roo"
150+
151+
# Create empty .env file.
152+
touch "$RELEASE_DIR/.env"
153+
154+
# Create tarball.
155+
echo "Creating tarball..."
156+
tar -czvf "$TARBALL" "$RELEASE_DIR"
157+
158+
# Clean up release directory.
159+
rm -rf "$RELEASE_DIR"
160+
161+
# Create checksum.
162+
if command -v sha256sum &> /dev/null; then
163+
sha256sum "$TARBALL" > "${TARBALL}.sha256"
164+
elif command -v shasum &> /dev/null; then
165+
shasum -a 256 "$TARBALL" > "${TARBALL}.sha256"
166+
fi
167+
168+
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
169+
echo "Created: $TARBALL"
170+
ls -la "$TARBALL"
171+
172+
- name: Verify tarball
173+
env:
174+
PLATFORM: ${{ matrix.platform }}
175+
run: |
176+
TARBALL="roo-cli-${PLATFORM}.tar.gz"
177+
178+
# Create temp directory for verification.
179+
VERIFY_DIR=$(mktemp -d)
180+
181+
# Extract and verify structure.
182+
tar -xzf "$TARBALL" -C "$VERIFY_DIR"
183+
184+
echo "Verifying tarball contents..."
185+
ls -la "$VERIFY_DIR/roo-cli-${PLATFORM}/"
186+
187+
# Check required files exist.
188+
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/bin/roo" || { echo "Missing bin/roo"; exit 1; }
189+
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/lib/index.js" || { echo "Missing lib/index.js"; exit 1; }
190+
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/package.json" || { echo "Missing package.json"; exit 1; }
191+
test -d "$VERIFY_DIR/roo-cli-${PLATFORM}/extension" || { echo "Missing extension directory"; exit 1; }
192+
193+
echo "Tarball verification passed!"
194+
195+
# Cleanup.
196+
rm -rf "$VERIFY_DIR"
197+
198+
- name: Upload artifact
199+
uses: actions/upload-artifact@v4
200+
with:
201+
name: cli-${{ matrix.platform }}
202+
path: |
203+
roo-cli-${{ matrix.platform }}.tar.gz
204+
roo-cli-${{ matrix.platform }}.tar.gz.sha256
205+
retention-days: 7
206+
207+
# Create GitHub release with all platform artifacts.
208+
release:
209+
needs: build
210+
runs-on: ubuntu-latest
211+
if: ${{ !inputs.dry_run }}
212+
permissions:
213+
contents: write
214+
215+
steps:
216+
- name: Checkout code
217+
uses: actions/checkout@v4
218+
219+
- name: Get version
220+
id: version
221+
run: |
222+
if [ -n "${{ inputs.version }}" ]; then
223+
VERSION="${{ inputs.version }}"
224+
else
225+
VERSION=$(node -p "require('./apps/cli/package.json').version")
226+
fi
227+
echo "version=$VERSION" >> $GITHUB_OUTPUT
228+
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
229+
230+
- name: Download all artifacts
231+
uses: actions/download-artifact@v4
232+
with:
233+
path: artifacts
234+
235+
- name: Prepare release files
236+
run: |
237+
mkdir -p release
238+
find artifacts -name "*.tar.gz" -exec cp {} release/ \;
239+
find artifacts -name "*.sha256" -exec cp {} release/ \;
240+
ls -la release/
241+
242+
- name: Extract changelog
243+
id: changelog
244+
env:
245+
VERSION: ${{ steps.version.outputs.version }}
246+
run: |
247+
CHANGELOG_FILE="apps/cli/CHANGELOG.md"
248+
249+
if [ -f "$CHANGELOG_FILE" ]; then
250+
# Extract content between version headers.
251+
CONTENT=$(awk -v version="$VERSION" '
252+
BEGIN { found = 0; content = ""; target = "[" version "]" }
253+
/^## \[/ {
254+
if (found) { exit }
255+
if (index($0, target) > 0) { found = 1; next }
256+
}
257+
found { content = content $0 "\n" }
258+
END { print content }
259+
' "$CHANGELOG_FILE")
260+
261+
if [ -n "$CONTENT" ]; then
262+
echo "Found changelog content"
263+
echo "content<<EOF" >> $GITHUB_OUTPUT
264+
echo "$CONTENT" >> $GITHUB_OUTPUT
265+
echo "EOF" >> $GITHUB_OUTPUT
266+
else
267+
echo "No changelog content found for version $VERSION"
268+
echo "content=" >> $GITHUB_OUTPUT
269+
fi
270+
else
271+
echo "No changelog file found"
272+
echo "content=" >> $GITHUB_OUTPUT
273+
fi
274+
275+
- name: Generate checksums summary
276+
id: checksums
277+
run: |
278+
echo "checksums<<EOF" >> $GITHUB_OUTPUT
279+
cat release/*.sha256 >> $GITHUB_OUTPUT
280+
echo "EOF" >> $GITHUB_OUTPUT
281+
282+
- name: Check for existing release
283+
id: check_release
284+
env:
285+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
286+
TAG: ${{ steps.version.outputs.tag }}
287+
run: |
288+
if gh release view "$TAG" &> /dev/null; then
289+
echo "exists=true" >> $GITHUB_OUTPUT
290+
else
291+
echo "exists=false" >> $GITHUB_OUTPUT
292+
fi
293+
294+
- name: Delete existing release
295+
if: steps.check_release.outputs.exists == 'true'
296+
env:
297+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
298+
TAG: ${{ steps.version.outputs.tag }}
299+
run: |
300+
echo "Deleting existing release $TAG..."
301+
gh release delete "$TAG" --yes || true
302+
git push origin ":refs/tags/$TAG" || true
303+
304+
- name: Create GitHub Release
305+
env:
306+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
307+
VERSION: ${{ steps.version.outputs.version }}
308+
TAG: ${{ steps.version.outputs.tag }}
309+
CHANGELOG_CONTENT: ${{ steps.changelog.outputs.content }}
310+
CHECKSUMS: ${{ steps.checksums.outputs.checksums }}
311+
run: |
312+
WHATS_NEW=""
313+
if [ -n "$CHANGELOG_CONTENT" ]; then
314+
WHATS_NEW="## What's New
315+
316+
$CHANGELOG_CONTENT
317+
318+
"
319+
fi
320+
321+
RELEASE_NOTES=$(cat << EOF
322+
${WHATS_NEW}## Installation
323+
324+
\`\`\`bash
325+
curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh
326+
\`\`\`
327+
328+
Or install a specific version:
329+
\`\`\`bash
330+
ROO_VERSION=$VERSION curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh
331+
\`\`\`
332+
333+
## Requirements
334+
335+
- Node.js 20 or higher
336+
- macOS (Intel or Apple Silicon) or Linux x64
337+
338+
## Usage
339+
340+
\`\`\`bash
341+
# Run a task
342+
roo "What is this project?"
343+
344+
# See all options
345+
roo --help
346+
\`\`\`
347+
348+
## Platform Support
349+
350+
This release includes binaries for:
351+
- \`roo-cli-darwin-arm64.tar.gz\` - macOS Apple Silicon (M1/M2/M3)
352+
- \`roo-cli-darwin-x64.tar.gz\` - macOS Intel
353+
- \`roo-cli-linux-x64.tar.gz\` - Linux x64
354+
355+
## Checksums
356+
357+
\`\`\`
358+
${CHECKSUMS}
359+
\`\`\`
360+
EOF
361+
)
362+
363+
gh release create "$TAG" \
364+
--title "Roo Code CLI v$VERSION" \
365+
--notes "$RELEASE_NOTES" \
366+
--prerelease \
367+
release/*
368+
369+
echo "Release created: https://github.com/${{ github.repository }}/releases/tag/$TAG"
370+
371+
# Summary job for dry runs
372+
summary:
373+
needs: build
374+
runs-on: ubuntu-latest
375+
if: ${{ inputs.dry_run }}
376+
377+
steps:
378+
- name: Download all artifacts
379+
uses: actions/download-artifact@v4
380+
with:
381+
path: artifacts
382+
383+
- name: Show build summary
384+
run: |
385+
echo "## Dry Run Complete" >> $GITHUB_STEP_SUMMARY
386+
echo "" >> $GITHUB_STEP_SUMMARY
387+
echo "The following artifacts were built:" >> $GITHUB_STEP_SUMMARY
388+
echo "" >> $GITHUB_STEP_SUMMARY
389+
find artifacts -name "*.tar.gz" | while read f; do
390+
SIZE=$(ls -lh "$f" | awk '{print $5}')
391+
echo "- $(basename $f) ($SIZE)" >> $GITHUB_STEP_SUMMARY
392+
done
393+
echo "" >> $GITHUB_STEP_SUMMARY
394+
echo "### Checksums" >> $GITHUB_STEP_SUMMARY
395+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
396+
cat artifacts/*/*.sha256 >> $GITHUB_STEP_SUMMARY
397+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)