Download Claude Code Offline Packages #11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Download Claude Code Offline Packages | |
| on: | |
| # Manual trigger | |
| workflow_dispatch: | |
| inputs: | |
| force_rebuild: | |
| description: 'Force rebuild even if version exists' | |
| required: false | |
| default: 'false' | |
| # Daily check for new versions at 00:00 UTC | |
| # Weekly full rebuild every Monday at 00:00 UTC | |
| schedule: | |
| - cron: '0 0 * * *' # Daily | |
| - cron: '0 0 * * 1' # Weekly (Monday) | |
| env: | |
| NODE_VERSION: '20' | |
| NPM_REGISTRY: 'https://registry.npmmirror.com' | |
| jobs: | |
| check-version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| latest_version: ${{ steps.check.outputs.latest_version }} | |
| should_build: ${{ steps.check.outputs.should_build }} | |
| release_exists: ${{ steps.check.outputs.release_exists }} | |
| steps: | |
| - name: Install jq | |
| run: | | |
| sudo apt-get update && sudo apt-get install -y jq | |
| - name: Check latest version from npm | |
| id: check | |
| run: | | |
| set -e | |
| echo "=== Checking latest version from npm ===" | |
| # Get latest version from npm | |
| NPM_RESPONSE=$(curl -s https://registry.npmjs.org/@anthropic-ai/claude-code) | |
| LATEST_VERSION=$(echo "$NPM_RESPONSE" | jq -r '.["dist-tags"].latest // empty') | |
| if [ -z "$LATEST_VERSION" ] || [ "$LATEST_VERSION" = "null" ]; then | |
| echo "ERROR: Failed to get version from npm" | |
| echo "Response: $NPM_RESPONSE" | |
| exit 1 | |
| fi | |
| echo "Latest npm version: $LATEST_VERSION" | |
| echo "latest_version=$LATEST_VERSION" >> $GITHUB_OUTPUT | |
| # Check if release already exists | |
| echo "=== Checking if release exists ===" | |
| RELEASE_RESPONSE=$(curl -s -w "\n%{http_code}" \ | |
| "https://api.github.com/repos/${{ github.repository }}/releases/tags/v${LATEST_VERSION}") | |
| HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -1) | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| echo "Release v${LATEST_VERSION} already exists" | |
| echo "release_exists=true" >> $GITHUB_OUTPUT | |
| # Check if force rebuild | |
| FORCE_REBUILD="${{ github.event.inputs.force_rebuild }}" | |
| if [ "$FORCE_REBUILD" = "true" ]; then | |
| echo "Force rebuild requested" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Skipping build - version already exists" | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| fi | |
| elif [ "$HTTP_CODE" = "404" ]; then | |
| echo "Release v${LATEST_VERSION} does not exist" | |
| echo "release_exists=false" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "WARNING: Unexpected HTTP code $HTTP_CODE" | |
| echo "Assuming release does not exist, proceeding with build" | |
| echo "release_exists=false" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| fi | |
| build-offline-packages: | |
| needs: check-version | |
| runs-on: ubuntu-latest | |
| if: needs.check-version.outputs.should_build == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Debug outputs | |
| run: | | |
| echo "Latest version: ${{ needs.check-version.outputs.latest_version }}" | |
| echo "Should build: ${{ needs.check-version.outputs.should_build }}" | |
| echo "Release exists: ${{ needs.check-version.outputs.release_exists }}" | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Configure npm mirrors | |
| run: | | |
| # Configure npm to use domestic mirror | |
| npm config set registry ${{ env.NPM_REGISTRY }} | |
| # Verify configuration | |
| echo "npm registry configured: $(npm config get registry)" | |
| - name: Install Claude Code globally | |
| run: | | |
| npm install -g @anthropic-ai/claude-code | |
| # Verify installation | |
| echo "=== Verifying installation ===" | |
| which claude || echo "claude not in PATH" | |
| ls -la $(npm prefix -g)/lib/node_modules/@anthropic-ai/claude-code/ || true | |
| CLAUDE_VERSION="${{ needs.check-version.outputs.latest_version }}" | |
| echo "CLAUDE_VERSION=$CLAUDE_VERSION" >> $GITHUB_ENV | |
| echo "Building Claude Code version: $CLAUDE_VERSION" | |
| - name: Prepare offline packages | |
| run: | | |
| set -e | |
| echo "=== Preparing offline packages ===" | |
| # Create output directory | |
| mkdir -p claude-offline-packages | |
| # Get Claude Code version | |
| CLAUDE_VERSION="${{ needs.check-version.outputs.latest_version }}" | |
| echo "CLAUDE_VERSION=$CLAUDE_VERSION" >> $GITHUB_ENV | |
| echo "Building Claude Code version: $CLAUDE_VERSION" | |
| # Find global installation path | |
| GLOBAL_PREFIX=$(npm prefix -g) | |
| echo "NPM global prefix: $GLOBAL_PREFIX" | |
| # Copy global installation directly (don't npm install, it has publish restrictions) | |
| if [ -d "$GLOBAL_PREFIX/lib/node_modules/@anthropic-ai/claude-code" ]; then | |
| echo "Found global installation, copying..." | |
| mkdir -p claude-offline-packages/node_modules/@anthropic-ai | |
| cp -r "$GLOBAL_PREFIX/lib/node_modules/@anthropic-ai/claude-code" claude-offline-packages/node_modules/@anthropic-ai/ | |
| # Also copy .bin if exists | |
| if [ -d "$GLOBAL_PREFIX/lib/node_modules/.bin" ]; then | |
| cp -r "$GLOBAL_PREFIX/lib/node_modules/.bin" claude-offline-packages/node_modules/ | |
| fi | |
| else | |
| echo "ERROR: Global installation not found" | |
| find "$GLOBAL_PREFIX" -name "claude-code" -type d 2>/dev/null || true | |
| exit 1 | |
| fi | |
| cd claude-offline-packages | |
| # Create .bin directory and links if not exist | |
| mkdir -p node_modules/.bin | |
| if [ -f "node_modules/@anthropic-ai/claude-code/cli.js" ]; then | |
| echo "Creating symlink for cli.js" | |
| ln -sf ../@anthropic-ai/claude-code/cli.js node_modules/.bin/claude | |
| else | |
| echo "ERROR: cli.js not found" | |
| find . -name "cli.js" 2>/dev/null || true | |
| exit 1 | |
| fi | |
| # Add execute permissions | |
| chmod +x node_modules/.bin/claude 2>/dev/null || true | |
| chmod -R +x node_modules/@anthropic-ai/claude-code/ 2>/dev/null || true | |
| echo "=== Package structure ===" | |
| ls -la | |
| ls -la node_modules/.bin/ || true | |
| ls -la node_modules/@anthropic-ai/claude-code/ | head -20 | |
| - name: Download VSCode extension | |
| run: | | |
| # Try to download latest VSCode extension | |
| echo "=== Checking for VSCode extension ===" | |
| # Go back to root directory (Prepare step changed to claude-offline-packages) | |
| cd "$GITHUB_WORKSPACE" | |
| RELEASE_INFO=$(curl -s https://api.github.com/repos/anthropics/claude-code/releases/latest) | |
| EXTENSION_URL=$(echo "$RELEASE_INFO" | jq -r '.assets[] | select(.name | contains(".vsix")) | .browser_download_url' | head -1) | |
| if [ -n "$EXTENSION_URL" ] && [ "$EXTENSION_URL" != "null" ]; then | |
| echo "Downloading VSCode extension from: $EXTENSION_URL" | |
| curl -L -o claude-offline-packages/anthropic.claude-code.vsix "$EXTENSION_URL" || { | |
| echo "WARNING: Failed to download VSCode extension" | |
| } | |
| # Verify download | |
| if [ -f "claude-offline-packages/anthropic.claude-code.vsix" ]; then | |
| echo "✓ VSCode extension downloaded successfully" | |
| ls -lh claude-offline-packages/anthropic.claude-code.vsix | |
| fi | |
| else | |
| echo "No VSCode extension found in releases, skipping..." | |
| fi | |
| continue-on-error: true | |
| - name: Copy setup script | |
| run: | | |
| echo "=== Copying setup script ===" | |
| # Ensure we're in the correct directory | |
| cd "$GITHUB_WORKSPACE" | |
| cp setup-claude-code.sh claude-offline-packages/ | |
| chmod +x claude-offline-packages/setup-claude-code.sh | |
| if [ -f "check-update.sh" ]; then | |
| cp check-update.sh claude-offline-packages/ | |
| chmod +x claude-offline-packages/check-update.sh | |
| fi | |
| echo "Files in package:" | |
| ls -la claude-offline-packages/ | |
| # Verify VSCode extension is included | |
| if [ -f "claude-offline-packages/anthropic.claude-code.vsix" ]; then | |
| echo "✓ VSCode extension included in package" | |
| else | |
| echo "! VSCode extension not found in package" | |
| fi | |
| - name: Create package info | |
| run: | | |
| BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| CLAUDE_VERSION="${{ needs.check-version.outputs.latest_version }}" | |
| cat > claude-offline-packages/package-info.json << EOF | |
| { | |
| "name": "claude-offline-packages", | |
| "version": "${CLAUDE_VERSION}", | |
| "created_at": "${BUILD_DATE}", | |
| "node_version": "${{ env.NODE_VERSION }}", | |
| "source": "https://www.npmjs.com/package/@anthropic-ai/claude-code", | |
| "github_repo": "${{ github.repository }}" | |
| } | |
| EOF | |
| echo "=== Package info ===" | |
| cat claude-offline-packages/package-info.json | |
| - name: Pack offline packages | |
| run: | | |
| echo "=== Packing offline packages ===" | |
| # List all files before packing | |
| echo "Files to be packed:" | |
| ls -la claude-offline-packages/ | |
| # Check if VSCode extension exists | |
| if [ -f "claude-offline-packages/anthropic.claude-code.vsix" ]; then | |
| echo "✓ VSCode extension will be included in the package" | |
| else | |
| echo "! Warning: VSCode extension not found" | |
| fi | |
| tar -czf claude-offline-packages.tar.gz claude-offline-packages/ | |
| # Calculate checksum | |
| sha256sum claude-offline-packages.tar.gz > claude-offline-packages.tar.gz.sha256 | |
| # Show package size | |
| echo "Package size:" | |
| ls -lh claude-offline-packages.tar.gz | |
| ls -lh claude-offline-packages.tar.gz.sha256 | |
| # Verify archive contents | |
| echo "=== Verifying archive contents ===" | |
| echo "Looking for VSCode extension in archive:" | |
| tar -tzf claude-offline-packages.tar.gz | grep -i "vsix" || echo "! VSCode extension not found in archive" | |
| - name: Create GitHub Release | |
| id: create_release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: v${{ needs.check-version.outputs.latest_version }} | |
| name: Claude Code v${{ needs.check-version.outputs.latest_version }} | |
| body: | | |
| ## Claude Code Offline Package v${{ needs.check-version.outputs.latest_version }} | |
| **Version:** ${{ needs.check-version.outputs.latest_version }} | |
| **Node.js:** ${{ env.NODE_VERSION }} | |
| **Build Date:** $(date -u +"%Y-%m-%d %H:%M:%S UTC") | |
| ### Downloads | |
| | File | Description | | |
| |------|-------------| | |
| | `claude-offline-packages.tar.gz` | Offline installation package | | |
| | `claude-offline-packages.tar.gz.sha256` | SHA256 checksum | | |
| ### Quick Start | |
| ```bash | |
| # 1. Download and extract | |
| tar -xzf claude-offline-packages.tar.gz | |
| # 2. Run installer | |
| cd claude-offline-packages | |
| bash setup-claude-code.sh | |
| ``` | |
| ### Verification | |
| ```bash | |
| sha256sum -c claude-offline-packages.tar.gz.sha256 | |
| ``` | |
| ### Features | |
| - ✅ Automatic mirror source detection | |
| - ✅ Region restriction bypass | |
| - ✅ Multi-language support (EN/ZH) | |
| - ✅ Universal Node.js installation | |
| - ✅ Uninstall support | |
| draft: false | |
| prerelease: false | |
| files: | | |
| claude-offline-packages.tar.gz | |
| claude-offline-packages.tar.gz.sha256 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: claude-offline-packages-v${{ needs.check-version.outputs.latest_version }} | |
| path: | | |
| claude-offline-packages.tar.gz | |
| claude-offline-packages.tar.gz.sha256 | |
| retention-days: 30 | |
| notify-no-build: | |
| needs: check-version | |
| runs-on: ubuntu-latest | |
| if: needs.check-version.outputs.should_build == 'false' | |
| steps: | |
| - name: Skip notification | |
| run: | | |
| echo "Skipping build - version ${{ needs.check-version.outputs.latest_version }} already exists" | |
| echo "To force rebuild, trigger workflow with 'force_rebuild' set to 'true'" |