Repository navigation
CI #42
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: CI | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| schedule: | |
| - cron: "0 0 * * 5" | |
| workflow_dispatch: | |
| permissions: | |
| actions: read | |
| contents: read | |
| jobs: | |
| validation: | |
| if: >- | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name != github.repository || | |
| (github.event.pull_request.head.ref != 'main' && github.event.pull_request.head.ref != 'develop') | |
| uses: ./.github/workflows/validation.yml | |
| aocc: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-aocc.yml | |
| armflang: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-armflang.yml | |
| gfortran: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-gfortran.yml | |
| flang: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-flang.yml | |
| ifort: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-ifort.yml | |
| ifx: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-ifx.yml | |
| lfortran: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-lfortran.yml | |
| nvfortran: | |
| needs: [validation] | |
| uses: ./.github/workflows/ci-nvfortran.yml | |
| two-invocations: | |
| name: sequential installations in one job | |
| needs: [validation] | |
| runs-on: ubuntu-24.04 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| - name: Install Dependencies | |
| run: npm ci | |
| - name: Restore dist-verify | |
| uses: nick-fields/retry@v4 | |
| with: | |
| timeout_minutes: 5 | |
| max_attempts: 3 | |
| retry_wait_seconds: 10 | |
| command: gh run download ${{ github.run_id }} --name generated-dist-verify --dir dist-verify | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Restore dist-tests | |
| uses: nick-fields/retry@v4 | |
| with: | |
| timeout_minutes: 5 | |
| max_attempts: 3 | |
| retry_wait_seconds: 10 | |
| command: gh run download ${{ github.run_id }} --name generated-dist-tests --dir dist-tests | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Install GFortran 14 | |
| id: first | |
| uses: ./ | |
| with: | |
| compiler: gfortran | |
| version: "14" | |
| timeout-minutes: 30 | |
| - name: Verify first installation env vars (baseline) | |
| env: | |
| FIRST_VERSION: ${{ steps.first.outputs.version }} | |
| FIRST_FC: ${{ steps.first.outputs.fc }} | |
| FIRST_CC: ${{ steps.first.outputs.cc }} | |
| FIRST_CXX: ${{ steps.first.outputs.cxx }} | |
| run: | | |
| echo "First installation: version=$FIRST_VERSION FC=$FIRST_FC CC=$FIRST_CC CXX=$FIRST_CXX F77=$F77 F90=$F90" | |
| # Env vars must be set and match the step outputs | |
| if [ "$FC" != "$FIRST_FC" ]; then | |
| echo "::error::FC env var (\$FC='$FC') does not match first output ('$FIRST_FC')" | |
| exit 1 | |
| fi | |
| if [ "$CC" != "$FIRST_CC" ]; then | |
| echo "::error::CC env var (\$CC='$CC') does not match first output ('$FIRST_CC')" | |
| exit 1 | |
| fi | |
| if [ "$CXX" != "$FIRST_CXX" ]; then | |
| echo "::error::CXX env var (\$CXX='$CXX') does not match first output ('$FIRST_CXX')" | |
| exit 1 | |
| fi | |
| # F77 and F90 aliases should also match FC | |
| if [ "$F77" != "$FIRST_FC" ]; then | |
| echo "::error::F77 env var (\$F77) does not match first FC ('$FIRST_FC')" | |
| exit 1 | |
| fi | |
| if [ "$F90" != "$FIRST_FC" ]; then | |
| echo "::error::F90 env var (\$F90) does not match first FC ('$FIRST_FC')" | |
| exit 1 | |
| fi | |
| echo "First installation env vars: OK (FC, CC, CXX, F77, F90)" | |
| # PATH ordering: first compiler's bin dir must be present | |
| first_bin=$(dirname "$(dirname "$FIRST_FC")") | |
| path_first=$(echo "$PATH" | tr ':' '\n' | grep -n "^${first_bin}" | head -1 | cut -d: -f1) | |
| if [ -z "$path_first" ]; then | |
| echo "::error::First compiler bin dir ($first_bin) not found on PATH" | |
| exit 1 | |
| fi | |
| echo "First installation PATH: OK (bin at position $path_first)" | |
| # Actual runtime: $FC --version must report the first version | |
| actual_version=$($FC --version | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) | |
| first_major=$(echo "$FIRST_VERSION" | grep -oE '^[0-9]+') | |
| if [ -z "$actual_version" ]; then | |
| echo "::error::Could not extract version from \$FC --version output" | |
| exit 1 | |
| fi | |
| if ! echo "$actual_version" | grep -q "^${first_major}\."; then | |
| echo "::error::Runtime \$FC reports version '$actual_version' but expected '${first_major}.*' (first installation)" | |
| exit 1 | |
| fi | |
| echo "First installation runtime: OK (reports version $actual_version)" | |
| - name: Install GFortran 15 (different version) | |
| id: second | |
| uses: ./ | |
| with: | |
| compiler: gfortran | |
| version: "15" | |
| timeout-minutes: 30 | |
| - name: Verify second installation took precedence | |
| env: | |
| FIRST_VERSION: ${{ steps.first.outputs.version }} | |
| SECOND_VERSION: ${{ steps.second.outputs.version }} | |
| FIRST_FC: ${{ steps.first.outputs.fc }} | |
| SECOND_FC: ${{ steps.second.outputs.fc }} | |
| FIRST_CC: ${{ steps.first.outputs.cc }} | |
| SECOND_CC: ${{ steps.second.outputs.cc }} | |
| FIRST_CXX: ${{ steps.first.outputs.cxx }} | |
| SECOND_CXX: ${{ steps.second.outputs.cxx }} | |
| run: | | |
| echo "First installation: version=$FIRST_VERSION FC=$FIRST_FC CC=$FIRST_CC CXX=$FIRST_CXX F77=$F77 F90=$F90" | |
| echo "Second installation: version=$SECOND_VERSION FC=$SECOND_FC CC=$SECOND_CC CXX=$SECOND_CXX F77=$F77 F90=$F90" | |
| if [ "$FIRST_VERSION" = "$SECOND_VERSION" ]; then | |
| echo "::error::Two different versions resolved to the same version." | |
| exit 1 | |
| fi | |
| # 1. Env-var overwrite: $FC, $CC, $CXX, $F77, $F90 must point to the SECOND installation | |
| if [ "$FC" != "$SECOND_FC" ]; then | |
| echo "::error::FC env var (\$FC='$FC') does not match second output ('$SECOND_FC')" | |
| exit 1 | |
| fi | |
| if [ "$CC" != "$SECOND_CC" ]; then | |
| echo "::error::CC env var (\$CC='$CC') does not match second output ('$SECOND_CC')" | |
| exit 1 | |
| fi | |
| if [ "$CXX" != "$SECOND_CXX" ]; then | |
| echo "::error::CXX env var (\$CXX='$CXX') does not match second output ('$SECOND_CXX')" | |
| exit 1 | |
| fi | |
| # F77 and F90 aliases should also point to the second installation | |
| if [ "$F77" != "$SECOND_FC" ]; then | |
| echo "::error::F77 env var (\$F77) does not match second FC ('$SECOND_FC')" | |
| exit 1 | |
| fi | |
| if [ "$F90" != "$SECOND_FC" ]; then | |
| echo "::error::F90 env var (\$F90) does not match second FC ('$SECOND_FC')" | |
| exit 1 | |
| fi | |
| # Also verify they are NOT still pointing to the first installation | |
| if [ "$FC" = "$FIRST_FC" ] || [ "$CC" = "$FIRST_CC" ] || [ "$CXX" = "$FIRST_CXX" ] || [ "$F77" = "$FIRST_FC" ] || [ "$F90" = "$FIRST_FC" ]; then | |
| echo "::error::One or more env vars still point to the FIRST installation" | |
| exit 1 | |
| fi | |
| echo "Env-var overwrite: OK (FC, CC, CXX, F77, F90 all replaced)" | |
| # 2. PATH ordering: second compiler's bin dir must precede first's | |
| first_bin=$(dirname "$(dirname "$FIRST_FC")") | |
| second_bin=$(dirname "$(dirname "$SECOND_FC")") | |
| path_first=$(echo "$PATH" | tr ':' '\n' | grep -n "^${first_bin}" | head -1 | cut -d: -f1) | |
| path_second=$(echo "$PATH" | tr ':' '\n' | grep -n "^${second_bin}" | head -1 | cut -d: -f1) | |
| if [ -z "$path_second" ]; then | |
| echo "::error::Second compiler bin dir not found on PATH" | |
| exit 1 | |
| fi | |
| if [ -n "$path_first" ] && [ "$path_first" -lt "$path_second" ]; then | |
| echo "::error::First compiler bin ($first_bin) precedes second ($second_bin) on PATH" | |
| exit 1 | |
| fi | |
| echo "PATH ordering: OK (second compiler precedes first)" | |
| # 3. Actual runtime: $FC --version must report the second version | |
| actual_version=$($FC --version | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) | |
| echo "Actual $FC --version reports full version: $actual_version" | |
| second_major=$(echo "$SECOND_VERSION" | grep -oE '^[0-9]+') | |
| if [ -z "$actual_version" ]; then | |
| echo "::error::Could not extract version from \$FC --version output" | |
| exit 1 | |
| fi | |
| if ! echo "$actual_version" | grep -q "^${second_major}\."; then | |
| echo "::error::Runtime $FC reports version '$actual_version' but expected '${second_major}.*' (second installation)" | |
| exit 1 | |
| fi | |
| echo "Runtime compiler: OK (reports second installation version)" | |
| echo "Sequential installation test passed: second installation took precedence." | |
| - name: Run integration tests for final state | |
| run: npm run integration-test | |
| timeout-minutes: 5 | |
| canary-report: | |
| name: Weekly Canary Report | |
| needs: | |
| [ | |
| validation, | |
| aocc, | |
| armflang, | |
| gfortran, | |
| flang, | |
| ifort, | |
| ifx, | |
| lfortran, | |
| nvfortran, | |
| two-invocations, | |
| ] | |
| if: always() && github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - name: Create issue for failed integration jobs | |
| uses: actions/github-script@v9 | |
| env: | |
| AOCC: ${{ needs.aocc.result }} | |
| ARMFLANG: ${{ needs.armflang.result }} | |
| GFORTRAN: ${{ needs.gfortran.result }} | |
| FLANG: ${{ needs.flang.result }} | |
| IFORT: ${{ needs.ifort.result }} | |
| IFX: ${{ needs.ifx.result }} | |
| LFORTRAN: ${{ needs.lfortran.result }} | |
| NVFORTRAN: ${{ needs.nvfortran.result }} | |
| TWO_INVOCATIONS: ${{ needs.two-invocations.result }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| with: | |
| script: | | |
| const jobs = { | |
| 'AOCC': process.env.AOCC, | |
| 'ARM Flang': process.env.ARMFLANG, | |
| 'GFortran': process.env.GFORTRAN, | |
| 'Flang': process.env.FLANG, | |
| 'Ifort (Classic)': process.env.IFORT, | |
| 'Ifx': process.env.IFX, | |
| 'LFortran': process.env.LFORTRAN, | |
| 'NVFORTRAN': process.env.NVFORTRAN, | |
| 'Sequential installations': process.env.TWO_INVOCATIONS, | |
| }; | |
| const failed = Object.entries(jobs) | |
| .filter(([, result]) => result === 'failure') | |
| .map(([name]) => `- ${name}`); | |
| if (failed.length === 0) { | |
| console.log('All canary jobs passed.'); | |
| return; | |
| } | |
| const runUrl = process.env.RUN_URL; | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `Weekly Canary: ${failed.length} integration job(s) failed`, | |
| body: [ | |
| '## Weekly Canary Failure Report', | |
| '', | |
| 'The weekly CI canary run detected failures in the following integration jobs:', | |
| '', | |
| ...failed, | |
| '', | |
| 'These failures typically indicate upstream breakage — installer URLs, package repositories, Homebrew formulae, hosted runner images, or `latest` resolution may have changed without a code modification.', | |
| '', | |
| `[View the workflow run](${runUrl}) for details on the failed jobs.`, | |
| ].join('\n'), | |
| }); |