|
| 1 | +name: Build |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + tags: [ "v*.*.*" ] |
| 6 | + branches: [ "main", "build-pipeline" ] |
| 7 | + pull_request: |
| 8 | + branches: [ "main" ] |
| 9 | + merge_group: |
| 10 | + schedule: |
| 11 | + - cron: '45 1 * * *' |
| 12 | + |
| 13 | +# Declare default permissions as read-only. |
| 14 | +permissions: read-all |
| 15 | + |
| 16 | +concurrency: |
| 17 | + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} |
| 18 | + cancel-in-progress: true |
| 19 | + |
| 20 | +env: |
| 21 | + BUILD_ID: build-${{ github.run_id }} |
| 22 | + REGISTRY: ghcr.io/${{ github.repository_owner }}/interpolar |
| 23 | + |
| 24 | +jobs: |
| 25 | + build-image: |
| 26 | + runs-on: ubuntu-24.04 |
| 27 | + permissions: |
| 28 | + packages: write |
| 29 | + strategy: |
| 30 | + matrix: |
| 31 | + image: [ r-env ] |
| 32 | + include: |
| 33 | + - image: r-env |
| 34 | + context: . |
| 35 | + file: Dockerfile_R |
| 36 | + steps: |
| 37 | + - name: Harden Runner |
| 38 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 39 | + with: |
| 40 | + egress-policy: audit |
| 41 | + |
| 42 | + - name: Checkout |
| 43 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 44 | + |
| 45 | + - name: Login to GHCR |
| 46 | + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 |
| 47 | + with: |
| 48 | + registry: ghcr.io |
| 49 | + username: ${{ github.repository_owner }} |
| 50 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 51 | + |
| 52 | + - name: Collect Info |
| 53 | + id: info |
| 54 | + run: | |
| 55 | + date="$(git log -1 --date="iso-strict-local" --format="%cd")" |
| 56 | + echo "commit_date=${date}" >>"${GITHUB_OUTPUT}" |
| 57 | +
|
| 58 | + - name: Generate Container Image Metadata |
| 59 | + id: meta |
| 60 | + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 |
| 61 | + with: |
| 62 | + images: ${{ env.REGISTRY }}/${{ matrix.image }} |
| 63 | + labels: | |
| 64 | + org.opencontainers.image.created=${{ steps.info.outputs.commit_date }} |
| 65 | + annotations: | |
| 66 | + org.opencontainers.image.created=${{ steps.info.outputs.commit_date }} |
| 67 | +
|
| 68 | + - name: Build and Push Container Image |
| 69 | + id: push |
| 70 | + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 |
| 71 | + with: |
| 72 | + context: ${{ matrix.context }} |
| 73 | + file: ${{ matrix.file }} |
| 74 | + platforms: linux/amd64 |
| 75 | + push: true |
| 76 | + tags: ${{ env.REGISTRY }}/${{ matrix.image }}:${{ env.BUILD_ID }} |
| 77 | + labels: ${{ steps.meta.outputs.labels }} |
| 78 | + annotations: ${{ steps.meta.outputs.annotations }} |
| 79 | + |
| 80 | + - name: Output Image Digest |
| 81 | + id: digest |
| 82 | + run: echo "${{ matrix.image }}-digest=${{ steps.push.outputs.digest }}" >>"${GITHUB_OUTPUT}" |
| 83 | + outputs: |
| 84 | + r-env-digest: ${{ steps.digest.outputs.r-env-digest }} |
| 85 | + |
| 86 | + scan-image: |
| 87 | + needs: [ build-image ] |
| 88 | + runs-on: ubuntu-24.04 |
| 89 | + strategy: |
| 90 | + matrix: |
| 91 | + image: [ r-env ] |
| 92 | + permissions: |
| 93 | + security-events: write |
| 94 | + steps: |
| 95 | + - name: Harden Runner |
| 96 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 97 | + with: |
| 98 | + egress-policy: audit |
| 99 | + |
| 100 | + - name: Run Trivy Vulnerability Scanner |
| 101 | + uses: aquasecurity/trivy-action@76071ef0d7ec797419534a183b498b4d6366cf37 # 0.31.0 |
| 102 | + with: |
| 103 | + image-ref: ${{ env.REGISTRY }}/${{ matrix.image }}:${{ env.BUILD_ID }} |
| 104 | + format: sarif |
| 105 | + output: trivy-results.sarif |
| 106 | + severity: 'CRITICAL,HIGH' |
| 107 | + timeout: '15m0s' |
| 108 | + |
| 109 | + - name: Upload Trivy Scan Results to GitHub Security Tab |
| 110 | + uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 |
| 111 | + with: |
| 112 | + sarif_file: trivy-results.sarif |
| 113 | + |
| 114 | + - name: Generate SBOM |
| 115 | + uses: aquasecurity/trivy-action@76071ef0d7ec797419534a183b498b4d6366cf37 # 0.31.0 |
| 116 | + with: |
| 117 | + image-ref: ${{ env.REGISTRY }}/${{ matrix.image }}:${{ env.BUILD_ID }} |
| 118 | + format: cyclonedx |
| 119 | + output: sbom-trivy.json |
| 120 | + scan-type: image |
| 121 | + |
| 122 | + - name: Upload Trivy SBOM |
| 123 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 124 | + with: |
| 125 | + name: ${{ matrix.image }}-sbom-trivy |
| 126 | + path: sbom-trivy.json |
| 127 | + if-no-files-found: error |
| 128 | + retention-days: 7 |
| 129 | + |
| 130 | + tag-image: |
| 131 | + if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork |
| 132 | + # `test` must succeed before finally tagging (i.e., publishing) the image |
| 133 | + needs: [ build-image, scan-image ] |
| 134 | + runs-on: ubuntu-24.04 |
| 135 | + permissions: |
| 136 | + packages: write |
| 137 | + strategy: |
| 138 | + matrix: |
| 139 | + image: [ r-env ] |
| 140 | + env: |
| 141 | + DIGEST: ${{ needs.build-image.outputs[format('{0}-digest', matrix.image )]}} |
| 142 | + |
| 143 | + steps: |
| 144 | + - name: Harden Runner |
| 145 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 146 | + with: |
| 147 | + egress-policy: audit |
| 148 | + |
| 149 | + - name: Install SLSA Verifier |
| 150 | + uses: slsa-framework/slsa-verifier/actions/installer@ea584f4502babc6f60d9bc799dbbb13c1caa9ee6 # v2.7.1 |
| 151 | + |
| 152 | + - name: Install crane |
| 153 | + uses: iarekylew00t/crane-installer@af22986e01a08365e8b29e45e5a336c7995c111b # v4.0.0 |
| 154 | + |
| 155 | + - name: Login to GHCR |
| 156 | + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 |
| 157 | + with: |
| 158 | + registry: ghcr.io |
| 159 | + username: ${{ github.repository_owner }} |
| 160 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 161 | + |
| 162 | + - name: Generate Container Image Metadata |
| 163 | + id: meta |
| 164 | + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 |
| 165 | + with: |
| 166 | + images: ghcr.io/${{ github.repository_owner }}/interpolar/${{ matrix.image }} |
| 167 | + tags: | |
| 168 | + type=schedule |
| 169 | + type=ref,event=branch |
| 170 | + type=ref,event=pr |
| 171 | + type=semver,pattern={{version}} |
| 172 | + type=semver,pattern={{major}}.{{minor}} |
| 173 | + type=raw,value=latest,enable={{is_default_branch}} |
| 174 | +
|
| 175 | + - name: Retag and Push Container Image |
| 176 | + run: | |
| 177 | + while read -r tag; do |
| 178 | + echo "${tag}" | cut -d: -f2 | xargs crane tag "${REGISTRY}/${{ matrix.image }}@${DIGEST}" |
| 179 | + done <<<"${{ steps.meta.outputs.tags }}" |
| 180 | +
|
| 181 | + sign-image: |
| 182 | + needs: [ build-image, scan-image ] |
| 183 | + runs-on: ubuntu-24.04 |
| 184 | + permissions: |
| 185 | + packages: write |
| 186 | + id-token: write |
| 187 | + strategy: |
| 188 | + matrix: |
| 189 | + image: [ r-env ] |
| 190 | + env: |
| 191 | + DIGEST: ${{ needs.build-image.outputs[format('{0}-digest', matrix.image )]}} |
| 192 | + steps: |
| 193 | + - name: Harden Runner |
| 194 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 195 | + with: |
| 196 | + egress-policy: audit |
| 197 | + |
| 198 | + - name: Install cosign |
| 199 | + uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 |
| 200 | + |
| 201 | + - name: Download SBOM |
| 202 | + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 |
| 203 | + with: |
| 204 | + name: ${{ matrix.image }}-sbom-trivy |
| 205 | + path: . |
| 206 | + |
| 207 | + - name: Login to GHCR |
| 208 | + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 |
| 209 | + with: |
| 210 | + registry: ghcr.io |
| 211 | + username: ${{ github.repository_owner }} |
| 212 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 213 | + |
| 214 | + - name: Sign Image |
| 215 | + env: |
| 216 | + IMAGE_REF: ${{ env.REGISTRY }}/${{ matrix.image }}@${{ env.DIGEST }} |
| 217 | + run: cosign sign --yes "${IMAGE_REF}" |
| 218 | + |
| 219 | + - name: Attest Image SBOM |
| 220 | + env: |
| 221 | + IMAGE_REF: ${{ env.REGISTRY }}/${{ matrix.image }}@${{ env.DIGEST }} |
| 222 | + run: cosign attest --yes --predicate "sbom-trivy.json" --type cyclonedx "${IMAGE_REF}" |
| 223 | + |
| 224 | + attest-image: |
| 225 | + needs: [ build-image ] |
| 226 | + permissions: |
| 227 | + actions: read # for detecting the Github Actions environment. |
| 228 | + id-token: write |
| 229 | + packages: write # for uploading attestations. |
| 230 | + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0 |
| 231 | + with: |
| 232 | + image: ghcr.io/${{ github.repository_owner }}/interpolar/${{ matrix.image }} |
| 233 | + digest: ${{ needs.build-image.outputs[format('{0}-digest', matrix.image)] }} |
| 234 | + registry-username: ${{ github.repository_owner }} |
| 235 | + secrets: |
| 236 | + registry-password: ${{ secrets.GITHUB_TOKEN }} |
| 237 | + strategy: |
| 238 | + matrix: |
| 239 | + image: [ r-env ] |
| 240 | + |
| 241 | + create-release: |
| 242 | + runs-on: ubuntu-24.04 |
| 243 | + steps: |
| 244 | + - name: Harden Runner |
| 245 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 246 | + with: |
| 247 | + egress-policy: audit |
| 248 | + |
| 249 | + - name: Checkout |
| 250 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 251 | + |
| 252 | + - name: Insert Version |
| 253 | + env: |
| 254 | + COMMIT_TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }} |
| 255 | + REF_NAME: ${{ github.event_name == 'pull_request' && format('pr-{}', github.event.number) || github.ref_name }} |
| 256 | + run: | |
| 257 | + raw_tag="${COMMIT_TAG#v}" |
| 258 | + image_tag="${raw_tag:-${REF_NAME}}" |
| 259 | + echo "INTERPOLAR_VERSION=${tag}" >VERSION.env |
| 260 | + sed -i -E 's|(ghcr\.io/${{ github.repository_owner }}/interpolar/[[:alnum:]_.-]+):latest|\1:'"${image_tag}"'|g' docker-compose.yml |
| 261 | +
|
| 262 | + - name: Create Release Archives |
| 263 | + id: create |
| 264 | + run: | |
| 265 | + zip -r ".github/release/interpolar-${GITHUB_REF_NAME}.zip" -x@.releaseignore . |
| 266 | + tar -czf ".github/release/interpolar-${GITHUB_REF_NAME}.tar.gz" --exclude-from=.releaseignore . |
| 267 | +
|
| 268 | + - name: Collect Checksums |
| 269 | + id: checksums |
| 270 | + working-directory: .github/release |
| 271 | + run: | |
| 272 | + # shellcheck disable=SC2016 |
| 273 | + sha256sum "interpolar-${GITHUB_REF_NAME}.zip" >"interpolar-${GITHUB_REF_NAME}.zip.sha256" |
| 274 | + sha256sum "interpolar-${GITHUB_REF_NAME}.tar.gz" >"interpolar-${GITHUB_REF_NAME}.tar.gz.sha256" |
| 275 | + checksums=$(cat <<EOF |
| 276 | + { |
| 277 | + "zip": "$(cat "interpolar-${GITHUB_REF_NAME}.zip.sha256" | base64 -w0)", |
| 278 | + "tar.gz": "$(cat "interpolar-${GITHUB_REF_NAME}.tar.gz.sha256" | base64 -w0)" |
| 279 | + } |
| 280 | + EOF |
| 281 | + ) |
| 282 | + echo "checksums-b64=${checksums}" >>"${GITHUB_OUTPUT}" |
| 283 | + files="$(echo "${checksums}" | jq -c keys)" |
| 284 | + echo "files=${files}" >>"${GITHUB_OUTPUT}" |
| 285 | +
|
| 286 | + - name: Upload Release Archives |
| 287 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 288 | + with: |
| 289 | + name: release-archives |
| 290 | + path: | |
| 291 | + .github/release/*.zip |
| 292 | + .github/release/*.tar.gz |
| 293 | + .github/release/*.sha256 |
| 294 | + if-no-files-found: error |
| 295 | + retention-days: 7 |
| 296 | + |
| 297 | + outputs: |
| 298 | + checksums-b64: ${{ steps.checksums.outputs.checksums-b64 }} |
| 299 | + files: ${{ steps.checksums.outputs.files }} |
| 300 | + |
| 301 | + attest-release: |
| 302 | + needs: [ create-release ] |
| 303 | + permissions: |
| 304 | + actions: read |
| 305 | + contents: write |
| 306 | + id-token: write |
| 307 | + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 |
| 308 | + strategy: |
| 309 | + matrix: |
| 310 | + file: ${{ fromJson(needs.create-release.outputs.files) }} |
| 311 | + with: |
| 312 | + base64-subjects: ${{ fromJson(needs.create-release.outputs.checksums-b64)[matrix.file] }} |
| 313 | + upload-assets: false |
| 314 | + |
| 315 | + publish-release: |
| 316 | + needs: [ attest-release, tag-image ] |
| 317 | + runs-on: ubuntu-24.04 |
| 318 | + if: github.ref_type == 'tag' |
| 319 | + permissions: |
| 320 | + contents: write |
| 321 | + steps: |
| 322 | + - name: Harden Runner |
| 323 | + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 |
| 324 | + with: |
| 325 | + egress-policy: audit |
| 326 | + |
| 327 | + - name: Recognize Prerelease |
| 328 | + id: prerelease |
| 329 | + # In SemVer 2.0, a prerelease version is always indicated by the presence of a hyphen |
| 330 | + run: | |
| 331 | + if [[ "${GITHUB_REF_NAME}" == *-* ]]; then |
| 332 | + echo "is-prerelease=true" >>"${GITHUB_OUTPUT}" |
| 333 | + fi |
| 334 | +
|
| 335 | + - name: Download Release Archives |
| 336 | + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 |
| 337 | + with: |
| 338 | + name: release-archives |
| 339 | + path: . |
| 340 | + |
| 341 | + - name: Download Provenance |
| 342 | + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 |
| 343 | + with: |
| 344 | + pattern: "interpolar-*.intoto.jsonl" |
| 345 | + merge-multiple: true |
| 346 | + path: . |
| 347 | + |
| 348 | + - name: Release |
| 349 | + uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3 |
| 350 | + with: |
| 351 | + files: | |
| 352 | + interpolar-*.zip |
| 353 | + interpolar-*.tar.gz |
| 354 | + interpolar-*.sha256 |
| 355 | + draft: true |
| 356 | + prerelease: ${{ steps.prerelease.outputs.is-prerelease }} |
0 commit comments