294 lines
10 KiB
Markdown
294 lines
10 KiB
Markdown
# 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
|
|
|
|
1. Runs `setup` if provided. Non-zero exit fails the job immediately.
|
|
2. Runs `command`. Exit code is captured but not yet propagated.
|
|
3. If `command` failed and `artifacts-path` is set, uploads matching files.
|
|
4. Re-exits with failure if `command` failed.
|
|
|
|
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:
|
|
|
|
1. Run inside a `container:` so the toolchain is predictable across runners.
|
|
2. Clone the repository under test manually with the job token, in place of `actions/checkout`.
|
|
3. Clone this actions repo into a local directory and reference the action with `uses: ./.actions/run-tests`, in place of `uses: 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](#choosing-a-runner) below.
|
|
|
|
### Node.js
|
|
|
|
```yaml
|
|
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)
|
|
|
|
```yaml
|
|
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
|
|
|
|
```yaml
|
|
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)
|
|
|
|
```yaml
|
|
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)
|
|
|
|
```yaml
|
|
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`:
|
|
|
|
```yaml
|
|
- 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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 with `node-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:
|
|
|
|
1. Repo → **Settings → Branches → Protected Branches**
|
|
2. Add a rule for `main` (or your default branch)
|
|
3. Enable **Require status checks to pass before merging**
|
|
4. 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-on` must match a label your runner was registered with — see [Choosing a runner](#choosing-a-runner). The GitHub-style `ubuntu-latest` typically 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](#workflow-shape).
|
|
- The failure-artifact upload uses [`forgejo/upload-artifact`](https://code.forgejo.org/forgejo/upload-artifact), which is API-compatible with `actions/upload-artifact` and works on both Forgejo and GitHub.
|