Development Containers (CI) #9
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
| --- | |
| # Note: Keep keys and envs in alphabetical order. | |
| name: Development Containers (CI) | |
| # yamllint disable-line rule:truthy | |
| on: | |
| pull_request: | |
| paths: | |
| - .devcontainer/** | |
| - .github/workflows/devcontainer-ci.yml | |
| push: | |
| branches: | |
| - master | |
| paths: | |
| - .devcontainer/** | |
| - .github/workflows/devcontainer-ci.yml | |
| schedule: | |
| - cron: 0 0 * * 1 # Run every Monday at 00:00 UTC | |
| workflow_call: | |
| # IMPORTANT: When calling this reusable workflow from another repository, | |
| # you MUST grant 'packages: write' permission in the calling workflow. | |
| # Example: | |
| # jobs: | |
| # devcontainer: | |
| # uses: FX31337/.github/.github/workflows/devcontainer-ci.yml@master | |
| # permissions: | |
| # contents: read | |
| # packages: write # Required for pushing to GitHub Container Registry | |
| inputs: | |
| required_commands: | |
| description: Space-separated list of required command-line tools | |
| required: false | |
| type: string | |
| default: '' | |
| required_python_packages: | |
| description: Space-separated list of required Python packages | |
| required: false | |
| type: string | |
| default: '' | |
| env: | |
| # Keep the commands and packages in lexicographical order. | |
| REQUIRED_COMMANDS: >- | |
| ${{ inputs.required_commands != '' && inputs.required_commands || 'actionlint | |
| ansible | |
| docker | |
| gh | |
| make | |
| node | |
| npm | |
| pip | |
| pre-commit | |
| python3 | |
| rg' }} | |
| REQUIRED_PYTHON_PACKAGES: >- | |
| ${{ inputs.required_python_packages != '' && inputs.required_python_packages || 'ansible | |
| ansible-lint | |
| docker | |
| molecule | |
| pre-commit | |
| uv' }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| jobs: | |
| devcontainer-build: | |
| name: Build & Test | |
| permissions: | |
| contents: read | |
| packages: write # Enables push to GHCR | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set image name | |
| id: image | |
| run: | | |
| # Transform repository name to valid Docker tag format | |
| # 1. Convert to lowercase (Docker tags must be lowercase) | |
| # 2. Remove leading dots from path components (Docker components must start with alphanumeric) | |
| REPO_NAME="${{ github.repository }}" | |
| REPO_LOWER="${REPO_NAME,,}" | |
| # Remove /. pattern to handle repos like "org/.github" -> "org/github" | |
| SAFE_REPO_NAME="${REPO_LOWER//\/\./\/}" | |
| echo "name=ghcr.io/${SAFE_REPO_NAME}/devcontainer" >> "$GITHUB_OUTPUT" | |
| - name: Check cache image existence | |
| id: cache_check | |
| continue-on-error: true | |
| run: | | |
| IMAGE_NAME="${{ steps.image.outputs.name }}" | |
| echo "Checking if cache image exists: ${IMAGE_NAME}:latest" | |
| if docker pull "${IMAGE_NAME}:latest" 2>/dev/null; then | |
| echo "✓ Cache image found: ${IMAGE_NAME}:latest" | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "⚠ Cache image not found: ${IMAGE_NAME}:latest" | |
| echo "⚠ Build will proceed without cache (first build or image expired)" | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Build and test dev container | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| imageName: ${{ steps.image.outputs.name }} | |
| cacheFrom: ${{ steps.image.outputs.name }} | |
| push: filter | |
| refFilterForPush: refs/heads/master | |
| runCmd: |- | |
| echo "Testing devcontainer build..." | |
| # Check all required commands are installed | |
| echo "Checking required command-line tools..." | |
| for cmd in $REQUIRED_COMMANDS; do | |
| if ! command -v "$cmd" &> /dev/null; then | |
| echo "✗ $cmd is not installed" | |
| exit 1 | |
| fi | |
| echo "✓ $cmd is installed" | |
| done | |
| # Check all required Python packages are installed | |
| echo "Checking required Python packages..." | |
| for pkg in $REQUIRED_PYTHON_PACKAGES; do | |
| if ! python3 -m pip show "$pkg" &> /dev/null; then | |
| echo "✗ Required Python package '$pkg' is missing" | |
| exit 1 | |
| fi | |
| echo "✓ $pkg is installed" | |
| done | |
| echo "✓ All required Python packages are installed" | |
| # Verify pre-commit can run (hooks may not be installed in CI container) | |
| if pre-commit --version &> /dev/null; then | |
| echo "✓ pre-commit is functional" | |
| else | |
| echo "✗ pre-commit is not functional" | |
| exit 1 | |
| fi | |
| echo "✓ All devcontainer tests passed!" |