Skip to content

build: upgrade GitHub Actions and Docker setup versions #28

build: upgrade GitHub Actions and Docker setup versions

build: upgrade GitHub Actions and Docker setup versions #28

name: Build and Push Docker Image
on:
push:
branches:
- master
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
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@v6
- 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@v4
- name: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,format=short
type=raw,value=${{ github.sha }}
type=raw,value=${{ env.DATE_TAG }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
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@v6
- 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@v4
- name: Log in to the Container registry
uses: docker/login-action@v4
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@v6
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,format=short
type=raw,value=${{ github.sha }}
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
timeout-minutes: 15
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: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull image
run: docker pull ${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Start test container
run: docker run -d --name test-container ${{ env.IMAGE_NAME }}:${{ github.sha }} sleep 600
- name: Test default user is coder
timeout-minutes: 1
run: |
echo "Testing default user is 'coder'..."
USER=$(docker exec test-container 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 (VS Code terminal simulation)
timeout-minutes: 5
run: |
echo "Testing installed tools accessibility for coder user..."
echo "Using interactive shell (bash -i) to simulate VS Code terminal behavior"
# Test function with timeout
# Uses 'bash -i -c' to simulate VS Code terminal (interactive non-login shell)
# This ensures .bashrc is loaded, matching real VS Code terminal environment
test_tool() {
local tool=$1
local cmd=$2
local timeout_secs=10
if timeout $timeout_secs docker exec test-container bash -i -c "$cmd" > /dev/null 2>&1; then
echo " ✓ $tool is accessible"
return 0
else
echo " ✗ $tool is NOT accessible"
return 1
fi
}
FAILED_TOOLS=""
# Go tools
test_tool "go" "go version" || FAILED_TOOLS="$FAILED_TOOLS go"
test_tool "gopls" "gopls version" || FAILED_TOOLS="$FAILED_TOOLS gopls"
test_tool "dlv" "dlv version" || FAILED_TOOLS="$FAILED_TOOLS dlv"
test_tool "golangci-lint" "golangci-lint --version" || FAILED_TOOLS="$FAILED_TOOLS golangci-lint"
# Python tools
test_tool "python3" "python3 --version" || FAILED_TOOLS="$FAILED_TOOLS python3"
test_tool "pip" "pip --version" || FAILED_TOOLS="$FAILED_TOOLS pip"
test_tool "uv" "uv --version" || FAILED_TOOLS="$FAILED_TOOLS uv"
test_tool "conda" "conda --version" || FAILED_TOOLS="$FAILED_TOOLS conda"
# Node.js tools
test_tool "node" "node --version" || FAILED_TOOLS="$FAILED_TOOLS node"
test_tool "npm" "npm --version" || FAILED_TOOLS="$FAILED_TOOLS npm"
test_tool "pnpm" "pnpm --version" || FAILED_TOOLS="$FAILED_TOOLS pnpm"
test_tool "yarn" "yarn --version" || FAILED_TOOLS="$FAILED_TOOLS yarn"
# Java tools
test_tool "java" "java -version" || FAILED_TOOLS="$FAILED_TOOLS java"
test_tool "mvn" "mvn --version" || FAILED_TOOLS="$FAILED_TOOLS mvn"
# Ruby tools
test_tool "ruby" "ruby --version" || FAILED_TOOLS="$FAILED_TOOLS ruby"
test_tool "gem" "gem --version" || FAILED_TOOLS="$FAILED_TOOLS gem"
test_tool "rails" "rails --version" || FAILED_TOOLS="$FAILED_TOOLS rails"
# System tools
test_tool "git" "git --version" || FAILED_TOOLS="$FAILED_TOOLS git"
test_tool "curl" "curl --version" || FAILED_TOOLS="$FAILED_TOOLS curl"
test_tool "wget" "wget --version" || FAILED_TOOLS="$FAILED_TOOLS wget"
test_tool "vim" "vim --version | head -1" || FAILED_TOOLS="$FAILED_TOOLS vim"
test_tool "kubectl" "kubectl version --client --output=json" || FAILED_TOOLS="$FAILED_TOOLS kubectl"
test_tool "yq" "yq --version" || FAILED_TOOLS="$FAILED_TOOLS yq"
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 (VS Code terminal simulation)"
- name: Test sudo without password
timeout-minutes: 1
run: |
echo "Testing sudo works without password..."
docker exec test-container 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
timeout-minutes: 1
run: |
echo "Testing su command is blocked..."
if docker exec test-container 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 "::error::su command is not blocked as expected"
exit 1
fi
- name: Test mirror configurations
timeout-minutes: 2
run: |
echo "Testing mirror configurations..."
echo "Using interactive shell (bash -i) to simulate VS Code terminal behavior"
# Test Go proxy
docker exec test-container bash -i -c 'echo $GOPROXY | grep -q "goproxy.cn"' && echo " ✓ Go proxy configured" || echo " ⚠ Go proxy not using goproxy.cn"
# Test npm registry
docker exec test-container bash -i -c 'npm config get registry | grep -q "npmmirror"' && echo " ✓ npm registry configured" || echo " ⚠ npm registry not using npmmirror"
# Test gem sources
docker exec test-container bash -i -c 'gem sources | grep -q "ruby-china"' && echo " ✓ gem sources configured" || echo " ⚠ gem sources not using ruby-china"
echo "✓ Mirror configurations verified (VS Code terminal simulation)"
- name: Cleanup test container
if: always()
run: docker rm -f test-container 2>/dev/null || true