10 KiB
Run Tests
Language-agnostic composite action that executes a project's test suite with optional setup and failure-artifact upload. Designed for Forgejo Actions (works on GitHub Actions too).
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
command |
yes | — | Test command to execute (multiline allowed) |
setup |
no | "" |
Setup commands run before tests (install deps, build, etc.) |
working-directory |
no | . |
Directory to run setup and command in |
shell |
no | bash |
Shell for setup and command |
artifacts-path |
no | "" |
Glob of paths to upload on failure. Empty disables upload. |
artifacts-name |
no | test-artifacts |
Name of the uploaded artifact bundle |
Behavior
- Runs
setupif provided. Non-zero exit fails the job immediately. - Runs
command. Exit code is captured but not yet propagated. - If
commandfailed andartifacts-pathis set, uploads matching files. - Re-exits with failure if
commandfailed.
This ordering ensures artifacts are always uploaded on failure, even though the job ultimately fails.
Workflow shape
This Forgejo instance is hosted on a subpath (bitfreedom.net/code/...), and Forgejo's action resolver does not handle URL-form uses: references against subpath instances. The workflows below therefore:
- Run inside a
container:so the toolchain is predictable across runners. - Clone the repository under test manually with the job token, in place of
actions/checkout. - Clone this actions repo into a local directory and reference the action with
uses: ./.actions/run-tests, in place ofuses: https://.../run-tests@v1.
The manual checkout uses github.event.pull_request.head.sha, so it assumes on: [pull_request]. For on: push, swap that for github.sha (or github.event.head_commit.id). The same workflow files run on GitHub unchanged.
Usage
Each example below is a complete workflow — drop it into .forgejo/workflows/test.yml (or .github/workflows/test.yml) and adjust runs-on to match a label one of your runners is registered with. See Choosing a runner below.
Node.js
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: docker-amd64
container:
image: node:20-slim
steps:
- name: Install system deps
run: |
apt-get update
apt-get install -y --no-install-recommends git ca-certificates
rm -rf /var/lib/apt/lists/*
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
setup: npm ci
command: npm test
artifacts-path: |
coverage/**
junit.xml
Python (pytest)
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: docker-amd64
container:
image: python:3.12-slim
steps:
- name: Install system deps
run: |
apt-get update
apt-get install -y --no-install-recommends git ca-certificates
rm -rf /var/lib/apt/lists/*
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
setup: |
python -m pip install --upgrade pip
pip install -e .[test]
command: pytest --junitxml=report.xml
artifacts-path: report.xml
Rust
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: docker-amd64
container:
image: rust:1
steps:
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
command: cargo test --all
Java (Gradle)
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: docker-amd64
container:
image: gradle:8-jdk21
steps:
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
setup: gradle --version
command: gradle test
artifacts-path: |
**/build/reports/tests/**
**/build/test-results/**/*.xml
Java (Maven)
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: docker-amd64
container:
image: maven:3-eclipse-temurin-21
steps:
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
setup: mvn --version
command: mvn -B test
artifacts-path: |
**/target/surefire-reports/**
**/target/failsafe-reports/**
Multi-line command
Any of the examples above can run multiple commands in a single step by passing a YAML block scalar to command:
- uses: ./.actions/run-tests
with:
command: |
make lint
make test
make integration-test
Choosing a runner
runs-on must match the labels a runner was registered with — there is no fuzzy match. If your runners are registered as docker-amd64 and docker-arm64, then runs-on: docker will queue forever waiting for a runner that doesn't exist.
Single architecture
Pick the label that matches your runner:
jobs:
test:
runs-on: docker-amd64
Both architectures (matrix)
Run the same job on every arch in parallel. Useful when you ship binaries, link against native libraries, or want to catch arch-specific bugs early:
name: PR Tests
on: [pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
arch: [docker-amd64, docker-arm64]
runs-on: ${{ matrix.arch }}
container:
image: python:3.12-slim
steps:
- name: Install system deps
run: |
apt-get update
apt-get install -y --no-install-recommends git ca-certificates
rm -rf /var/lib/apt/lists/*
- name: Checkout
run: |
git config --global --add safe.directory "$PWD"
git clone --depth=1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" .
git fetch --depth=1 origin "+${{ github.event.pull_request.head.sha }}:pr"
git checkout pr
- name: Fetch action source
run: |
git clone --depth=1 --branch v1 \
"https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \
./.actions
- uses: ./.actions/run-tests
with:
command: ./run-tests.sh
fail-fast: false lets the other arch finish even if one fails, so you see both results.
Does the architecture matter for the tests?
- Pure JVM, Python, or Node with no native dependencies: only execution speed differs.
- Native code (Rust, C/C++, JNI, Python wheels like
numpy/cryptography, Node modules built withnode-gyp): the compiled artifact is arch-specific. Same source, different binary — running the matrix exercises both. - Container images pulled during setup: must publish a manifest for the runner's arch. Most official images do; some niche ones are amd64-only and will fail on arm64.
Making it a merge gate
This action alone does not block merges. To enforce passing tests before merge on Forgejo:
- Repo → Settings → Branches → Protected Branches
- Add a rule for
main(or your default branch) - Enable Require status checks to pass before merging
- Add the workflow job name (e.g.
test) to the required checks list
After the workflow has run at least once on a PR, the check name will appear in the dropdown.
Notes
runs-onmust match a label your runner was registered with — see Choosing a runner. The GitHub-styleubuntu-latesttypically does not exist on Forgejo.- This Forgejo instance is hosted on a subpath, so URL-form
uses:(e.g.uses: https://bitfreedom.net/code/...@v1) does not resolve. The examples clone the repository under test and the actions repo manually, then reference the action by local path. See Workflow shape. - The failure-artifact upload uses
forgejo/upload-artifact, which is API-compatible withactions/upload-artifactand works on both Forgejo and GitHub.