Bump next from 14.2.35 to 16.2.0 in /sample-app in the npm_and_yarn group across 1 directory #1
Workflow file for this run
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
| # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json | |
| name: FinOps Cost Gate | |
| on: | |
| pull_request: | |
| paths: | |
| - '**/*.tf' | |
| - '**/*.bicep' | |
| - '**/*.json' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| security-events: write | |
| env: | |
| MONTHLY_BUDGET: ${{ vars.FINOPS_MONTHLY_BUDGET || '500' }} | |
| INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} | |
| jobs: | |
| cost-estimate: | |
| name: FinOps — IaC Cost Estimation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Infracost | |
| uses: infracost/actions/setup@v3 | |
| with: | |
| api-key: ${{ env.INFRACOST_API_KEY }} | |
| - name: Generate Infracost baseline | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1 | |
| git checkout ${{ github.event.pull_request.base.ref }} | |
| infracost breakdown --path . --format json --out-file /tmp/infracost-base.json || true | |
| git checkout ${{ github.sha }} | |
| - name: Generate Infracost diff | |
| run: | | |
| infracost diff \ | |
| --path . \ | |
| --compare-to /tmp/infracost-base.json \ | |
| --format json \ | |
| --out-file /tmp/infracost-diff.json || \ | |
| infracost breakdown \ | |
| --path . \ | |
| --format json \ | |
| --out-file /tmp/infracost-diff.json | |
| - name: Post PR comment | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| infracost comment github \ | |
| --path /tmp/infracost-diff.json \ | |
| --repo ${{ github.repository }} \ | |
| --pull-request ${{ github.event.pull_request.number }} \ | |
| --github-token ${{ github.token }} \ | |
| --behavior update | |
| - name: Convert cost report to SARIF | |
| id: sarif | |
| run: | | |
| cat > convert-cost.js << 'SCRIPT' | |
| const fs = require('fs'); | |
| const budget = parseInt(process.argv[2], 10); | |
| const inputFile = process.argv[3]; | |
| const outputFile = process.argv[4]; | |
| let data; | |
| try { | |
| data = JSON.parse(fs.readFileSync(inputFile, 'utf8')); | |
| } catch { | |
| console.log('No Infracost output found, creating empty SARIF'); | |
| data = { totalMonthlyCost: '0', projects: [] }; | |
| } | |
| const monthlyCost = parseFloat(data.totalMonthlyCost || '0'); | |
| const results = []; | |
| const rules = [ | |
| { | |
| id: 'budget-overspend', | |
| shortDescription: { text: 'Estimated cost exceeds monthly budget' }, | |
| fullDescription: { text: `Estimated monthly cost exceeds the configured budget of $${budget}` }, | |
| help: { text: `Reduce resource costs to stay within the $${budget}/month budget`, markdown: `Reduce resource costs to stay within the **$${budget}/month** budget. Consider:\n- Smaller SKUs\n- Reserved instances\n- Spot instances\n- Removing unused resources` }, | |
| defaultConfiguration: { level: 'error' }, | |
| properties: { tags: ['finops', 'cost'] } | |
| }, | |
| { | |
| id: 'cost-increase', | |
| shortDescription: { text: 'Resource cost increase detected' }, | |
| fullDescription: { text: 'A resource change increases estimated monthly cost' }, | |
| help: { text: 'Review the cost increase and confirm it is justified', markdown: 'Review the cost increase and confirm it is **justified** by the feature requirements.' }, | |
| defaultConfiguration: { level: 'warning' }, | |
| properties: { tags: ['finops', 'cost'] } | |
| } | |
| ]; | |
| if (monthlyCost > budget) { | |
| results.push({ | |
| ruleId: 'budget-overspend', | |
| level: 'error', | |
| message: { text: `Estimated monthly cost $${monthlyCost.toFixed(2)} exceeds budget $${budget}` }, | |
| locations: [{ | |
| physicalLocation: { | |
| artifactLocation: { uri: '.' }, | |
| region: { startLine: 1 } | |
| } | |
| }], | |
| partialFingerprints: { primaryLocationLineHash: `budget-${monthlyCost.toFixed(0)}` } | |
| }); | |
| } | |
| for (const project of (data.projects || [])) { | |
| for (const diff of (project.diff?.resources || [])) { | |
| if (parseFloat(diff.monthlyCost || '0') > 0) { | |
| results.push({ | |
| ruleId: 'cost-increase', | |
| level: 'warning', | |
| message: { text: `${diff.name}: +$${parseFloat(diff.monthlyCost).toFixed(2)}/month` }, | |
| locations: [{ | |
| physicalLocation: { | |
| artifactLocation: { uri: project.path || '.' }, | |
| region: { startLine: 1 } | |
| } | |
| }], | |
| partialFingerprints: { primaryLocationLineHash: `cost-${diff.name}` } | |
| }); | |
| } | |
| } | |
| } | |
| const sarif = { | |
| '$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json', | |
| version: '2.1.0', | |
| runs: [{ | |
| tool: { driver: { name: 'finops-cost-gate', rules } }, | |
| results, | |
| automationDetails: { id: 'finops-finding/' } | |
| }] | |
| }; | |
| fs.writeFileSync(outputFile, JSON.stringify(sarif, null, 2)); | |
| console.log(`Wrote ${results.length} findings to ${outputFile}`); | |
| process.exit(monthlyCost > budget ? 1 : 0); | |
| SCRIPT | |
| node convert-cost.js "${{ env.MONTHLY_BUDGET }}" /tmp/infracost-diff.json finops-results.sarif | |
| continue-on-error: true | |
| - name: Upload FinOps SARIF | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() && hashFiles('finops-results.sarif') != '' | |
| with: | |
| sarif_file: finops-results.sarif | |
| category: finops-finding/ | |
| - name: Budget gate | |
| run: | | |
| if [ "${{ steps.sarif.outcome }}" = "failure" ]; then | |
| echo "::error::Estimated monthly cost exceeds budget threshold of \$${{ env.MONTHLY_BUDGET }}" | |
| exit 1 | |
| fi |