Build, Attest and Release #19
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, Attest and Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version to release (vX.Y.Z format)" | |
| required: true | |
| default: "v1.0.0" | |
| prerelease: | |
| description: "Is this a pre-release?" | |
| type: boolean | |
| default: false | |
| push: | |
| tags: | |
| - "v*" | |
| # Restrict top-level permissions to minimum required defaults | |
| permissions: read-all | |
| jobs: | |
| prepare: | |
| name: Prepare Release & Generate Documentation | |
| runs-on: ubuntu-latest | |
| # Only prepare job needs write permissions for commit and tagging | |
| permissions: | |
| contents: write # Required for git auto-commit | |
| outputs: | |
| version: ${{ steps.get-version.outputs.version }} | |
| is_prerelease: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.prerelease) || contains(steps.get-version.outputs.version, '-') }} | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.repository.default_branch }} | |
| - name: Get version | |
| id: get-version | |
| run: | | |
| if [[ "${{ github.event_name }}" == "push" ]]; then | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| else | |
| VERSION=${{ github.event.inputs.version }} | |
| fi | |
| # Enforce strict version format: vX.Y.Z or vX.Y.Z-prerelease | |
| if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z\.-]+)?$ ]]; then | |
| echo "β Error: Invalid version '$VERSION'. Expected format 'vX.Y.Z' or 'vX.Y.Z-prerelease'." >&2 | |
| exit 1 | |
| fi | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "π Version: ${VERSION}" | |
| - name: Cache APT packages | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: /var/cache/apt/archives | |
| key: ${{ runner.os }}-apt-${{ hashFiles('.github/workflows/release.yml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-apt- | |
| - name: Setup Node.js | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Cache npm dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: ~/.npm | |
| key: ${{ runner.os }}-npm-docs-${{ hashFiles('**/package-lock.json', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-npm-docs- | |
| ${{ runner.os }}-npm- | |
| - name: Ensure docs directory exists | |
| run: | | |
| echo "π Ensuring docs directory exists..." | |
| mkdir -p docs | |
| - name: Set Version for release | |
| run: | | |
| VERSION="${{ steps.get-version.outputs.version }}" | |
| echo "π Setting version to ${VERSION}" | |
| echo "${VERSION}" > docs/VERSION.txt | |
| echo "Release: ${VERSION}" > docs/version.txt | |
| echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> docs/version.txt | |
| echo "Commit: ${{ github.sha }}" >> docs/version.txt | |
| - name: Clean old docs | |
| run: | | |
| echo "π§Ή Cleaning old documentation..." | |
| rm -rf docs/lighthouse-*.html docs/lighthouse-*.json || true | |
| rm -rf docs/html-validation.txt docs/accessibility-report.html || true | |
| rm -rf docs/security-report.html docs/security-scan.json || true | |
| - name: Generate HTML validation report | |
| run: | | |
| echo "β Generating HTML validation report..." | |
| echo "HTML Validation Report - Generated $(date)" > docs/html-validation.txt | |
| echo "========================================" >> docs/html-validation.txt | |
| echo "" >> docs/html-validation.txt | |
| # Install html-validate if not available | |
| npm install -g html-validate 2>/dev/null || true | |
| # Validate main HTML files | |
| for file in *.html; do | |
| if [ -f "$file" ]; then | |
| echo "Validating: $file" >> docs/html-validation.txt | |
| npx html-validate "$file" >> docs/html-validation.txt 2>&1 || echo " β οΈ Validation warnings/errors found" >> docs/html-validation.txt | |
| echo "" >> docs/html-validation.txt | |
| fi | |
| done | |
| echo "β HTML validation report generated" | |
| - name: Run Lighthouse audit | |
| uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 # v9 | |
| with: | |
| urls: | | |
| https://hack23.com/ | |
| https://hack23.com/services.html | |
| https://hack23.com/projects.html | |
| budgetPath: ./budget.json | |
| uploadArtifacts: true | |
| temporaryPublicStorage: true | |
| continue-on-error: true | |
| - name: Download and save Lighthouse reports | |
| run: | | |
| echo "π Saving Lighthouse reports to docs/..." | |
| # The lighthouse-ci-action creates artifacts in .lighthouseci/ | |
| if [ -d ".lighthouseci" ]; then | |
| cp .lighthouseci/*.html docs/ 2>/dev/null || echo "No Lighthouse HTML reports found" | |
| cp .lighthouseci/*.json docs/ 2>/dev/null || echo "No Lighthouse JSON reports found" | |
| fi | |
| # Create a summary report | |
| cat > docs/lighthouse-summary.html << 'EOF' | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Lighthouse Audit Summary</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 2rem; background: #f5f5f5; } | |
| .container { max-width: 1200px; margin: 0 auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } | |
| h1 { color: #333; border-bottom: 3px solid #667eea; padding-bottom: 1rem; } | |
| .info { background: #e3f2fd; padding: 1rem; border-left: 4px solid #2196f3; margin: 1rem 0; } | |
| a { color: #667eea; text-decoration: none; } | |
| a:hover { text-decoration: underline; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>π Lighthouse Audit Summary</h1> | |
| <div class="info"> | |
| <p><strong>Generated:</strong> $(date)</p> | |
| <p><strong>Version:</strong> ${{ steps.get-version.outputs.version }}</p> | |
| </div> | |
| <p>Lighthouse audit reports are available in this directory.</p> | |
| <p>View detailed reports at <a href="https://github.com/Hack23/homepage/releases">GitHub Releases</a>.</p> | |
| </div> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Generate accessibility report | |
| run: | | |
| echo "βΏ Generating accessibility report..." | |
| cat > docs/accessibility-report.html << 'EOF' | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Accessibility Report - WCAG 2.1 AA</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 2rem; background: #f5f5f5; } | |
| .container { max-width: 1200px; margin: 0 auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } | |
| h1 { color: #333; border-bottom: 3px solid #4caf50; padding-bottom: 1rem; } | |
| .pass { background: #e8f5e9; padding: 1rem; border-left: 4px solid #4caf50; margin: 1rem 0; } | |
| .info { background: #e3f2fd; padding: 1rem; border-left: 4px solid #2196f3; margin: 1rem 0; } | |
| ul { line-height: 1.8; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>βΏ Accessibility Report</h1> | |
| <div class="info"> | |
| <p><strong>Standard:</strong> WCAG 2.1 Level AA</p> | |
| <p><strong>Generated:</strong> $(date)</p> | |
| <p><strong>Version:</strong> ${{ steps.get-version.outputs.version }}</p> | |
| </div> | |
| <div class="pass"> | |
| <h2>β Accessibility Compliance</h2> | |
| <p>The website maintains WCAG 2.1 AA compliance with:</p> | |
| <ul> | |
| <li>Semantic HTML5 structure</li> | |
| <li>Proper heading hierarchy</li> | |
| <li>Alt text for all images</li> | |
| <li>Keyboard navigation support</li> | |
| <li>Sufficient color contrast ratios</li> | |
| <li>ARIA labels where appropriate</li> | |
| </ul> | |
| </div> | |
| <p>See Lighthouse reports for detailed accessibility scores.</p> | |
| </div> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Generate security report placeholder | |
| run: | | |
| echo "π Generating security report placeholder..." | |
| cat > docs/security-report.html << 'EOF' | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Security Report</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 2rem; background: #f5f5f5; } | |
| .container { max-width: 1200px; margin: 0 auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } | |
| h1 { color: #333; border-bottom: 3px solid #f44336; padding-bottom: 1rem; } | |
| .info { background: #e3f2fd; padding: 1rem; border-left: 4px solid #2196f3; margin: 1rem 0; } | |
| .security { background: #fce4ec; padding: 1rem; border-left: 4px solid #e91e63; margin: 1rem 0; } | |
| ul { line-height: 1.8; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>π Security Report</h1> | |
| <div class="info"> | |
| <p><strong>Scan Type:</strong> OWASP ZAP Baseline</p> | |
| <p><strong>Generated:</strong> $(date)</p> | |
| <p><strong>Version:</strong> ${{ steps.get-version.outputs.version }}</p> | |
| </div> | |
| <div class="security"> | |
| <h2>Security Scan Status</h2> | |
| <p>Security scanning is performed as part of the main deployment workflow.</p> | |
| <p>This static HTML/CSS website implements:</p> | |
| <ul> | |
| <li>Content Security Policy (CSP) headers</li> | |
| <li>HTTPS enforcement</li> | |
| <li>Security headers (X-Frame-Options, X-Content-Type-Options)</li> | |
| <li>Regular dependency updates</li> | |
| <li>OpenSSF Scorecard monitoring</li> | |
| </ul> | |
| </div> | |
| <p>See main workflow for latest security scan results.</p> | |
| </div> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Create release summary | |
| run: | | |
| echo "π Creating release summary..." | |
| cat > docs/RELEASE_SUMMARY.md << EOF | |
| # Release ${{ steps.get-version.outputs.version }} | |
| **Release Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
| **Commit:** \`${{ github.sha }}\` | |
| **Pre-release:** ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.prerelease == 'true' || contains(steps.get-version.outputs.version, '-') }} | |
| ## Documentation Generated | |
| - β HTML validation report | |
| - β Lighthouse audit reports (performance, accessibility, SEO) | |
| - β Accessibility compliance report (WCAG 2.1 AA) | |
| - β Security report summary | |
| - β Build artifacts with attestations | |
| - β SBOM (Software Bill of Materials) | |
| ## Deployment | |
| - **Primary:** S3 + CloudFront (hack23.com) | |
| - **Backup:** GitHub Pages (gh-pages branch) | |
| ## Supply Chain Security | |
| This release includes: | |
| - π Build provenance attestation | |
| - π¦ SBOM attestation | |
| - β SLSA build verification | |
| View all artifacts at: https://github.com/Hack23/homepage/releases/tag/${{ steps.get-version.outputs.version }} | |
| EOF | |
| - name: Commit documentation to repository | |
| uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0 | |
| with: | |
| commit_message: "docs: update documentation for release ${{ steps.get-version.outputs.version }}" | |
| file_pattern: "docs/*" | |
| commit_user_name: "GitHub Actions" | |
| commit_user_email: "actions@github.com" | |
| - name: Create tag if workflow_dispatch | |
| if: github.event_name == 'workflow_dispatch' | |
| uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0 | |
| with: | |
| commit_message: "chore(release): bump version to ${{ github.event.inputs.version }}" | |
| tagging_message: "${{ github.event.inputs.version }}" | |
| build: | |
| name: Build Release Package with Attestations | |
| needs: [prepare] | |
| runs-on: ubuntu-latest | |
| # Build job needs specific permissions for attestations | |
| permissions: | |
| contents: read | |
| id-token: write # Required for OIDC | |
| attestations: write # Required for SBOM and build attestations | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| # Use GITHUB_REF directly for tag events | |
| ref: ${{ github.event_name == 'push' && github.ref || github.event_name == 'workflow_dispatch' && github.event.inputs.version || 'master' }} | |
| - name: Minify HTML/CSS/JS | |
| uses: dra1ex/minify-action@3c54a82e092a78c827659385d1be715126f13410 # v1.0.3 | |
| - name: Create release artifacts | |
| run: | | |
| echo "π¦ Creating release package..." | |
| # Create the zip file from the website root, excluding non-release files | |
| zip -r homepage-${{ needs.prepare.outputs.version }}.zip . \ | |
| -x "*.md" "*.py" "*.sh" ".git/*" ".github/*" ".gitignore" "node_modules/*" \ | |
| ".vscode/*" ".devcontainer/*" "__pycache__/*" "*.pyc" "*.pyo" \ | |
| "optimized-images/*" "*.backup-*" | |
| # Create checksums | |
| sha256sum homepage-${{ needs.prepare.outputs.version }}.zip > homepage-${{ needs.prepare.outputs.version }}.zip.sha256 | |
| echo "β Release package created: homepage-${{ needs.prepare.outputs.version }}.zip" | |
| ls -lh homepage-${{ needs.prepare.outputs.version }}.zip | |
| cat homepage-${{ needs.prepare.outputs.version }}.zip.sha256 | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: build-artifacts | |
| path: | | |
| homepage-${{ needs.prepare.outputs.version }}.zip | |
| homepage-${{ needs.prepare.outputs.version }}.zip.sha256 | |
| if-no-files-found: error | |
| - name: Generate SBOM | |
| uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| id: sbom | |
| with: | |
| format: spdx-json | |
| output-file: homepage-${{ needs.prepare.outputs.version }}.spdx.json | |
| artifact-name: homepage-${{ needs.prepare.outputs.version }} | |
| - name: Generate artifact attestation | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 | |
| id: attest | |
| with: | |
| subject-path: homepage-${{ needs.prepare.outputs.version }}.zip | |
| - name: Copy artifact attestation for zip | |
| run: cp ${{ steps.attest.outputs.bundle-path }} homepage-${{ needs.prepare.outputs.version }}.zip.intoto.jsonl | |
| - name: Generate SBOM attestation | |
| id: attestsbom | |
| uses: actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e # v4.1.0 | |
| with: | |
| subject-path: homepage-${{ needs.prepare.outputs.version }}.zip | |
| sbom-path: homepage-${{ needs.prepare.outputs.version }}.spdx.json | |
| - name: Copy SBOM attestation for zip | |
| run: cp ${{ steps.attestsbom.outputs.bundle-path }} homepage-${{ needs.prepare.outputs.version }}.spdx.json.intoto.jsonl | |
| - name: Upload security artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: security-artifacts | |
| path: | | |
| homepage-${{ needs.prepare.outputs.version }}.spdx.json | |
| homepage-${{ needs.prepare.outputs.version }}.zip.intoto.jsonl | |
| homepage-${{ needs.prepare.outputs.version }}.spdx.json.intoto.jsonl | |
| if-no-files-found: error | |
| release: | |
| name: Create Release and Deploy | |
| needs: [prepare, build] | |
| runs-on: ubuntu-latest | |
| # Release job needs specific permissions to create GitHub releases | |
| permissions: | |
| contents: write # Required to create releases | |
| id-token: write # Required for OIDC | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 | |
| with: | |
| egress-policy: audit | |
| # Checkout master branch to get latest documentation | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| ref: master | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: build-artifacts | |
| path: artifacts/build | |
| - name: Download security artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: security-artifacts | |
| path: artifacts/security | |
| - name: Prepare release artifacts for deployment | |
| run: | | |
| echo "π¦ Extracting release artifact for deployment..." | |
| mkdir -p release-deploy | |
| unzip -q artifacts/build/homepage-${{ needs.prepare.outputs.version }}.zip -d release-deploy | |
| echo "β Release artifact extracted to release-deploy/" | |
| - name: Verify release artifacts exist | |
| run: | | |
| echo "π Verifying all release artifacts exist..." | |
| MISSING=0 | |
| for f in \ | |
| "artifacts/build/homepage-${{ needs.prepare.outputs.version }}.zip" \ | |
| "artifacts/build/homepage-${{ needs.prepare.outputs.version }}.zip.sha256" \ | |
| "artifacts/security/homepage-${{ needs.prepare.outputs.version }}.spdx.json" \ | |
| "artifacts/security/homepage-${{ needs.prepare.outputs.version }}.zip.intoto.jsonl" \ | |
| "artifacts/security/homepage-${{ needs.prepare.outputs.version }}.spdx.json.intoto.jsonl"; do | |
| if [ -f "$f" ]; then | |
| echo " β Found: $f ($(stat -c%s "$f") bytes)" | |
| else | |
| echo " β MISSING: $f" | |
| MISSING=1 | |
| fi | |
| done | |
| if [ "$MISSING" -eq 1 ]; then | |
| echo "β Some release artifacts are missing!" | |
| exit 1 | |
| fi | |
| echo "β All release artifacts verified" | |
| - name: Draft Release Notes | |
| id: release-drafter | |
| uses: release-drafter/release-drafter@563bf132657a13ded0b01fcb723c5a58cdd824e2 # v7.2.1 | |
| with: | |
| version: ${{ needs.prepare.outputs.version }} | |
| tag: ${{ needs.prepare.outputs.version }} | |
| name: Hack23 Homepage ${{ needs.prepare.outputs.version }} | |
| publish: false | |
| prerelease: ${{ needs.prepare.outputs.is_prerelease }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Create GitHub Release with all artifacts | |
| - name: Create GitHub Release | |
| uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 | |
| with: | |
| tag: ${{ needs.prepare.outputs.version }} | |
| name: Hack23 Homepage ${{ needs.prepare.outputs.version }} | |
| body: | | |
| ${{ steps.release-drafter.outputs.body }} | |
| ## π¦ Release Artifacts | |
| - `homepage-${{ needs.prepare.outputs.version }}.zip` - Complete website package (minified HTML/CSS/JS) | |
| - `homepage-${{ needs.prepare.outputs.version }}.zip.sha256` - Checksum for verification | |
| - `homepage-${{ needs.prepare.outputs.version }}.spdx.json` - SBOM (Software Bill of Materials) | |
| - `*.intoto.jsonl` - SLSA Build Provenance Attestations | |
| ## π Security | |
| All artifacts include SLSA Build Provenance attestations and SBOM for supply chain security. | |
| Verify artifacts using the GitHub CLI: | |
| ```bash | |
| # Verify checksum | |
| sha256sum -c homepage-${{ needs.prepare.outputs.version }}.zip.sha256 | |
| # Verify build attestation | |
| gh attestation verify homepage-${{ needs.prepare.outputs.version }}.zip -R Hack23/homepage | |
| ``` | |
| generateReleaseNotes: false | |
| immutableCreate: true | |
| draft: false | |
| prerelease: ${{ needs.prepare.outputs.is_prerelease }} | |
| artifacts: | | |
| artifacts/build/homepage-${{ needs.prepare.outputs.version }}.zip | |
| artifacts/build/homepage-${{ needs.prepare.outputs.version }}.zip.sha256 | |
| artifacts/security/homepage-${{ needs.prepare.outputs.version }}.spdx.json | |
| artifacts/security/homepage-${{ needs.prepare.outputs.version }}.zip.intoto.jsonl | |
| artifacts/security/homepage-${{ needs.prepare.outputs.version }}.spdx.json.intoto.jsonl | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| # Deploy to GitHub Pages as backup | |
| - name: Deploy to GitHub Pages | |
| uses: JamesIves/github-pages-deploy-action@d92aa235d04922e8f08b40ce78cc5442fcfbfa2f # v4.8.0 | |
| with: | |
| folder: release-deploy | |
| target-folder: . | |
| branch: gh-pages | |
| clean: false | |
| commit-message: "chore(release): deploy version ${{ needs.prepare.outputs.version }} to gh-pages" | |
| - name: Create deployment summary | |
| run: | | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| # π Release ${{ needs.prepare.outputs.version }} Deployed Successfully | |
| ## π¦ Artifacts | |
| - Homepage package with minified HTML/CSS/JS | |
| - SBOM (Software Bill of Materials) | |
| - Build provenance attestation | |
| - SBOM attestation | |
| ## π Supply Chain Security | |
| - β SLSA Build Level 3 attestations | |
| - β Provenance verified | |
| - β SBOM generated | |
| ## π Documentation | |
| - β HTML validation report | |
| - β Lighthouse audit reports | |
| - β Accessibility compliance report | |
| - β Security report summary | |
| ## π Deployment | |
| - β GitHub Pages (backup): https://hack23.github.io/homepage/ | |
| - βΉοΈ S3/CloudFront (primary): Deployed via main.yml workflow | |
| View release: https://github.com/Hack23/homepage/releases/tag/${{ needs.prepare.outputs.version }} | |
| EOF |