fix: resolve ruby command not found and add CI test stage #13
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: Build and Push Docker Image | |
| on: | |
| push: | |
| branches: | |
| - master | |
| workflow_dispatch: | |
| jobs: | |
| build: | |
| runs-on: ${{ matrix.runs-on }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runs-on: ubuntu-latest | |
| - platform: linux/arm64 | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Get current date for tagging | |
| run: echo "DATE_TAG=$(date +'%Y%m%d')" >> $GITHUB_ENV | |
| - name: Prepare platform pair | |
| run: | | |
| PLATFORM_PAIR=$(echo ${{ matrix.platform }} | tr '/' '-') | |
| echo "PLATFORM_PAIR=$PLATFORM_PAIR" >> $GITHUB_ENV | |
| - name: Set lowercase image name | |
| run: | | |
| echo "IMAGE_NAME=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Docker | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,format=short | |
| type=raw,value=${{ env.DATE_TAG }} | |
| - name: Build and push by digest | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| platforms: ${{ matrix.platform }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true | |
| cache-from: type=gha,scope=${{ matrix.platform }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.platform }} | |
| - name: Export digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: digests-${{ env.PLATFORM_PAIR }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| merge: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Get current date for tagging | |
| run: echo "DATE_TAG=$(date +'%Y%m%d')" >> $GITHUB_ENV | |
| - name: Set lowercase image name | |
| run: | | |
| echo "IMAGE_NAME=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests | |
| pattern: digests-* | |
| merge-multiple: true | |
| - name: Extract metadata (tags, labels) for Docker | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,format=short | |
| type=raw,value=${{ env.DATE_TAG }} | |
| - name: Create manifest list and push | |
| working-directory: /tmp/digests | |
| run: | | |
| # Build the list of digests | |
| digests=$(find . -type f | while read f; do | |
| digest=$(basename "$f") | |
| echo "${{ env.IMAGE_NAME }}@sha256:${digest}" | |
| done) | |
| # Create and push manifest with all tags | |
| tags="${{ steps.meta.outputs.tags }}" | |
| echo "$tags" | while read -r tag; do | |
| docker buildx imagetools create -t "$tag" $digests | |
| done | |
| - name: Inspect image | |
| run: | | |
| TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1 | tr -d '\n') | |
| docker buildx imagetools inspect "$TAG" | |
| test: | |
| name: Test Image | |
| runs-on: ${{ matrix.runs-on }} | |
| needs: merge | |
| permissions: | |
| contents: read | |
| packages: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runs-on: ubuntu-latest | |
| - platform: linux/arm64 | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Set lowercase image name | |
| run: | | |
| echo "IMAGE_NAME=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | |
| - name: Get current date for tagging | |
| run: echo "DATE_TAG=$(date +'%Y%m%d')" >> $GITHUB_ENV | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull image | |
| run: docker pull ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} | |
| - name: Test default user is coder | |
| run: | | |
| echo "Testing default user is 'coder'..." | |
| USER=$(docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} whoami) | |
| if [ "$USER" != "coder" ]; then | |
| echo "::error::Default user is '$USER', expected 'coder'" | |
| exit 1 | |
| fi | |
| echo "✓ Default user is 'coder'" | |
| - name: Test installed tools accessibility | |
| run: | | |
| echo "Testing installed tools accessibility for coder user..." | |
| # Define tools to test with their version commands | |
| declare -A TOOLS=( | |
| ["go"]="go version" | |
| ["gopls"]="gopls version" | |
| ["dlv"]="dlv version" | |
| ["golangci-lint"]="golangci-lint --version" | |
| ["python3"]="python3 --version" | |
| ["pip"]="pip --version" | |
| ["uv"]="uv --version" | |
| ["conda"]="conda --version" | |
| ["node"]="node --version" | |
| ["npm"]="npm --version" | |
| ["pnpm"]="pnpm --version" | |
| ["yarn"]="yarn --version" | |
| ["java"]="java -version" | |
| ["mvn"]="mvn --version" | |
| ["ruby"]="ruby --version" | |
| ["gem"]="gem --version" | |
| ["rails"]="rails --version" | |
| ["git"]="git --version" | |
| ["curl"]="curl --version" | |
| ["wget"]="wget --version" | |
| ["vim"]="vim --version | head -1" | |
| ["kubectl"]="kubectl version --client" | |
| ["yq"]="yq --version" | |
| ) | |
| FAILED_TOOLS="" | |
| for tool in "${!TOOLS[@]}"; do | |
| cmd="${TOOLS[$tool]}" | |
| echo "Testing $tool..." | |
| if docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c "$cmd" > /dev/null 2>&1; then | |
| echo " ✓ $tool is accessible" | |
| else | |
| echo " ✗ $tool is NOT accessible" | |
| FAILED_TOOLS="$FAILED_TOOLS $tool" | |
| fi | |
| done | |
| if [ -n "$FAILED_TOOLS" ]; then | |
| echo "::error::The following tools are not accessible:$FAILED_TOOLS" | |
| exit 1 | |
| fi | |
| echo "✓ All tools are accessible for coder user" | |
| - name: Test sudo without password | |
| run: | | |
| echo "Testing sudo works without password..." | |
| docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c "sudo whoami" | grep -q "root" && echo "✓ sudo works without password" || { echo "::error::sudo requires password"; exit 1; } | |
| - name: Test su command is blocked | |
| run: | | |
| echo "Testing su command is blocked..." | |
| if docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c "sudo su - root" 2>&1 | grep -q "sudo: su: command not found\|Sorry, user coder is not allowed to execute"; then | |
| echo "✓ su command is properly blocked" | |
| else | |
| echo "::warning::su command might not be properly blocked" | |
| fi | |
| - name: Test mirror configurations | |
| run: | | |
| echo "Testing mirror configurations..." | |
| # Test Go proxy | |
| docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c 'echo $GOPROXY | grep -q "goproxy.cn"' && echo " ✓ Go proxy configured" || echo " ⚠ Go proxy not using goproxy.cn" | |
| # Test npm registry | |
| docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c 'npm config get registry | grep -q "npmmirror"' && echo " ✓ npm registry configured" || echo " ⚠ npm registry not using npmmirror" | |
| # Test gem sources | |
| docker run --rm ${{ env.IMAGE_NAME }}:${{ env.DATE_TAG }} bash -c 'gem sources | grep -q "ruby-china"' && echo " ✓ gem sources configured" || echo " ⚠ gem sources not using ruby-china" | |
| echo "✓ Mirror configurations verified" |