GitHub Action
The reusable GitHub Composite Action that brings up the full suite in one step. Inputs, outputs, certificate handling, version pinning.
The suite ships a GitHub Composite Action. Add a single step
to your workflow and all selected servers come up on
localhost.
Minimal example
name: Integration tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: php-opcua/[email protected]
- run: cargo test # or pytest, npm test, dotnet test, …
When the uses: step completes, all selected servers (default:
all) are accepting connections.
Inputs
| Input | Default | Effect |
|---|---|---|
servers |
all |
Comma-separated list of services to start |
wait-timeout |
120 |
Seconds passed to docker compose up --wait --wait-timeout. Only the opcua-no-security service has a healthcheck — the other services are considered "started" as soon as the container is running. |
Valid servers values:
no-security, userpass, certificate, all-security,
discovery, auto-accept, sign-only, legacy,
ecc-nist, ecc-brainpool, sks, pubsub, all.
Outputs
| Output | Description |
|---|---|
certs-dir |
Absolute path to the generated certificates directory |
Inside certs-dir:
ca/ # CA cert + key
server/ # Server cert + key
client/ # Trusted client cert
self-signed/ # Untrusted (for rejection tests)
expired/ # Expired (for expiry tests)
trusted/ # Trust dir layout
See Certificates for the full file map.
Selecting a subset of servers
Smaller test runs save CI time:
- uses: php-opcua/[email protected]
with:
servers: 'no-security,userpass'
Common subsets:
| Purpose | servers |
|---|---|
| Basic connectivity tests | no-security |
| Auth + crypto only | userpass,certificate,all-security |
| ECC only | ecc-nist,ecc-brainpool |
| PubSub subscriber tests | pubsub |
| PubSub + SKS | pubsub,sks |
| Everything | all (default) |
Using certificates in tests
Pass certs-dir to your test command:
- id: opcua
uses: php-opcua/[email protected]
- run: dotnet test
env:
OPCUA_CERTS_DIR: ${{ steps.opcua.outputs.certs-dir }}
OPCUA_CLIENT_CERT: ${{ steps.opcua.outputs.certs-dir }}/client/cert.pem
OPCUA_CLIENT_KEY: ${{ steps.opcua.outputs.certs-dir }}/client/key.pem
OPCUA_CA_CERT: ${{ steps.opcua.outputs.certs-dir }}/ca/ca-cert.pem
OPCUA_UNTRUSTED_CERT: ${{ steps.opcua.outputs.certs-dir }}/self-signed/cert.pem
OPCUA_EXPIRED_CERT: ${{ steps.opcua.outputs.certs-dir }}/expired/cert.pem
Your tests read those env vars and present the appropriate cert to each server.
Version pinning
| Form | Recommendation |
|---|---|
@v1.2.0 |
Recommended for stability |
@master |
Bleeding edge — may break |
@<sha> (full git SHA) |
Maximum reproducibility (CI provenance) |
Pinning to a tag means your tests don't break when the suite releases changes — the trade-off is you need to manually bump the version to pick up new servers or fixes.
What the Action does internally
The composite action runs four steps (see action.yml):
actions/checkoutofphp-opcua/uanetstandard-test-suiteinto.uanetstandard-test-suite/.- Builds
--scale <svc>=0arguments for every service not in theserversinput (or no scale args whenservers: all). - Runs
docker compose up -d --build --wait --wait-timeout <input>in.uanetstandard-test-suite/with the computed scale args. The action does not layerdocker-compose.ci.yml— the base file'srestart: unless-stoppedpolicies and theopcua-no-securityhealthcheck apply. - Verifies
certs/caexists,chmod -R a+r certs/, and exposes the absolute path via thecerts-diroutput.
Note: --wait only blocks until docker compose considers each
service "started". Of the 12 services, only opcua-no-security
has a healthcheck declared, so --wait returns once that one
container reports healthy and the other 11 containers are simply
"running". If you need to be sure a specific other server is
ready, poll its TCP port after the action completes.
See action.yml
in the repo for the full file.
End-to-end example
A realistic workflow that runs three test layers:
name: Tests
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4' }
- run: composer install
- run: vendor/bin/pest --exclude=Integration
integration:
runs-on: ubuntu-latest
needs: unit
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4' }
- id: opcua
uses: php-opcua/[email protected]
with:
servers: 'no-security,userpass,certificate,all-security,ecc-nist'
- run: composer install
- run: vendor/bin/pest tests/Integration
env:
OPCUA_CERTS_DIR: ${{ steps.opcua.outputs.certs-dir }}
ecc-integration:
runs-on: ubuntu-latest
needs: unit
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4' }
- id: opcua
uses: php-opcua/[email protected]
with:
servers: 'ecc-nist,ecc-brainpool'
- run: composer install
- run: vendor/bin/pest tests/EccIntegration
env:
OPCUA_CERTS_DIR: ${{ steps.opcua.outputs.certs-dir }}
Three jobs — unit (no servers), integration (RSA servers), and ECC integration (ECC servers only). Each gets its own set of running servers.
Resource limits on Actions runners
Standard GitHub-hosted runners have:
- 2-4 vCPU
- 7-16 GB RAM
- ~14 GB disk
The full suite (10 classic + SKS + PubSub) uses ~600 MB RAM and modest CPU. Comfortable on every tier.
Common failures
| Symptom | Cause / fix |
|---|---|
| Step exits with timeout | A service didn't start in wait-timeout seconds. Increase to 180. |
Bad_CertificateHostNameInvalid |
Connecting to a Docker service name instead of localhost. |
| Port already in use | Another job on the same runner is using 4840-4849. Use job isolation. |
| Cert dir empty | The certs-generator container failed — check the action logs. |
Where to read next
- Docker Compose and other CI — for GitLab, Jenkins, etc.
- Troubleshooting — common CI failures and fixes.