(refactor): Added security considerations. Renamed isolated to hardened to more explicit description. Added Codespaces variant.
#23
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: Prebuilt Dev Containers - Build & Smoke Test | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths: | |
| - '.devcontainer/**' | |
| - '.github/workflows/main.yml' | |
| pull_request: | |
| paths: | |
| - '.devcontainer/**' | |
| - '.github/workflows/main.yml' | |
| workflow_dispatch: | |
| jobs: | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| subFolder: | |
| - .devcontainer/auditor | |
| - .devcontainer/minimal | |
| - .devcontainer/hardened | |
| - .devcontainer/airgapped | |
| - .devcontainer/eth-security-toolbox | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine if this devcontainer changed | |
| id: changed | |
| run: | | |
| set -e | |
| FOLDER="${{ matrix.subFolder }}" | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| git fetch origin "${{ github.base_ref }}" --depth=1 | |
| BASE=$(git merge-base FETCH_HEAD HEAD) | |
| if git diff --name-only "$BASE" HEAD -- "$FOLDER/" | grep .; then | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| BEFORE="${{ github.event.before }}" | |
| if [ -n "$BEFORE" ]; then | |
| if git diff --name-only "$BEFORE" "${{ github.sha }}" -- "$FOLDER/" | grep .; then | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| # Fallback: if we cannot determine before SHA, run the test | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| - name: No changes in this devcontainer — skipping | |
| if: steps.changed.outputs.changed != 'true' | |
| run: echo "No changes detected in ${{ matrix.subFolder }}; skipping build." | |
| - name: Check devcontainer config exists | |
| id: check | |
| if: steps.changed.outputs.changed == 'true' | |
| run: | | |
| if [ -f "${{ matrix.subFolder }}/devcontainer.json" ]; then | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| echo "Skipping: ${{ matrix.subFolder }}/devcontainer.json does not exist." | |
| fi | |
| - name: Build and run devcontainer | |
| if: steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: ${{ matrix.subFolder }} | |
| configFile: ${{ matrix.subFolder }}/devcontainer.json | |
| runCmd: echo "Devcontainer OK in ${{ matrix.subFolder }}" && uname -a | |
| push: never | |
| - name: Test Minimal Tools | |
| if: success() && steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: ${{ matrix.subFolder }} | |
| configFile: ${{ matrix.subFolder }}/devcontainer.json | |
| runCmd: | | |
| echo "🧪 Testing Foundry installation and functionality..." | |
| foundryup --version || echo "❌ Foundry not found" | |
| forge --version || echo "❌ Forge not found" | |
| cast --version || echo "❌ Cast not found" | |
| anvil --version || echo "❌ Anvil not found" | |
| echo "✅ Foundry tools verification completed" | |
| echo "🧪 Testing Hardhat installation and functionality..." | |
| hardhat --version || echo "❌ Hardhat not found" | |
| push: never | |
| - name: Test Auditor Tools | |
| if: success() && steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' && contains(fromJSON('[".devcontainer/auditor", ".devcontainer/hardened", ".devcontainer/airgapped"]'), matrix.subFolder) | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: ${{ matrix.subFolder }} | |
| configFile: ${{ matrix.subFolder }}/devcontainer.json | |
| runCmd: | | |
| echo "🧪 Testing Auditor tools..." | |
| slither --version || echo "❌ Slither not found" | |
| myth --version || echo "❌ Mythril not found" | |
| echidna --version || echo "❌ Echidna not found" | |
| echo "✅ Auditor tools verification completed" | |
| push: never | |
| - name: Test Filesystem Hardening | |
| if: success() && steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' && matrix.subFolder == '.devcontainer/hardened' | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: ${{ matrix.subFolder }} | |
| configFile: ${{ matrix.subFolder }}/devcontainer.json | |
| runCmd: | | |
| echo "🧪 Verifying that no host content is present in /workspace..." | |
| if [ -d "/workspace" ]; then | |
| if [ -n "$(ls -A /workspace 2>/dev/null || true)" ]; then | |
| echo "❌ Found content in /workspace (possible host mount)" | |
| ls -la /workspace || true | |
| exit 1 | |
| else | |
| echo "✅ /workspace exists but is empty (no host content)" | |
| fi | |
| else | |
| echo "✅ /workspace does not exist inside the container (no host mount)" | |
| fi | |
| echo "✅ /workspace Hardening verification completed" | |
| push: never | |
| - name: Test Network Hardening | |
| if: success() && steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' && matrix.subFolder == '.devcontainer/airgapped' | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: ${{ matrix.subFolder }} | |
| configFile: ${{ matrix.subFolder }}/devcontainer.json | |
| runCmd: | | |
| echo "🧪 Testing Network Hardening..." | |
| if (curl -sS https://www.google.com); then echo "❌ Network is not hardened"; exit 1; else echo "✅ Network is hardened"; fi | |
| echo "✅ Network Hardening verification completed" | |
| push: never | |
| - name: Purge Docker cache and resources (on success) | |
| if: success() && steps.check.outputs.exists == 'true' && steps.changed.outputs.changed == 'true' | |
| run: | | |
| echo "Pruning Docker resources to free memory and disk..." | |
| docker system prune -af --volumes || true | |
| docker builder prune -af || true | |
| docker system df || true |