release.sh 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. #!/bin/bash
  2. # Roo Code CLI Release Script
  3. #
  4. # Usage:
  5. # ./apps/cli/scripts/release.sh [version]
  6. #
  7. # Examples:
  8. # ./apps/cli/scripts/release.sh # Use version from package.json
  9. # ./apps/cli/scripts/release.sh 0.1.0 # Specify version
  10. #
  11. # This script:
  12. # 1. Builds the extension and CLI
  13. # 2. Creates a tarball for the current platform
  14. # 3. Creates a GitHub release and uploads the tarball
  15. #
  16. # Prerequisites:
  17. # - GitHub CLI (gh) installed and authenticated
  18. # - pnpm installed
  19. # - Run from the monorepo root directory
  20. set -e
  21. # Colors
  22. RED='\033[0;31m'
  23. GREEN='\033[0;32m'
  24. YELLOW='\033[1;33m'
  25. BLUE='\033[0;34m'
  26. BOLD='\033[1m'
  27. NC='\033[0m'
  28. info() { printf "${GREEN}==>${NC} %s\n" "$1"; }
  29. warn() { printf "${YELLOW}Warning:${NC} %s\n" "$1"; }
  30. error() { printf "${RED}Error:${NC} %s\n" "$1" >&2; exit 1; }
  31. step() { printf "${BLUE}${BOLD}[%s]${NC} %s\n" "$1" "$2"; }
  32. # Get script directory and repo root
  33. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  34. REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
  35. CLI_DIR="$REPO_ROOT/apps/cli"
  36. # Detect current platform
  37. detect_platform() {
  38. OS=$(uname -s | tr '[:upper:]' '[:lower:]')
  39. ARCH=$(uname -m)
  40. case "$OS" in
  41. darwin) OS="darwin" ;;
  42. linux) OS="linux" ;;
  43. *) error "Unsupported OS: $OS" ;;
  44. esac
  45. case "$ARCH" in
  46. x86_64|amd64) ARCH="x64" ;;
  47. arm64|aarch64) ARCH="arm64" ;;
  48. *) error "Unsupported architecture: $ARCH" ;;
  49. esac
  50. PLATFORM="${OS}-${ARCH}"
  51. }
  52. # Check prerequisites
  53. check_prerequisites() {
  54. step "1/7" "Checking prerequisites..."
  55. if ! command -v gh &> /dev/null; then
  56. error "GitHub CLI (gh) is not installed. Install it with: brew install gh"
  57. fi
  58. if ! gh auth status &> /dev/null; then
  59. error "GitHub CLI is not authenticated. Run: gh auth login"
  60. fi
  61. if ! command -v pnpm &> /dev/null; then
  62. error "pnpm is not installed."
  63. fi
  64. if ! command -v node &> /dev/null; then
  65. error "Node.js is not installed."
  66. fi
  67. info "Prerequisites OK"
  68. }
  69. # Get version
  70. get_version() {
  71. if [ -n "$1" ]; then
  72. VERSION="$1"
  73. else
  74. VERSION=$(node -p "require('$CLI_DIR/package.json').version")
  75. fi
  76. # Validate semver format
  77. if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
  78. error "Invalid version format: $VERSION (expected semver like 0.1.0)"
  79. fi
  80. TAG="cli-v$VERSION"
  81. info "Version: $VERSION (tag: $TAG)"
  82. }
  83. # Build everything
  84. build() {
  85. step "2/7" "Building extension bundle..."
  86. cd "$REPO_ROOT"
  87. pnpm bundle
  88. step "3/7" "Building CLI..."
  89. pnpm --filter @roo-code/cli build
  90. info "Build complete"
  91. }
  92. # Create release tarball
  93. create_tarball() {
  94. step "4/7" "Creating release tarball for $PLATFORM..."
  95. RELEASE_DIR="$REPO_ROOT/roo-cli-${PLATFORM}"
  96. TARBALL="roo-cli-${PLATFORM}.tar.gz"
  97. # Clean up any previous build
  98. rm -rf "$RELEASE_DIR"
  99. rm -f "$REPO_ROOT/$TARBALL"
  100. # Create directory structure
  101. mkdir -p "$RELEASE_DIR/bin"
  102. mkdir -p "$RELEASE_DIR/lib"
  103. mkdir -p "$RELEASE_DIR/extension"
  104. # Copy CLI dist files
  105. info "Copying CLI files..."
  106. cp -r "$CLI_DIR/dist/"* "$RELEASE_DIR/lib/"
  107. # Create package.json for npm install (only runtime dependencies)
  108. info "Creating package.json..."
  109. node -e "
  110. const pkg = require('$CLI_DIR/package.json');
  111. const newPkg = {
  112. name: '@roo-code/cli',
  113. version: pkg.version,
  114. type: 'module',
  115. dependencies: {
  116. commander: pkg.dependencies.commander
  117. }
  118. };
  119. console.log(JSON.stringify(newPkg, null, 2));
  120. " > "$RELEASE_DIR/package.json"
  121. # Copy extension bundle
  122. info "Copying extension bundle..."
  123. cp -r "$REPO_ROOT/src/dist/"* "$RELEASE_DIR/extension/"
  124. # Add package.json to extension directory to mark it as CommonJS
  125. # This is necessary because the main package.json has "type": "module"
  126. # but the extension bundle is CommonJS
  127. echo '{"type": "commonjs"}' > "$RELEASE_DIR/extension/package.json"
  128. # Find and copy ripgrep binary
  129. # The extension looks for ripgrep at: appRoot/node_modules/@vscode/ripgrep/bin/rg
  130. # The CLI sets appRoot to the CLI package root, so we need to put ripgrep there
  131. info "Looking for ripgrep binary..."
  132. RIPGREP_PATH=$(find "$REPO_ROOT/node_modules" -path "*/@vscode/ripgrep/bin/rg" -type f 2>/dev/null | head -1)
  133. if [ -n "$RIPGREP_PATH" ] && [ -f "$RIPGREP_PATH" ]; then
  134. info "Found ripgrep at: $RIPGREP_PATH"
  135. # Create the expected directory structure for the extension to find ripgrep
  136. mkdir -p "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin"
  137. cp "$RIPGREP_PATH" "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/"
  138. chmod +x "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/rg"
  139. # Also keep a copy in bin/ for direct access
  140. mkdir -p "$RELEASE_DIR/bin"
  141. cp "$RIPGREP_PATH" "$RELEASE_DIR/bin/"
  142. chmod +x "$RELEASE_DIR/bin/rg"
  143. else
  144. warn "ripgrep binary not found - users will need ripgrep installed"
  145. fi
  146. # Create the wrapper script
  147. info "Creating wrapper script..."
  148. cat > "$RELEASE_DIR/bin/roo" << 'WRAPPER_EOF'
  149. #!/usr/bin/env node
  150. import { fileURLToPath } from 'url';
  151. import { dirname, join } from 'path';
  152. const __filename = fileURLToPath(import.meta.url);
  153. const __dirname = dirname(__filename);
  154. // Set environment variables for the CLI
  155. process.env.ROO_EXTENSION_PATH = join(__dirname, '..', 'extension');
  156. process.env.ROO_RIPGREP_PATH = join(__dirname, 'rg');
  157. // Import and run the actual CLI
  158. await import(join(__dirname, '..', 'lib', 'index.js'));
  159. WRAPPER_EOF
  160. chmod +x "$RELEASE_DIR/bin/roo"
  161. # Create version file
  162. echo "$VERSION" > "$RELEASE_DIR/VERSION"
  163. # Create tarball
  164. info "Creating tarball..."
  165. cd "$REPO_ROOT"
  166. tar -czvf "$TARBALL" "$(basename "$RELEASE_DIR")"
  167. # Clean up release directory
  168. rm -rf "$RELEASE_DIR"
  169. # Show size
  170. TARBALL_PATH="$REPO_ROOT/$TARBALL"
  171. TARBALL_SIZE=$(ls -lh "$TARBALL_PATH" | awk '{print $5}')
  172. info "Created: $TARBALL ($TARBALL_SIZE)"
  173. }
  174. # Create checksum
  175. create_checksum() {
  176. step "5/7" "Creating checksum..."
  177. cd "$REPO_ROOT"
  178. if command -v sha256sum &> /dev/null; then
  179. sha256sum "$TARBALL" > "${TARBALL}.sha256"
  180. elif command -v shasum &> /dev/null; then
  181. shasum -a 256 "$TARBALL" > "${TARBALL}.sha256"
  182. else
  183. warn "No sha256sum or shasum found, skipping checksum"
  184. return
  185. fi
  186. info "Checksum: $(cat "${TARBALL}.sha256")"
  187. }
  188. # Check if release already exists
  189. check_existing_release() {
  190. step "6/7" "Checking for existing release..."
  191. if gh release view "$TAG" &> /dev/null; then
  192. warn "Release $TAG already exists"
  193. read -p "Do you want to delete it and create a new one? [y/N] " -n 1 -r
  194. echo
  195. if [[ $REPLY =~ ^[Yy]$ ]]; then
  196. info "Deleting existing release..."
  197. gh release delete "$TAG" --yes
  198. # Also delete the tag if it exists
  199. git tag -d "$TAG" 2>/dev/null || true
  200. git push origin ":refs/tags/$TAG" 2>/dev/null || true
  201. else
  202. error "Aborted. Use a different version or delete the existing release manually."
  203. fi
  204. fi
  205. }
  206. # Create GitHub release
  207. create_release() {
  208. step "7/7" "Creating GitHub release..."
  209. cd "$REPO_ROOT"
  210. RELEASE_NOTES=$(cat << EOF
  211. ## Installation
  212. \`\`\`bash
  213. curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh
  214. \`\`\`
  215. Or install a specific version:
  216. \`\`\`bash
  217. ROO_VERSION=$VERSION curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh
  218. \`\`\`
  219. ## Requirements
  220. - Node.js 20 or higher
  221. - macOS (Intel or Apple Silicon) or Linux (x64 or ARM64)
  222. ## Usage
  223. \`\`\`bash
  224. # Set your API key
  225. export OPENROUTER_API_KEY=sk-or-v1-...
  226. # Run a task
  227. roo "What is this project?" --workspace ~/my-project
  228. # See all options
  229. roo --help
  230. \`\`\`
  231. ## Platform Support
  232. This release includes:
  233. - \`roo-cli-${PLATFORM}.tar.gz\` - Built on $(uname -s) $(uname -m)
  234. > **Note:** Additional platforms will be added as needed. If you need a different platform, please open an issue.
  235. ## Checksum
  236. \`\`\`
  237. $(cat "${TARBALL}.sha256" 2>/dev/null || echo "N/A")
  238. \`\`\`
  239. EOF
  240. )
  241. # Get the current commit SHA for the release target
  242. COMMIT_SHA=$(git rev-parse HEAD)
  243. info "Creating release at commit: ${COMMIT_SHA:0:8}"
  244. # Create release (gh will create the tag automatically)
  245. info "Creating release..."
  246. RELEASE_FILES="$TARBALL"
  247. if [ -f "${TARBALL}.sha256" ]; then
  248. RELEASE_FILES="$RELEASE_FILES ${TARBALL}.sha256"
  249. fi
  250. gh release create "$TAG" \
  251. --title "Roo Code CLI v$VERSION" \
  252. --notes "$RELEASE_NOTES" \
  253. --prerelease \
  254. --target "$COMMIT_SHA" \
  255. $RELEASE_FILES
  256. info "Release created!"
  257. }
  258. # Cleanup
  259. cleanup() {
  260. info "Cleaning up..."
  261. cd "$REPO_ROOT"
  262. rm -f "$TARBALL" "${TARBALL}.sha256"
  263. }
  264. # Print summary
  265. print_summary() {
  266. echo ""
  267. printf "${GREEN}${BOLD}✓ Release v$VERSION created successfully!${NC}\n"
  268. echo ""
  269. echo " Release URL: https://github.com/RooCodeInc/Roo-Code/releases/tag/$TAG"
  270. echo ""
  271. echo " Install with:"
  272. echo " curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh"
  273. echo ""
  274. }
  275. # Main
  276. main() {
  277. echo ""
  278. printf "${BLUE}${BOLD}"
  279. echo " ╭─────────────────────────────────╮"
  280. echo " │ Roo Code CLI Release Script │"
  281. echo " ╰─────────────────────────────────╯"
  282. printf "${NC}"
  283. echo ""
  284. detect_platform
  285. check_prerequisites
  286. get_version "$1"
  287. build
  288. create_tarball
  289. create_checksum
  290. check_existing_release
  291. create_release
  292. cleanup
  293. print_summary
  294. }
  295. main "$@"