GitHub Actions Style Guide
We use GitHub Actions to run automated checks, deployment pipelines etc. Each team maintains their own workflows but some common standards should be observed to ensure maintainability and prevent hard-to-diagnose issues. These standards are outlined here.
Creating Workflows
Workflows live under .github/workflows/. To make it easy to understand what
they are for they should follow certain naming patterns. If you are creating a
workflow for your project (where a project is typically a top-level directory
in the monorepo), name your workflow file project-<my-project>.yml, e.g.
project-infra.yml and give it a suitable name, i.e.:
name: Project - Infra
Prefixing with project/"Project" makes sure workflows appear in a sensible
order in .github/workflows/ and the Actions tab on GitHub.
Ideally also add a description: that explains what your workflow does.
Triggering Workflows
Basic test workflows typically run on pull request and pushes to main. Unless
you have specific requirements for when to run your workflow consider
triggering it on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- '.github/workflows/my-workflow.yml'
- ...
- '!**/*.md'
push:
branches: [main]
paths:
- '.github/workflows/my-workflow.yml'
- ...
- '!**/*.md'
I.e.:
-
Run it on opened, reopened and synchronized PRs as well as pushes to
main. -
Run it when the workflow file itself or relevant files in your project (...) change.
-
Do not run it if only documentation has changed (unless you're deploying documentation somewhere).
Many workflows also benefit from being able to be triggered manually, e.g. if
they are expensive to run or used to trigger certain events like releases. This
requires adding workflow_dispatch trigger.
Workflow Inputs
When triggering a workflow via workflow_dispatch or workflow_call, you are
often going to need to specify certain input variables. Here are some related
tips:
-
For
workflow_dispatch, usechoicefor enum-like inputs to create dropdowns in the GitHub UI, e.g.:workflow_dispatch: inputs: environment: description: Environment to deploy to type: choice options: - devel - integration - prod required: true -
Because GitHub does not do this automatically for
workflow_call, add avalidate-inputsjob to sanity-check inputs. E.g. for a "choice" input (which would be of typestringforworkflow_callbecausechoiceis not supported):validate-inputs: name: Validate correctness of workflow inputs runs-on: ubuntu-latest steps: - name: Validate environment run: | case "${{ inputs.environment }}" in devel|integration|prod) ;; *) echo "::error::Invalid environment: ${{ inputs.environment }}"; exit 1 ;; esac
Make sure to use the ::error::... pattern so that GitHub displays this as
an actual error and not just log output. All other jobs should then depend on
this one if they use any of the inputs.
Workflow Runners
For now just run all your jobs on ubuntu-latest until we decide otherwise.
Workflow Jobs
Workflows run one or more jobs. Give these easy to understand names, usually these will be sufficient:
-
(optional)
build: Use this if you want to separate building from testing. -
test: Run tests, optionally split intounit-tests/component-tests/integration-tests. -
docker: Build and push Docker images.
Composite Actions
We provide some custom GitHub actions that you can reuse inside the
monorepo under ./github/actions/. Some tips for writing these:
- If something in an action is variable in the sense that we may want to update
it periodically, but should always be the same across callers (e.g. a package
version), do not expose it as an input but rather use the following pattern:
using: composite steps: - name: Set constants id: constants shell: bash run: | echo "my_package_version=1.2.0" >> "$GITHUB_OUTPUT"
Freestanding Actions
We also implement some GitHub actions as freestanding repos. The latter is
currently restricted to lyceum-tech/set-up-lyceum which you must use to check
out our repo (i.e. do NOT use actions/checkout directly). You will typically
want to use it as follows, adjust if you need to tweak other available options:
- name: Set up Lyceum
uses: lyceum-tech/set-up-lyceum@v1
with:
github_vars: ${{ toJSON(vars) }}
github_secrets: ${{ toJSON(secrets) }}
uv_python_packages: my-package-1,my-package2
Where my-package-1 and my-package-1 are Python packages from the monorepo
you want to install. You can also set this to none if you do not need any or
leave it out in which case all of them will be installed.
Third-Party Actions
Using arbitrary third-party actions is dangerous and basically asking for a supply-chain attack. We try to prevent this in two ways:
-
We only allow whitelisted actions. Note that this is currently not enforced because this feature is not available for private repositories so please be careful when adding external actions.
-
We require non-Lyceum actions to always be pinned to a specific commit instead of just pecifying a version number, but with a comment indicating the corresponding version, e.g.:
astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6
Workflow Environments/Secrets
Environment variables and secrets for use inside workflows should be defined at
the organization level (ask the administrator if you need to
add or modify them). As a convention prefix environment variables with LYC_
if possible. If you use the lyceum-tech/setup-up-lyceum action as described
above these will be automatically made available in your
environment.
Known Bugs/Quirks and Workarounds
GitHub Action bugs that are not related to Copilot are almost never addressed by Microsoft and so there is a long and growing list. We detail a couple that we have encountered and what to do about them here:
github.workspaceis unreliable: This variable does usually not point where you think it does in containerized workflows. To work around this, especially when using this inside a composite action, simply do the following once and then use$GITHUB_WORKFSPACE(which is already correct) orenv.GITHUB_WORKSPACEin the rest of the action:- name: Set GITHUB_WORKSPACE shell: bash run: | echo "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" >> $GITHUB_ENV