name: Release on: push: tags: - "v*" permissions: contents: write # Create GitHub Release packages: write # Push to ghcr.io id-token: write # OIDC for provenance attestation jobs: # ───────────────────────────────────────────── # Build & push Docker image to GHCR # ───────────────────────────────────────────── docker: name: Build & Push Docker Image runs-on: ubuntu-latest outputs: image-digest: ${{ steps.push.outputs.digest }} steps: - uses: actions/checkout@v4 - name: Extract version from tag id: version run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push id: push uses: docker/build-push-action@v5 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: | ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }} ghcr.io/${{ github.repository }}:latest cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:cache cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:cache,mode=max labels: | org.opencontainers.image.title=Veylant IA Gateway org.opencontainers.image.description=AI Governance Proxy for Enterprise org.opencontainers.image.version=${{ steps.version.outputs.VERSION }} org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} - name: Trivy — container scan (must pass for release) uses: aquasecurity/trivy-action@master with: image-ref: ghcr.io/${{ github.repository }}:${{ github.ref_name }} format: sarif output: trivy-release.sarif exit-code: "1" severity: CRITICAL,HIGH ignore-unfixed: true - name: Upload Trivy results uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: trivy-release.sarif # ───────────────────────────────────────────── # Package Helm chart # ───────────────────────────────────────────── helm: name: Package & Push Helm Chart runs-on: ubuntu-latest needs: [docker] steps: - uses: actions/checkout@v4 - name: Set up Helm uses: azure/setup-helm@v4 with: version: v3.16.0 - name: Log in to GHCR OCI registry (Helm) run: | echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io \ --username ${{ github.actor }} \ --password-stdin - name: Package Helm chart run: | helm package deploy/helm/veylant-proxy \ --version "${{ github.ref_name }}" \ --app-version "${{ github.ref_name }}" - name: Push Helm chart to GHCR OCI run: | helm push veylant-proxy-*.tgz \ oci://ghcr.io/${{ github.repository_owner }}/charts # ───────────────────────────────────────────── # Create GitHub Release with CHANGELOG notes # ───────────────────────────────────────────── release: name: Create GitHub Release runs-on: ubuntu-latest needs: [docker, helm] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Extract release notes from CHANGELOG.md id: changelog run: | # Extract section for this version from CHANGELOG.md VERSION="${{ github.ref_name }}" VERSION_NO_V="${VERSION#v}" # Extract content between this version header and the next one NOTES=$(awk "/^## \[${VERSION_NO_V}\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md) if [ -z "$NOTES" ]; then NOTES="See [CHANGELOG.md](./CHANGELOG.md) for full release notes." fi # Write to file to handle multiline content echo "$NOTES" > release_notes.md echo "Release notes extracted ($(wc -l < release_notes.md) lines)" - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: name: "Veylant IA ${{ github.ref_name }}" body_path: release_notes.md draft: false prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} generate_release_notes: false files: | CHANGELOG.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}