Fix Linux x64 release build and add fix-release notes for new issue folder #967
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
| name: PR Validation | |
| on: | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| checks: write | |
| jobs: | |
| validate: | |
| name: Validate PR | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: "Guardrail: internal/workflow PRs must not bump versions" | |
| run: | | |
| set -euo pipefail | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| HEAD_SHA="${{ github.event.pull_request.head.sha }}" | |
| changed_files=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" || true) | |
| if [ -z "$changed_files" ]; then | |
| echo "No changed files detected; skipping internal commit guardrail." | |
| exit 0 | |
| fi | |
| # If anything outside these paths changed, this is not an internal-only PR. | |
| non_internal=$(echo "$changed_files" | grep -Ev '^(\.github/|scripts/|docs/|website/|uat-repos/|TestData/|artifacts/|assets/)' || true) | |
| if [ -n "$non_internal" ]; then | |
| echo "Non-internal files changed; internal commit guardrail not applicable." | |
| exit 0 | |
| fi | |
| echo "Internal-only PR detected; enforcing non-version-bumping commit types." | |
| # Versionize bumps on conventional commits like feat/fix (and breaking changes). | |
| messages=$(git log --format=%B "$BASE_SHA".."$HEAD_SHA" || true) | |
| if echo "$messages" | grep -Eqi '^(feat|fix|perf)(\([^)]*\))?:|BREAKING CHANGE:'; then | |
| echo "❌ ERROR: This PR only changes internal/workflow/tooling files, but includes version-bumping commits (feat/fix/perf or BREAKING CHANGE)." | |
| echo "Please use non-bumping types like chore:, ci:, docs:, style:, refactor: for internal changes." | |
| exit 1 | |
| fi | |
| echo "✓ Commit messages OK for internal-only PR." | |
| - name: Check if validation is needed | |
| id: filter | |
| run: | | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| HEAD_SHA="${{ github.event.pull_request.head.sha }}" | |
| CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" || true) | |
| if [ -z "$CHANGED_FILES" ]; then | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| echo "No changed files detected; running validation" | |
| exit 0 | |
| fi | |
| NON_IGNORED=$(echo "$CHANGED_FILES" | grep -Ev '^(docs/|website/|\.github/agents/|\.github/skills/|\.github/copilot-instructions\.md$|\.github/gh-cli-instructions\.md$|\.github/pull_request_template\.md$)' || true) | |
| if [ -z "$NON_IGNORED" ]; then | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| echo "Only docs/website/agent-instructions changed; skipping validation" | |
| else | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| echo "Non-ignored files changed; running validation" | |
| fi | |
| - name: Snapshot integrity guardrail | |
| if: steps.filter.outputs.changed == 'true' | |
| run: scripts/validate-snapshot-changes.sh --base-ref "${{ github.event.pull_request.base.sha }}" --head-ref "${{ github.event.pull_request.head.sha }}" | |
| - name: Shell test (timeout wrapper) | |
| if: steps.filter.outputs.changed == 'true' | |
| run: src/tests/shell/test_with_timeout_test.sh | |
| - name: Shell test (analyze chat) | |
| if: steps.filter.outputs.changed == 'true' | |
| run: src/tests/shell/analyze_chat_test.sh | |
| - name: Setup .NET | |
| if: steps.filter.outputs.changed == 'true' | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| global-json-file: src/global.json | |
| - name: Restore dependencies | |
| if: steps.filter.outputs.changed == 'true' | |
| run: dotnet restore src/tfplan2md.slnx | |
| - name: Restore .NET tools | |
| if: steps.filter.outputs.changed == 'true' | |
| run: dotnet tool restore | |
| - name: Check formatting | |
| if: steps.filter.outputs.changed == 'true' | |
| run: dotnet format src/tfplan2md.slnx --verify-no-changes --verbosity diagnostic | |
| - name: Build | |
| if: steps.filter.outputs.changed == 'true' | |
| run: dotnet build src/tfplan2md.slnx --no-restore --configuration Release | |
| - name: Prepare Docker test image | |
| if: steps.filter.outputs.changed == 'true' | |
| run: | | |
| chmod +x scripts/prepare-test-image.sh | |
| scripts/prepare-test-image.sh | |
| - name: Test | |
| id: test | |
| if: steps.filter.outputs.changed == 'true' | |
| run: | | |
| chmod +x scripts/test-with-timeout.sh | |
| scripts/test-with-timeout.sh --timeout-seconds 120 -- dotnet test --project src/tests/Oocx.TfPlan2Md.TUnit/ --configuration Release --results-directory ${{ github.workspace }}/TestResults -- --report-trx --coverage --coverage-output coverage.cobertura.xml --coverage-output-format cobertura | |
| - name: Generate coverage report | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| run: | | |
| dotnet tool run reportgenerator \ | |
| -reports:"./TestResults/**/coverage.cobertura.xml" \ | |
| -targetdir:"./TestResults/coverage-report" \ | |
| -reporttypes:Html | |
| - name: Check coverage override label | |
| id: coverage_override | |
| if: steps.filter.outputs.changed == 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const labels = context.payload.pull_request.labels?.map(label => label.name) ?? []; | |
| core.setOutput('active', labels.includes('coverage-override')); | |
| - name: Enforce coverage thresholds | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| run: | | |
| report_path="./TestResults/coverage.cobertura.xml" | |
| if [ ! -f "$report_path" ]; then | |
| echo "❌ ERROR: Cobertura report not found for coverage enforcement." | |
| exit 1 | |
| fi | |
| dotnet run --project src/tools/Oocx.TfPlan2Md.CoverageEnforcer/Oocx.TfPlan2Md.CoverageEnforcer.csproj -- \ | |
| --report "$report_path" \ | |
| --line-threshold 84.48 \ | |
| --branch-threshold 72.80 \ | |
| --summary-output ./TestResults/coverage-summary.md \ | |
| --report-link "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ | |
| --override-active "${{ steps.coverage_override.outputs.active }}" | |
| - name: Publish coverage summary | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| run: cat ./TestResults/coverage-summary.md >> "$GITHUB_STEP_SUMMARY" | |
| - name: Verify tests ran | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| run: | | |
| # Check if TestResults directory exists and contains .trx files | |
| if [ ! -d "./TestResults" ] || [ -z "$(ls -A ./TestResults/*.trx 2>/dev/null)" ]; then | |
| echo "❌ ERROR: No test results found! Tests may not have run." | |
| exit 1 | |
| fi | |
| # Count the test results (safe because we verified .trx files exist above) | |
| test_count=$(grep -r "outcome" ./TestResults/*.trx | wc -l) | |
| echo "✓ Test results found: $test_count test(s) executed" | |
| if [ "$test_count" -eq 0 ]; then | |
| echo "❌ ERROR: Test results file exists but contains 0 tests" | |
| exit 1 | |
| fi | |
| - name: Publish test results | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| uses: EnricoMi/publish-unit-test-result-action@v2 | |
| with: | |
| files: ./TestResults/*.trx | |
| check_name: 'Test Results' | |
| - name: Upload coverage report | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: ./TestResults/coverage-report | |
| - name: Upload Cobertura report | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-cobertura | |
| path: ./TestResults/**/coverage.cobertura.xml | |
| - name: Update coverage badge and history | |
| id: coverage_badge_history | |
| if: steps.filter.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository && (success() || steps.coverage_override.outputs.active == 'true') | |
| run: | | |
| dotnet run --project src/tools/Oocx.TfPlan2Md.CoverageEnforcer/Oocx.TfPlan2Md.CoverageEnforcer.csproj -- \ | |
| --report ./TestResults/coverage.cobertura.xml \ | |
| --line-threshold 84.48 \ | |
| --branch-threshold 72.80 \ | |
| --badge-output ./TestResults/coverage-badge.svg \ | |
| --history-output ./TestResults/coverage-history.json \ | |
| --commit-sha "${{ github.event.pull_request.head.sha }}" \ | |
| --timestamp "${{ github.event.pull_request.updated_at }}" \ | |
| --override-active "${{ steps.coverage_override.outputs.active }}" | |
| - name: Upload coverage badge and history | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-badge-history | |
| path: | | |
| ./TestResults/coverage-badge.svg | |
| ./TestResults/coverage-history.json | |
| - name: Post coverage summary comment | |
| if: steps.filter.outputs.changed == 'true' && (success() || failure()) && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const marker = '<!-- coverage-summary -->'; | |
| const body = fs.readFileSync('./TestResults/coverage-summary.md', 'utf8'); | |
| const { owner, repo } = context.repo; | |
| const issue_number = context.payload.pull_request.number; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number, | |
| }); | |
| const existing = comments.find(comment => comment.body && comment.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, | |
| repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number, | |
| body, | |
| }); | |
| } | |
| - name: Setup Node | |
| if: steps.filter.outputs.changed == 'true' | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 20 | |
| - name: Generate comprehensive demo report | |
| if: steps.filter.outputs.changed == 'true' | |
| run: | | |
| mkdir -p artifacts | |
| dotnet run --project src/Oocx.TfPlan2Md/Oocx.TfPlan2Md.csproj -- examples/comprehensive-demo/plan.json --principals examples/comprehensive-demo/demo-principals.json --output artifacts/comprehensive-demo.md | |
| - name: Lint comprehensive demo markdown | |
| if: steps.filter.outputs.changed == 'true' | |
| run: npx markdownlint-cli2 artifacts/comprehensive-demo.md | |
| - name: Check for vulnerable packages | |
| if: steps.filter.outputs.changed == 'true' | |
| run: | | |
| dotnet list package --vulnerable --include-transitive 2>&1 | tee vulnerability-report.txt | |
| if grep -q "has the following vulnerable packages" vulnerability-report.txt; then | |
| echo "::error::Vulnerable packages detected!" | |
| exit 1 | |
| fi | |
| - name: Skip validation (no relevant files changed) | |
| if: steps.filter.outputs.changed == 'false' | |
| run: echo "✅ PR validation skipped (docs/website/agent-only changes)" |