Skip to content

CI

CI #63

Workflow file for this run

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