Update testing doc (#2994) #317
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: Compile Documentation Bundle (Secure) | |
| on: | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - 'content/**' | |
| - 'scripts/compile_docs.py' | |
| - 'scripts/compile_docs_secure.py' | |
| schedule: | |
| - cron: '0 2 * * 0' # Weekly on Sundays at 2 AM | |
| workflow_dispatch: | |
| inputs: | |
| create_release: | |
| description: 'Create a GitHub release with signed artifacts' | |
| required: false | |
| default: 'false' | |
| # Restrict permissions to minimum required | |
| permissions: | |
| contents: write # For pushing compiled docs and creating releases | |
| actions: read | |
| id-token: write # For cosign keyless signing | |
| packages: write # For container registry | |
| jobs: | |
| compile-docs: | |
| runs-on: ubuntu-latest | |
| environment: documentation # Use environment protection rules | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout edu repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| path: edu | |
| persist-credentials: false # Don't persist auth token | |
| - name: Create placeholder directories for compilation | |
| run: | | |
| # Create empty directories for future repos (will be populated when credentials are added) | |
| mkdir -p courses | |
| mkdir -p images-private | |
| echo "# Placeholder" > courses/README.md | |
| echo "# Placeholder" > images-private/README.md | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.10' | |
| - name: Cache Python dependencies | |
| uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} | |
| - name: Validate repositories | |
| run: | | |
| # Verify we have the expected repositories | |
| for repo in edu courses images-private; do | |
| if [ ! -d "$repo" ]; then | |
| echo "Error: Expected repository $repo not found" | |
| exit 1 | |
| fi | |
| done | |
| # Check for suspicious files | |
| find . -name "*.sh" -o -name "*.py" | grep -v -E "(compile_docs\.py|create_compressed_docs\.sh)" | while read file; do | |
| echo "Warning: Unexpected script found: $file" | |
| done | |
| - name: Run security scan on Python script | |
| run: | | |
| cd edu | |
| # Install security tools | |
| pip install bandit safety | |
| # Scan for security issues | |
| bandit -r scripts/compile_docs.py -f json -o bandit-report.json || true | |
| # Check for critical issues | |
| if [ -f bandit-report.json ]; then | |
| critical_issues=$(python -c "import json; data=json.load(open('bandit-report.json')); print(len([i for i in data.get('results', []) if i.get('issue_severity') == 'HIGH']))") | |
| if [ "$critical_issues" -gt 0 ]; then | |
| echo "Critical security issues found in compile_docs.py" | |
| cat bandit-report.json | |
| exit 1 | |
| fi | |
| fi | |
| - name: Set environment variable for GitHub Actions | |
| run: echo "GITHUB_ACTIONS=true" >> $GITHUB_ENV | |
| - name: Create temporary directory | |
| run: echo "TEMP_BUILD_DIR=$(mktemp -d)" >> $GITHUB_ENV | |
| - name: Compile documentation with resource limits | |
| run: | | |
| cd edu | |
| # Create output directory | |
| mkdir -p $TEMP_BUILD_DIR | |
| # Run with timeout and memory limits, output to temp directory | |
| timeout 600 python3 scripts/compile_docs.py --output $TEMP_BUILD_DIR/chainguard-complete-docs.md | |
| # Verify output file size (max 50MB uncompressed) | |
| if [ -f $TEMP_BUILD_DIR/chainguard-complete-docs.md ]; then | |
| size=$(stat -c%s $TEMP_BUILD_DIR/chainguard-complete-docs.md) | |
| if [ $size -gt 52428800 ]; then | |
| echo "Error: Compiled documentation exceeds 50MB limit" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Create compressed versions | |
| run: | | |
| cd $TEMP_BUILD_DIR | |
| # Create compressed versions with integrity checks | |
| gzip -k -9 chainguard-complete-docs.md | |
| zip -9 chainguard-complete-docs.zip chainguard-complete-docs.md | |
| tar -czf chainguard-complete-docs.tar.gz chainguard-complete-docs.md | |
| # Generate checksums | |
| sha256sum chainguard-complete-docs.* > checksums.txt | |
| # Verify compressed files | |
| gzip -t chainguard-complete-docs.md.gz | |
| unzip -t chainguard-complete-docs.zip > /dev/null | |
| tar -tzf chainguard-complete-docs.tar.gz > /dev/null | |
| - name: Install cosign | |
| uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 | |
| - name: Sign documentation with cosign | |
| run: | | |
| cd $TEMP_BUILD_DIR | |
| # Sign the main documentation file (bundle format for cosign v3) | |
| cosign sign-blob chainguard-complete-docs.md \ | |
| --yes \ | |
| --bundle=chainguard-complete-docs.md.bundle | |
| # Sign the checksums file (bundle format for cosign v3) | |
| cosign sign-blob checksums.txt \ | |
| --yes \ | |
| --bundle=checksums.txt.bundle | |
| # Create placeholder .sig/.crt files for backward compatibility | |
| # (verification.sh and some tools still expect these) | |
| touch chainguard-complete-docs.md.sig chainguard-complete-docs.md.crt | |
| touch checksums.txt.sig checksums.txt.crt | |
| # Copy verification script | |
| cp $GITHUB_WORKSPACE/edu/scripts/verification.sh . | |
| # Create release bundle with all verification files | |
| tar -czf chainguard-ai-docs.tar.gz \ | |
| chainguard-complete-docs.md \ | |
| chainguard-complete-docs.md.bundle \ | |
| chainguard-complete-docs.md.sig \ | |
| chainguard-complete-docs.md.crt \ | |
| checksums.txt \ | |
| checksums.txt.bundle \ | |
| checksums.txt.sig \ | |
| checksums.txt.crt \ | |
| verification.sh | |
| # Sign the bundle (bundle format for cosign v3) | |
| cosign sign-blob chainguard-ai-docs.tar.gz \ | |
| --yes \ | |
| --bundle=chainguard-ai-docs.tar.gz.bundle | |
| - name: Build and push container image | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| cd edu | |
| # Copy necessary files for container build (rename to match Dockerfile expectations) | |
| cp $TEMP_BUILD_DIR/chainguard-complete-docs.md scripts/chainguard-ai-docs.md | |
| cp $TEMP_BUILD_DIR/chainguard-complete-docs.md.sig scripts/chainguard-ai-docs.md.sig | |
| cp $TEMP_BUILD_DIR/chainguard-complete-docs.md.crt scripts/chainguard-ai-docs.md.crt | |
| cp $TEMP_BUILD_DIR/checksums.txt scripts/checksums.txt | |
| # verification.sh is already in scripts/ directory | |
| # Build container with GitHub Container Registry | |
| docker build -f scripts/Dockerfile.ai-docs -t ghcr.io/${{ github.repository_owner }}/ai-docs:latest scripts/ | |
| docker tag ghcr.io/${{ github.repository_owner }}/ai-docs:latest ghcr.io/${{ github.repository_owner }}/ai-docs:${{ github.sha }} | |
| # Login to GitHub Container Registry | |
| echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| # Push images | |
| docker push ghcr.io/${{ github.repository_owner }}/ai-docs:latest | |
| docker push ghcr.io/${{ github.repository_owner }}/ai-docs:${{ github.sha }} | |
| # Sign container image with cosign | |
| cosign sign --yes ghcr.io/${{ github.repository_owner }}/ai-docs:latest | |
| cosign sign --yes ghcr.io/${{ github.repository_owner }}/ai-docs:${{ github.sha }} | |
| - name: Scan for sensitive data | |
| run: | | |
| cd edu | |
| # Create a custom gitleaks config to exclude false positives from documentation | |
| cat > .gitleaks.toml << 'EOF' | |
| title = "Gitleaks config for documentation scanning" | |
| [allowlist] | |
| description = "Allowlisted patterns for documentation" | |
| paths = [ | |
| '''.*\.md$''' | |
| ] | |
| regexes = [ | |
| '''`-----BEGIN PRIVATE KEY-----`''', | |
| '''GitHub tokens \(`ghp_`, `ghs_`\)''', | |
| '''id_ed25519\.pub''', | |
| '''\[REDACTED\]''', | |
| '''Example patterns we redact''', | |
| '''-----BEGIN PRIVATE KEY-----''' | |
| ] | |
| EOF | |
| # Install gitleaks | |
| wget -q https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz | |
| tar -xzf gitleaks_8.18.0_linux_x64.tar.gz | |
| # Scan compiled documentation for secrets with custom config | |
| ./gitleaks detect --no-git --source $TEMP_BUILD_DIR/ --config .gitleaks.toml --verbose --report-format json --report-path gitleaks-report.json || true | |
| # Check results - parse JSON to see if there are actual findings | |
| if [ -f gitleaks-report.json ]; then | |
| # Check if JSON array is not empty (more than just "[]") | |
| findings_count=$(cat gitleaks-report.json | grep -o '"Description":' | wc -l) | |
| if [ "$findings_count" -gt 0 ]; then | |
| echo "Potential secrets detected in compiled documentation!" | |
| cat gitleaks-report.json | |
| # Double-check if these are real secrets or just documentation examples | |
| if grep -q "Example patterns we redact" gitleaks-report.json; then | |
| echo "Note: These appear to be documentation examples, not actual secrets" | |
| else | |
| exit 1 | |
| fi | |
| else | |
| echo "No secrets detected in compiled documentation" | |
| fi | |
| fi | |
| - name: Delete existing latest release if exists | |
| if: (github.event.inputs.create_release == 'true' || github.event_name == 'schedule') && github.ref == 'refs/heads/main' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Delete existing ai-docs-latest release if it exists | |
| if gh release view ai-docs-latest > /dev/null 2>&1; then | |
| echo "Deleting existing ai-docs-latest release" | |
| gh release delete ai-docs-latest -y | |
| fi | |
| # Also delete the tag | |
| if git rev-parse ai-docs-latest > /dev/null 2>&1; then | |
| git push --delete origin ai-docs-latest || true | |
| fi | |
| - name: Create GitHub Release | |
| if: (github.event.inputs.create_release == 'true' || github.event_name == 'schedule') && github.ref == 'refs/heads/main' | |
| uses: step-security/action-gh-release@d45511d7589f080cf54961ff056b9705a74fd160 # v2.5.0 | |
| with: | |
| tag_name: ai-docs-latest | |
| name: AI Documentation Bundle - Latest | |
| body: | | |
| ## Chainguard AI Documentation Bundle | |
| Cryptographically signed documentation for AI coding assistants. | |
| ### Verification Instructions | |
| ```bash | |
| # Download latest release | |
| curl -LO https://github.com/${{ github.repository }}/releases/latest/download/chainguard-ai-docs.tar.gz | |
| curl -LO https://github.com/${{ github.repository }}/releases/latest/download/chainguard-ai-docs.tar.gz.bundle | |
| # Verify with cosign (bundle format) | |
| cosign verify-blob \ | |
| --bundle chainguard-ai-docs.tar.gz.bundle \ | |
| --certificate-identity-regexp ".*github.com/${{ github.repository }}.*" \ | |
| --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | |
| chainguard-ai-docs.tar.gz | |
| # Extract and verify contents | |
| tar -xzf chainguard-ai-docs.tar.gz | |
| ./verification.sh | |
| ``` | |
| ### Container Distribution | |
| ```bash | |
| # Pull and verify container (when available) | |
| cosign verify cgr.dev/chainguard/ai-docs:latest | |
| docker run --rm -v $(pwd):/output cgr.dev/chainguard/ai-docs:latest extract /output | |
| ``` | |
| ### What's New | |
| - Compiled from commit: ${{ github.sha }} | |
| - Build date: ${{ github.event.repository.updated_at }} | |
| files: | | |
| ${{ env.TEMP_BUILD_DIR }}/chainguard-ai-docs.tar.gz | |
| ${{ env.TEMP_BUILD_DIR }}/chainguard-ai-docs.tar.gz.bundle | |
| draft: false | |
| prerelease: false |