Skip to main content
Version: v1

GitHub Actions

GitHub Actions is the continuous integration and delivery service built into GitHub, so your pipeline runs next to the repository and its pull requests. Vortex offers it for projects hosted on GitHub and ships the pipeline in .github/workflows/build-test-deploy.yml and the security checks in .github/workflows/audit.yml.

This page is for the lead or DevOps engineer who maintains a Vortex project's pipeline on GitHub Actions. It shows how to change the parts of the pipeline that are specific to GitHub Actions:

  • Trigger branches, the security audit workflow and the nightly schedule
  • Runner size and disk space
  • Test parallelism
  • Manual deployments and terminal access for debugging
  • Build timeouts, the coverage threshold and test results

The jobs, the database cache and the split of tests across containers work the same on both providers. See the Continuous integration overview. For GitHub's own reference, see the GitHub Actions documentation.

Onboarding​

GitHub Actions onboarding is part of the project setup flow. See Set up continuous integration in the Installation guide and select GitHub Actions.

Maintenance​

The following sections change how the pipeline runs on GitHub Actions. Most of them edit .github/workflows/build-test-deploy.yml or set a repository variable under Settings > Secrets and variables > Actions > Variables.

Update trigger branches​

The workflow builds pushes to the branches listed in on.push.branches and pull requests that target the branches listed in on.pull_request.branches. Both lists live in .github/workflows/build-test-deploy.yml.

The workflow builds direct pushes and merges to these branches:

