diff --git a/.github/workflows/nightly-builds.yml b/.github/workflows/nightly-builds.yml new file mode 100644 index 000000000..f072fca98 --- /dev/null +++ b/.github/workflows/nightly-builds.yml @@ -0,0 +1,95 @@ +name: Nightly Checks + +on: + schedule: + # Runs at 09Z (3am MDT) + - cron: "0 9 * * 2" + + # Allow a manual run + workflow_dispatch: + + # Run if we modify the workflow + push: + branches: + - main + paths: + - .github/workflows/nightly-builds.yml + - .github/workflows/unstable-builds.yml + pull_request: + paths: + - .github/workflows/nightly-builds.yml + - .github/workflows/unstable-builds.yml + +jobs: + Builds: + uses: ./.github/workflows/unstable-builds.yml + + Report: + name: Report + needs: Builds + if: failure() || github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - name: Download logs + uses: actions/download-artifact@v4 + with: + path: /tmp/workspace/logs + + - name: Grab log files + run: | + cp /tmp/workspace/logs/log-*/*.log . || true + touch tests-nightly.log build.log linkchecker.log + + - name: Report failures + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const title = "Nightly build is failing"; + const workflow_url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; + body = `The [Nightly workflow](${workflow_url}) is failing.\n`; + + if ('${{ needs.Builds.outputs.tests_result }}' === 'failure') { + const test_log = fs.readFileSync('tests-nightly.log', 'utf8').substring(0, 21000); + body += `The tests failed.\nLog:\n
${test_log}
`; + } + if ('${{ needs.Builds.outputs.docs_result }}' === 'failure') { + const build_log = fs.readFileSync('build.log', 'utf8').substring(0, 21000); + const linkchecker = fs.readFileSync('linkchecker.log', 'utf8').substring(0, 21000); + body += `The documentation build failed.\nLog:\n
${build_log}
`; + body += `\nLinkchecker output:\n
${linkchecker}
`; + } + + // See if we have an existing issue + const items = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + creator: 'github-actions[bot]' + }); + const existing = items.data.filter(i => i.title === title); + + params = { + owner: context.repo.owner, + repo: context.repo.repo, + body: body, + title: title, + labels: ['Type: Maintenance'] + }; + + // On PRs, avoid actually issuing an API request, since we don't have permission + if ( context.eventName === 'pull_request' ) { + github.hook.wrap('request', (request, options) => { return {}; }) + } + + if (existing.length === 0) { + console.log('Creating new issue.') + github.rest.issues.create(params) + } else { + params.issue_number = existing[0].number; + console.log(`Updating existing issue: ${params.issue_number}`) + github.rest.issues.update(params) + } diff --git a/.github/workflows/run-unstable-pr.yml b/.github/workflows/run-unstable-pr.yml new file mode 100644 index 000000000..859c94a77 --- /dev/null +++ b/.github/workflows/run-unstable-pr.yml @@ -0,0 +1,16 @@ +name: PR Unstable Builds + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + +jobs: + Builds: + if: | + ((github.event.action == 'labeled' && github.event.label.name == 'nightly-ci') || + contains(github.event.pull_request.labels.*.name, 'nightly-ci')) + uses: ./.github/workflows/unstable-builds.yml diff --git a/.github/workflows/unstable-builds.yml b/.github/workflows/unstable-builds.yml new file mode 100644 index 000000000..fb94400a4 --- /dev/null +++ b/.github/workflows/unstable-builds.yml @@ -0,0 +1,93 @@ +name: Unstable Builds + +on: + workflow_call: + outputs: + tests_result: + description: "Result from running tests" + value: ${{ jobs.Tests.outputs.result }} + docs_result: + description: "Result from running docs" + value: ${{ jobs.Docs.outputs.result }} + +jobs: + Tests: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.tests.outcome }} + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 150 + fetch-tags: true + + - name: Assemble test requirements + run: | + echo git+https://github.com/pydata/xarray@main#egg=xarray > ci/extra_requirements.txt + + - name: Install using PyPI + uses: Unidata/MetPy/.github/actions/install-pypi@main + with: + need-extras: true + type: test + version-file: Prerelease + python-version: 3.12 + need-cartopy: 'false' + + - name: Run tests + id: tests + uses: Unidata/MetPy/.github/actions/run-tests@main + with: + run-doctests: false + key: nightly + upload-coverage: false + pytest-args: '' + + - name: Upload test log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: log-nightly-tests + path: tests-nightly.log + retention-days: 5 + + Docs: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.build.outcome }} + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 150 + fetch-tags: true + + - name: Assemble doc requirements + run: | + echo git+https://github.com/pydata/xarray@main#egg=xarray > ci/extra_requirements.txt + + - name: Install using PyPI + uses: Unidata/MetPy/.github/actions/install-pypi@main + with: + type: doc + version-file: Prerelease + python-version: 3.12 + + - name: Build docs + id: build + uses: Unidata/MetPY/.github/actions/build-docs@main + with: + run-linkchecker: true + key: nightly + make-targets: '' + + - name: Upload build log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: log-nightly-docs + path: | + build.log + linkchecker.log + retention-days: 5