|
| 1 | +name: Release |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + tags: |
| 6 | + - 'v*' |
| 7 | + |
| 8 | +jobs: |
| 9 | + build: |
| 10 | + runs-on: ubuntu-latest |
| 11 | + permissions: |
| 12 | + packages: write |
| 13 | + outputs: |
| 14 | + image: ghcr.io/pprecel/claude-warmup:${{ github.ref_name }} |
| 15 | + steps: |
| 16 | + - name: Checkout |
| 17 | + uses: actions/checkout@v4 |
| 18 | + |
| 19 | + - name: Run npm audit |
| 20 | + run: npm audit --audit-level=high |
| 21 | + |
| 22 | + - name: Log in to GHCR |
| 23 | + uses: docker/login-action@v3 |
| 24 | + with: |
| 25 | + registry: ghcr.io |
| 26 | + username: ${{ github.actor }} |
| 27 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 28 | + |
| 29 | + - name: Build and push |
| 30 | + uses: docker/build-push-action@v6 |
| 31 | + with: |
| 32 | + push: true |
| 33 | + tags: | |
| 34 | + ghcr.io/pprecel/claude-warmup:${{ github.ref_name }} |
| 35 | + ghcr.io/pprecel/claude-warmup:latest |
| 36 | +
|
| 37 | + scan: |
| 38 | + runs-on: ubuntu-latest |
| 39 | + needs: build |
| 40 | + steps: |
| 41 | + - name: Checkout |
| 42 | + uses: actions/checkout@v4 |
| 43 | + |
| 44 | + - name: Scan image for vulnerabilities |
| 45 | + uses: aquasecurity/trivy-action@master |
| 46 | + with: |
| 47 | + image-ref: ghcr.io/pprecel/claude-warmup:${{ github.ref_name }} |
| 48 | + format: table |
| 49 | + exit-code: 1 |
| 50 | + severity: CRITICAL,HIGH |
| 51 | + trivyignores: .trivyignore |
| 52 | + |
| 53 | + release: |
| 54 | + runs-on: ubuntu-latest |
| 55 | + needs: [build, scan] |
| 56 | + permissions: |
| 57 | + contents: write |
| 58 | + steps: |
| 59 | + - name: Checkout |
| 60 | + uses: actions/checkout@v4 |
| 61 | + with: |
| 62 | + fetch-depth: 0 |
| 63 | + |
| 64 | + - name: Package k8s manifests |
| 65 | + run: | |
| 66 | + # Update deployment image to the release tag |
| 67 | + sed "s|ghcr.io/pprecel/claude-warmup:latest|ghcr.io/pprecel/claude-warmup:${{ github.ref_name }}|g" \ |
| 68 | + k8s/deployment.yaml > /tmp/deployment.yaml |
| 69 | + cp /tmp/deployment.yaml k8s/deployment.yaml |
| 70 | +
|
| 71 | + # Bundle all non-secret manifests into a single file |
| 72 | + kubectl kustomize /dev/null 2>/dev/null || true |
| 73 | + cat k8s/rbac.yaml \ |
| 74 | + k8s/network-policy.yaml \ |
| 75 | + k8s/redis.yaml \ |
| 76 | + k8s/service.yaml \ |
| 77 | + k8s/deployment.yaml \ |
| 78 | + | sed "s|---||g" \ |
| 79 | + | awk 'NF' \ |
| 80 | + | sed '1!{/^---/!{/^apiVersion/i --- |
| 81 | + }}' \ |
| 82 | + > /tmp/manifests.yaml |
| 83 | +
|
| 84 | + echo "---" >> /tmp/manifests.yaml |
| 85 | + echo "# NOTE: Create the redis-credentials secret separately before applying." >> /tmp/manifests.yaml |
| 86 | + echo "# See k8s/secret.yaml.example" >> /tmp/manifests.yaml |
| 87 | +
|
| 88 | + - name: Generate release notes |
| 89 | + id: notes |
| 90 | + run: | |
| 91 | + # Get previous tag |
| 92 | + PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${{ github.ref_name }}$" | head -1) |
| 93 | +
|
| 94 | + if [ -z "$PREV_TAG" ]; then |
| 95 | + COMMIT_RANGE="HEAD" |
| 96 | + RANGE_LABEL="all commits" |
| 97 | + else |
| 98 | + COMMIT_RANGE="${PREV_TAG}..${{ github.ref_name }}" |
| 99 | + RANGE_LABEL="since ${PREV_TAG}" |
| 100 | + fi |
| 101 | +
|
| 102 | + # Categorise commits |
| 103 | + FEATURES=$(git log $COMMIT_RANGE --oneline --no-merges | grep -iE "^[a-f0-9]+ (add|feat|new)" || true) |
| 104 | + FIXES=$(git log $COMMIT_RANGE --oneline --no-merges | grep -iE "^[a-f0-9]+ (fix|bug|patch)" || true) |
| 105 | + SECURITY=$(git log $COMMIT_RANGE --oneline --no-merges | grep -iE "^[a-f0-9]+ (sec|cve|vuln|auth|rbac|policy)" || true) |
| 106 | + DOCS=$(git log $COMMIT_RANGE --oneline --no-merges | grep -iE "^[a-f0-9]+ (doc|readme|claude)" || true) |
| 107 | + CI=$(git log $COMMIT_RANGE --oneline --no-merges | grep -iE "^[a-f0-9]+ (ci|build|workflow|pipeline|trivy|scan|test|integr)" || true) |
| 108 | + OTHER=$(git log $COMMIT_RANGE --oneline --no-merges | grep -viE "^[a-f0-9]+ (add|feat|new|fix|bug|patch|sec|cve|vuln|auth|rbac|policy|doc|readme|claude|ci|build|workflow|pipeline|trivy|scan|test|integr)" || true) |
| 109 | +
|
| 110 | + format_section() { |
| 111 | + local title="$1" |
| 112 | + local commits="$2" |
| 113 | + if [ -n "$commits" ]; then |
| 114 | + echo "## $title" |
| 115 | + echo "$commits" | while read -r line; do |
| 116 | + sha=$(echo "$line" | awk '{print $1}') |
| 117 | + msg=$(echo "$line" | cut -d' ' -f2-) |
| 118 | + echo "- ${msg} (\`${sha}\`)" |
| 119 | + done |
| 120 | + echo "" |
| 121 | + fi |
| 122 | + } |
| 123 | +
|
| 124 | + NOTES=$(cat <<NOTES |
| 125 | + ## What's included in ${{ github.ref_name }} |
| 126 | +
|
| 127 | + **Image:** \`ghcr.io/pprecel/claude-warmup:${{ github.ref_name }}\` |
| 128 | + **Manifests:** See \`manifests.yaml\` attached to this release |
| 129 | +
|
| 130 | + Changes ${RANGE_LABEL}: |
| 131 | +
|
| 132 | + $(format_section "Features" "$FEATURES") |
| 133 | + $(format_section "Bug Fixes" "$FIXES") |
| 134 | + $(format_section "Security" "$SECURITY") |
| 135 | + $(format_section "Documentation" "$DOCS") |
| 136 | + $(format_section "CI/CD" "$CI") |
| 137 | + $(format_section "Other" "$OTHER") |
| 138 | +
|
| 139 | + ## Deploying this release |
| 140 | +
|
| 141 | + \`\`\`bash |
| 142 | + # 1. Create the Redis secret (first time only) |
| 143 | + kubectl create secret generic redis-credentials \\ |
| 144 | + --from-literal=redis-password=\$(openssl rand -base64 24) |
| 145 | +
|
| 146 | + # 2. Apply all manifests |
| 147 | + kubectl apply -f manifests.yaml |
| 148 | +
|
| 149 | + # 3. Verify |
| 150 | + kubectl rollout status deployment/redis |
| 151 | + kubectl rollout status deployment/claude-warmup |
| 152 | + \`\`\` |
| 153 | + NOTES |
| 154 | + ) |
| 155 | +
|
| 156 | + # Write to file for gh release create |
| 157 | + echo "$NOTES" > /tmp/release-notes.md |
| 158 | + cat /tmp/release-notes.md |
| 159 | +
|
| 160 | + - name: Create GitHub release |
| 161 | + env: |
| 162 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 163 | + run: | |
| 164 | + gh release create ${{ github.ref_name }} \ |
| 165 | + --title "Release ${{ github.ref_name }}" \ |
| 166 | + --notes-file /tmp/release-notes.md \ |
| 167 | + /tmp/manifests.yaml#manifests.yaml |
0 commit comments