CI #49
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: | |
| # Deliberately no `branches: [ master ]` push trigger: PRs already run smoke-test/typecheck | |
| # before merge, and potential race conditions between parallel PRs will be caught on nightly/release. | |
| push: | |
| tags: [ "v*.*.*" ] | |
| pull_request: | |
| schedule: | |
| # Daily, off-peak minute rather than top-of-hour: GitHub Actions schedules cluster at round | |
| # minutes/top-of-hour across all repos, causing extra queueing delay. | |
| - cron: "17 3 * * *" | |
| workflow_dispatch: | |
| jobs: | |
| check-nightly-needed: | |
| # Always runs (cheap, no checkout) regardless of trigger, rather than being restricted to | |
| # the schedule trigger itself -- if this job were skipped by its own `if:` on other | |
| # triggers, jobs `needs`-ing it would then also get auto-skipped (GitHub Actions treats | |
| # "skipped" as not-succeeded for needs purposes), which would incorrectly block PRs, | |
| # tag pushes, and manual workflow_dispatch runs too. | |
| runs-on: ubuntu-latest | |
| outputs: | |
| changed: ${{ steps.check.outputs.changed }} | |
| steps: | |
| - name: Check whether master has new commits since the last nightly attempt | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [ "${{ github.event_name }}" != "schedule" ]; then | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Compares against nightly-attempted (moved unconditionally as nightly's first | |
| # step), not the nightly release tag (which only moves on success) -- otherwise a | |
| # failing build would get retried every night against the same unchanged, | |
| # still-broken commit until either it started passing or new commits landed. | |
| # Comparing against the attempted marker means a failure is tried once, then left | |
| # alone until master actually moves. | |
| attempted_sha="$(gh api "repos/${{ github.repository }}/git/refs/tags/nightly-attempted" --jq .object.sha 2>/dev/null || true)" | |
| if [ "$attempted_sha" = "$GITHUB_SHA" ]; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| smoke-test: | |
| # Skip entirely on a schedule tick that doesn't need a nightly build (see | |
| # check-nightly-needed above) -- no point spinning up the Blender matrix for nothing. | |
| # Always runs for PRs, tag pushes, and manual dispatch. | |
| if: | | |
| github.event_name != 'schedule' || needs.check-nightly-needed.outputs.changed == 'true' | |
| needs: [ check-nightly-needed ] | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # 4.1 is the minimum version declared in __init__.py's bl_info; 5.2 is the latest | |
| # confirmed-working version (see CLAUDE.md's "Known Blender version support"). | |
| # Testing just these two boundaries is deliberate, not an oversight: the fix that | |
| # extended support from 4.5 to 5.2 is uniform across the whole range (confirmed via | |
| # local podman runs against 4.5/5.0/5.1 too, not just CI), so exercising every | |
| # intermediate version on every CI run isn't worth the ongoing cost. | |
| blender-version: ["4.1", "5.2"] | |
| container: | |
| image: blenderkit/headless-blender:blender-${{ matrix.blender-version }}-stable | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run test suite in headless Blender | |
| run: /home/headless/blender/blender --background --python-exit-code 1 --python tests/run_tests.py | |
| typecheck: | |
| # See smoke-test above: skip on a schedule tick that doesn't need a nightly build. | |
| if: | | |
| github.event_name != 'schedule' || needs.check-nightly-needed.outputs.changed == 'true' | |
| needs: [ check-nightly-needed ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - run: pip install pyright fake-bpy-module-4.5 | |
| - run: pyright --pythonpath "$(command -v python3)" | |
| pep8: | |
| # See smoke-test above: skip on a schedule tick that doesn't need a nightly build. | |
| if: | | |
| github.event_name != 'schedule' || needs.check-nightly-needed.outputs.changed == 'true' | |
| needs: [ check-nightly-needed ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - run: pip install pycodestyle | |
| - run: make pep8 | |
| nightly: | |
| # Only runs on a schedule or manual trigger -- not on every push, to avoid rebuilding | |
| # and republishing a nightly prerelease for every single commit -- only once smoke-test | |
| # and typecheck have passed, and (for the schedule trigger specifically) only if master | |
| # has moved since the last nightly build, so an unchanged repo doesn't get a no-op | |
| # rebuild+republish every day. Manual workflow_dispatch runs always rebuild, on the | |
| # assumption that a human explicitly asking for it wants a fresh build regardless. | |
| if: | | |
| (github.event_name == 'schedule' && needs.check-nightly-needed.outputs.changed == 'true') || | |
| github.event_name == 'workflow_dispatch' | |
| needs: [ smoke-test, typecheck, pep8, check-nightly-needed ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set up Git repository | |
| uses: actions/checkout@v4 | |
| - name: Mark this commit as attempted | |
| # Deliberately the first step, before anything that could fail: this must move | |
| # regardless of whether the rest of the job succeeds, so a failure isn't retried | |
| # every night against the same unchanged commit (see check-nightly-needed above). | |
| run: | | |
| git tag -f nightly-attempted | |
| git push --force origin nightly-attempted | |
| - name: Create build dir | |
| run: mkdir build | |
| - name: Compile Manual | |
| # TODO: run `make build/jediacademy_plugins_doc.pdf` instead, | |
| # but that requires pdflatex to be installed | |
| uses: dante-ev/latex-action@latest | |
| with: | |
| working_directory: build | |
| root_file: ../jediacademy_plugins_doc.tex | |
| - name: Package plugin | |
| run: make build/jediacademy.zip | |
| - name: Extract latest commit message | |
| id: notes | |
| run: | | |
| { | |
| echo "body<<NIGHTLY_NOTES_EOF" | |
| git log -1 --format=%B | |
| echo "NIGHTLY_NOTES_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| # we must move the nightly tag so the right source code gets attached to the release | |
| # (in case users accidentally download that instead of the packaged add-on) | |
| - name: Update nightly tag | |
| uses: actions/github-script@v6 | |
| with: | |
| github-token: ${{secrets.GITHUB_TOKEN}} | |
| script: | | |
| github.rest.git.updateRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: "tags/nightly", | |
| sha: context.sha | |
| }) | |
| - name: Release nightly | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| name: nightly | |
| tag_name: nightly | |
| prerelease: true | |
| body: | | |
| Automatic pre-release build. | |
| Latest change: | |
| ${{ steps.notes.outputs.body }} | |
| The jediacademy.zip file can be installed using the button in Blender's add-on preferences. | |
| Please consult jediacademy_plugins_doc.pdf for instructions on how to use the plugin. | |
| files: | | |
| build/jediacademy_plugins_doc.pdf | |
| build/jediacademy.zip | |
| release: | |
| # Only runs for vX.Y.Z tag pushes, and only once smoke-test and typecheck have passed. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [ smoke-test, typecheck, pep8 ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set up Git repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify tag is on master | |
| # .claude/skills/release/tag_release.sh's check 1 runs this same command before ever | |
| # creating the tag -- if this line changes, check that script's equivalent check too. | |
| run: git merge-base --is-ancestor "$GITHUB_SHA" origin/master | |
| - name: Create build dir | |
| run: mkdir build | |
| - name: Compile Manual | |
| uses: dante-ev/latex-action@latest | |
| with: | |
| working_directory: build | |
| root_file: ../jediacademy_plugins_doc.tex | |
| - name: Package plugin | |
| run: make build/jediacademy.zip | |
| - name: Extract release notes from tagged commit | |
| id: notes | |
| run: | | |
| { | |
| echo "body<<RELEASE_NOTES_EOF" | |
| git log -1 --format=%B "$GITHUB_SHA" | |
| echo "RELEASE_NOTES_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| name: ${{ github.ref_name }} | |
| body: | | |
| ${{ steps.notes.outputs.body }} | |
| The jediacademy.zip file can be installed using the button in Blender's add-on preferences. | |
| Please consult jediacademy_plugins_doc.pdf for instructions on how to use the plugin. | |
| files: | | |
| build/jediacademy_plugins_doc.pdf | |
| build/jediacademy.zip |