PatternDescription
productionProduction environment branch
main, masterDefault branches
developIntegration branch for ongoing work
release/**Release preparation branches (for example, release/1.2.3, release/2023-04-17)
hotfix/**Urgent production fixes (for example, hotfix/1.2.4, hotfix/2023-04-17)
project/**Long-lived project branches for large initiatives

It builds pull requests that target these branches:

PatternDescription
All push branches listed earlierSame as push triggers
feature/**New feature branches (for example, feature/login, feature/123-new-ui)
bugfix/**Non-urgent bug fix branches (for example, bugfix/fix-auth, bugfix/456-crash)

The pull request list matches the branch that a pull request targets. A pull request from feature/login into develop builds because develop is listed. The feature/** and bugfix/** entries cover pull requests that target another feature or bugfix branch.

Short-lived feature/** and bugfix/** branches build only through their pull requests, because their pushes aren't in the push list. Tag pushes trigger the workflow too (on.push.tags is '*'), and the deploy job deploys the tag. Pull requests opened from a project/** branch skip every job, because pushes to that branch already build it.

To build both pushes to a branch and the pull requests that target it, add its pattern to both lists. A pattern in on.pull_request.branches alone builds only pull requests that target its branches, as the shipped feature/** and bugfix/** entries do.

To add or remove branches:

  1. In .github/workflows/build-test-deploy.yml, edit the on.push.branches and on.pull_request.branches lists.
  2. In .github/workflows/audit.yml, make the same change: the Security audit workflow carries the same lists.
  3. Confirm that the next push or pull request on an added branch starts both the Database, Build, Test and Deploy and Security audit workflows on the repository's Actions tab.

Security audit workflow​

The security checks live in a second workflow, .github/workflows/audit.yml. It triggers on the same branches and tags as the main workflow, but has no nightly schedule. To start it on demand, use Actions > Security audit > Run workflow.

See Security audit for what it runs and how to make it block merges.

Update nightly database schedule​

A scheduled run downloads a fresh database dump every day at 18:00 UTC (0 18 * * *) and caches it, so the next day's builds start from that dump. Only the database job runs on the schedule, and the lint, build and deploy jobs skip it. The scheduled run also turns off the cache fallback and the front-end build. For a project that stores its database in a container image, it imports the dump into a clean base image, then pushes the result to the registry.

The schedule ships only when the site provisions from a database dump. To change when it runs, update the on.schedule.cron value in .github/workflows/build-test-deploy.yml (cron format, UTC timezone):

schedule:
- cron: '0 18 * * *' # 6 PM UTC daily

Confirm that the next scheduled run of the Database, Build, Test and Deploy workflow starts at the new time and runs only the database job.

Change runner size​

Every job in build-test-deploy.yml and audit.yml runs on ubuntu-latest. For faster builds, switch a job to a larger runner by changing its runs-on value. A larger runner might raise your GitHub bill. The following example sets a larger runner's label:

runs-on: ubuntu-latest-4-cores # Options: ubuntu-latest, ubuntu-latest-4-cores, etc.

Larger runners also come with more disk, which matters for projects whose provisioning is disk-heavy. See Runner disk space.

Runner disk space​

GitHub-hosted runners start with most of their root volume already in use. On 1 measured ubuntu-latest image, preinstalled software occupied 59 GB of a 72 GB volume, leaving the job around 13 GB. Pulled container images, database dumps and every file created during provisioning share what's left.

The jobs report the disk state and save space where they can:

  • The build and database jobs print df -h in a Report disk usage step, so the disk state is on the record for every run.
  • The build job prunes the Docker build cache and dangling images after the stack starts.
  • The build job removes the database dump from the runner as soon as it's copied into the container, so only 1 copy exists during provisioning.

Reclaim the preinstalled toolchains​

The build and database jobs can remove these preinstalled toolchains, which no Vortex job uses, before doing anything else:

  • GHCup, the Haskell toolchain installer
  • Swift
  • PowerShell
  • .NET
  • CodeQL

The set -- line of the Free up disk space on the runner step lists their paths on the runner.

In the preceding measurement, the removal freed about 15 GB in around 14 seconds, so the job's headroom grew from roughly 13 GB to 28 GB.

The removal is destructive to the runner for the rest of the job, so it's off by default. Leave it off on a project whose workflow runs a step that needs any of those toolchains. To turn it on, set the VORTEX_CI_FREE_DISK_SPACE variable to 1 in Settings > Secrets and variables > Actions > Variables.

Confirm that the next build job runs the Free up disk space on the runner step.

note

Every figure here comes from a single runner image, measured once. GitHub reissues that image regularly. For the current numbers, read the Report disk usage output of a recent run. If the reclaimed amount has shrunk, revisit the path list.

Free even more space​

With VORTEX_CI_FREE_DISK_SPACE turned on, the Android SDK is the largest remaining tree, at about 10 GB. Removing it takes around 50 seconds, so it ships commented out in the workflow. To turn it on, uncomment the following line in the Free up disk space on the runner step of both the build and database jobs in .github/workflows/build-test-deploy.yml:

set -- "$@" /host/usr/local/lib/android

Confirm that the Report disk usage output of the next run shows more free space than before.

The removal runs through a container that bind-mounts the runner's root filesystem. The job itself runs in a container and can't reach the host's directories directly. For the same reason, the general-purpose disk-cleanup actions on the Marketplace have no effect in a Vortex job.

When space still runs out​

If a build stops during provisioning without reporting an error, suspect the disk and read the Report disk usage output. To remove the cause, reduce what has to fit: use a sanitized dump or a database container image in place of a full dump. A larger runner also comes with more disk.

Change test parallelism​

To get through the tests faster, add instances to the build job's matrix in .github/workflows/build-test-deploy.yml, which ships with 2 (instance: [0, 1]):

strategy:
matrix:
instance: [0, 1, 2, 3] # Run tests across 4 containers

See Using a matrix for your jobs for more information. Confirm that the next run lists a build job for each instance, from build (0) to build (3).

Every added container that runs Behat also needs a matching pN profile in behat.php. See Continuous integration > Adding more containers for the full procedure. Test parallelism also covers how to balance scenarios across containers and how to move a tool onto a different container.

Manual deployment​

You can deploy from the GitHub UI and skip the lint, database and build jobs. Use it, for example, to redeploy a branch or a pull request (PR) that has already passed. A manual run of the workflow takes these inputs:

InputDescription
deploy_targetBranch name (for example, develop) or PR reference (for example, PR-123, case-insensitive). Left empty, the run builds, tests and deploys the branch selected in Use workflow from
override_dbOverride the existing database in the deployment environment
enable_terminalStart a tmate session in the build job (see Terminal access for debugging)

To deploy a branch or a pull request manually:

  1. On the repository's Actions tab, select the Database, Build, Test and Deploy workflow.
  2. Click Run workflow. A panel opens with the workflow's inputs.
  3. In the Deploy target field, enter the branch name or the PR reference.
  4. Optional: To replace the environment's database, select Override existing database.
  5. Click Run workflow in the panel. The new run appears in the workflow's list of runs.

With deploy_target set, every other job skips and only the deploy job runs. For a PR target, the workflow looks up the pull request's head branch and commit SHA with the GitHub CLI. Confirm that the run shows the deploy job as succeeded and the lint, database and build jobs as skipped.

With override_db checked, the deployment replaces the environment's database with a fresh copy from the production environment. Only the lagoon deployment type acts on it, even when VORTEX_DEPLOY_TYPES lists other types. The override applies only when the deployment redeploys an environment that already exists.

note

A manual deploy can't ship an artifact deployment, because the artifact comes from the build job, which a manual deploy skips.

Terminal access for debugging​

The build job can start a tmate session that lets you connect to the runner over SSH. To start a session:

  1. On the repository's Actions tab, select the Database, Build, Test and Deploy workflow.
  2. Click Run workflow. A panel opens with the workflow's inputs.
  3. In Use workflow from, select the branch to build.
  4. Leave the Deploy target field empty, so the build job runs.
  5. Select Enable terminal session for CI jobs.
  6. Click Run workflow in the panel. The new run appears in the workflow's list of runs.

The session step runs last and starts even when an earlier step failed, so you can inspect the failure in place. Confirm that the Setup tmate session step of each build job prints the SSH connection details.

If nobody connects, the action ends the session 10 minutes after the job's other steps finish. The workflow caps the step at 120 minutes. See action-tmate for more information.

Adjust build timeout​

The shipped workflow caps 6 long-running steps at 30 minutes each:

  • Fetch DB
  • Export DB
  • Deploy DB image
  • Provision site
  • Test with Behat
  • Deploy

If a build times out, raise the timeout-minutes value of the step that hit the limit:

- name: Long running task
run: ./scripts/long-task.sh
timeout-minutes: 60 # Default is 360 (6 hours)

Code coverage threshold​

The build job fails when the line coverage that PHPUnit reports falls below a threshold.

Set the threshold in the VORTEX_CI_CODE_COVERAGE_THRESHOLD variable, under Settings > Secrets and variables > Actions > Variables. It defaults to 90 (percent). Use a whole number: the check multiplies the value in shell integer arithmetic, so a decimal such as 85.5 breaks the step.

The Extract code coverage step also writes the coverage percentage to the job summary. On pull requests, the job posts the coverage summary as a comment. Each new report hides the older comment and posts a fresh comment, so the PR timeline stays clean. To turn the comment off, set VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP to 1.

If you chose Codecov as the code coverage provider at install time, the job also uploads the coverage reports to it when the CODECOV_TOKEN secret is set. A failed upload doesn't fail the job.

Test results​

PHPUnit and Behat results appear as a check on the commit and on its pull requests, and in the summary of the workflow run. Each failing test gets an annotation with its failure details, on the check and in the pull request's Files changed tab. The results come from the JUnit reports that the test tools write, so the check needs no extra setup.

Each parallel build instance publishes its own check, named after its index, such as Test results (0) and Test results (1). To turn publishing off, set VORTEX_CI_TEST_RESULTS_SKIP to 1 in Settings > Secrets and variables > Actions > Variables. The full logs, including the raw reports and screenshots, stay attached to the run as the test-artifacts-<instance> artifacts.