The Brik test lab.
Local Docker infra for end-to-end validation of Brik pipelines on real GitLab and real Jenkins.
One command. Two CI platforms. 56 scenarios.
You cannot validate a CI/CD framework with unit tests. Brik's shared libraries call real GitLab APIs, run inside real Jenkins agents, push to real artifact registries, deploy to real Kubernetes clusters. Validation requires the real thing.
The alternatives are bad:
- Renting GitLab/Jenkins cloud accounts per contributor: expensive, slow to iterate, shared state across PRs.
- Hand-rolling GitLab CE + Jenkins (with Configuration-as-Code) + Nexus + k3d + ArgoCD in Docker: days of wiring per contributor for PAT registration, runner registration, Job DSL, Nexus repository creation, ArgoCD port-forwards.
Briklab wires it once. Every contributor runs ./scripts/briklab.sh init and gets the full stack ready in 5 minutes.
For internal architecture details, see docs/architecture.md.
GitLab PAT, Runner registration, Gitea PAT, Jenkins Configuration-as-Code + Job DSL, Nexus repository creation (npm, Maven, PyPI, NuGet, Docker, Cargo), k3d cluster, ArgoCD install + port-forwards, SSH target container. All scripted, all idempotent, all under scripts/setup/.
28 scenarios per platform, with filtering by group (--groups A,D,H), batching (--batch-size 4), parallel execution (--parallel-groups), single-scenario targeting (--project <name>), and listing (--list). Built on 17 reusable Bash libraries under scripts/lib/e2e/lib/.
The deploy stage is validated against actual targets, not mocks: Kubernetes (node-deploy-k8s), Helm (node-deploy-helm), SSH (node-deploy-ssh), Docker Compose (node-deploy), GitOps via ArgoCD (node-deploy-gitops), and a 3-step rollback chain (node-deploy-rollback) that verifies ArgoCD rolls back to the previous image.
E2E runs accumulate state -- repos, namespaces, ArgoCD apps, artifacts. briklab.sh reset --gitlab cleans everything in one command. Tokens and port-forwards expire on long sessions -- briklab.sh infra-refresh renews them without restarting any container.
- Docker Desktop (24 GB RAM recommended)
jq(brew install jq)k3d(lightweight K3s in Docker) -- see install docsargocdCLI -- see install docs
| Tool | macOS | Linux |
|---|---|---|
| k3d | brew install k3d |
wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash |
| argocd | brew install argocd |
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 && sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd && rm argocd-linux-amd64 |
Add to /etc/hosts:
127.0.0.1 gitlab.briklab.test nexus.briklab.test
127.0.0.1 gitea.briklab.test jenkins.briklab.test
127.0.0.1 argocd.briklab.test ssh-target.briklab.test
Add to Docker Desktop (Settings > Docker Engine):
{
"insecure-registries": [
"nexus.briklab.test:8082"
]
}./scripts/briklab.sh initGitLab takes 3-5 minutes on first start. Jenkins builds a custom Docker image on first start. Nexus takes 2-3 minutes. The script waits automatically.
| Service | URL | Credentials |
|---|---|---|
| GitLab UI | http://gitlab.briklab.test:8929 | root / Brik-Gtlb-2026 |
| GitLab SSH | ssh://git@gitlab.briklab.test:2222 |
- |
| GitLab Runner | - | - |
| Gitea UI | http://gitea.briklab.test:3000 | brik / Brik-Gitea-2026 |
| Jenkins UI | http://jenkins.briklab.test:9090 | admin / Brik-Jenkins-2026 |
| Nexus UI | http://nexus.briklab.test:8081 | admin / Brik-Nexus-2026 |
| Nexus Docker | http://nexus.briklab.test:8082 | - |
| ArgoCD UI | https://argocd.briklab.test:9080 | admin / (dynamic, see k3d-start output) |
| k3d (k3s) | localhost:6443 | - |
| SSH Target | internal only | deploy / SSH key |
Default credentials are defined in .env. Modify them before the first init.
Setup creates 6 hosted repositories for artifact publishing:
| Repository | Format | Endpoint | Usage |
|---|---|---|---|
brik-npm |
npm | :8081/repository/brik-npm/ |
npm publish |
brik-maven |
maven2 (release) | :8081/repository/brik-maven/ |
mvn deploy |
brik-pypi |
pypi | :8081/repository/brik-pypi/ |
twine upload / uv publish |
brik-nuget |
nuget (V3) | :8081/repository/brik-nuget/ |
dotnet nuget push |
brik-docker |
docker | :8082/v2/ |
docker push |
brik-cargo |
cargo | :8081/repository/brik-cargo/ |
cargo publish (sparse protocol) |
| Command | Description |
|---|---|
briklab.sh init |
First launch (start + setup + smoke-test) |
briklab.sh start |
Start all containers (+ set root password) |
briklab.sh stop |
Stop all containers |
briklab.sh restart |
Stop + start |
briklab.sh clean |
Delete all data and volumes (irreversible) |
| Command | Description |
|---|---|
briklab.sh setup |
Re-run GitLab/Runner/Gitea/Jenkins/Nexus configuration |
briklab.sh smoke-test |
Verify that each component is reachable |
Platform is required: --gitlab or --jenkins. All other flags are identical.
| Command | Description |
|---|---|
briklab.sh test --gitlab |
Run node-minimal on GitLab |
briklab.sh test --gitlab --all |
Run the full GitLab E2E suite |
briklab.sh test --gitlab --complete |
Run only *-complete scenarios (with Nexus publish) |
briklab.sh test --gitlab --project <name> |
Run a single GitLab scenario by name |
briklab.sh test --gitlab --list |
List available GitLab scenarios |
briklab.sh test --jenkins |
Run node-minimal on Jenkins |
briklab.sh test --jenkins --all |
Run the full Jenkins E2E suite |
briklab.sh test --jenkins --complete |
Run only Jenkins *-complete scenarios |
briklab.sh test --jenkins --project <name> |
Run a single Jenkins scenario by name |
briklab.sh test --jenkins --list |
List available Jenkins scenarios |
briklab.sh test --gitlab --batch-size N |
Execute scenarios in batches of N |
briklab.sh test --gitlab --groups A,D,H |
Filter by group (A=stack, B=full, C=complete, D=security, E=deploy, F=gitops, G=workflow, H=error) |
briklab.sh test --gitlab --parallel-groups |
Execute groups in parallel |
| Command | Description |
|---|---|
briklab.sh reset --gitlab --repos |
Delete test repos on GitLab |
briklab.sh reset --jenkins --repos |
Delete test repos on Gitea |
briklab.sh reset --k8s |
Delete test k8s namespaces |
briklab.sh reset --argocd |
Delete test ArgoCD apps |
briklab.sh reset --artifacts |
Purge Nexus artifacts |
briklab.sh reset --gitlab |
Full reset (repos + k8s + argocd + artifacts) |
| Command | Description |
|---|---|
briklab.sh infra-refresh |
Refresh expired tokens and port-forwards |
| Command | Description |
|---|---|
briklab.sh status |
Show container health and access URLs |
briklab.sh logs <service> |
Tail logs (gitlab, runner, gitea, jenkins, nexus, ssh-target) |
| Command | Description |
|---|---|
briklab.sh k3d-start |
Create k3d cluster + install ArgoCD |
briklab.sh k3d-stop |
Destroy the k3d cluster |
# Day 1 - Full setup
./scripts/briklab.sh init # First time setup (~5 min)
./scripts/briklab.sh test --gitlab --all # Run GitLab E2E suite
./scripts/briklab.sh test --jenkins --all # Run Jenkins E2E suite
./scripts/briklab.sh stop # Done for the day
# Day N
./scripts/briklab.sh start # Restart (fast, data preserved)
./scripts/briklab.sh test --gitlab # Quick GitLab smoke test
./scripts/briklab.sh test --jenkins # Quick Jenkins smoke test
./scripts/briklab.sh stop # DoneEach GitLab E2E scenario pushes a test project to briklab GitLab, triggers a pipeline, and validates that specific jobs pass.
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
node-minimal |
Node.js | push main |
init, build, test, deploy, notify | pass |
python-minimal |
Python | push main |
init, build, test, deploy, notify | pass |
java-minimal |
Java | push main |
init, build, test, deploy, notify | pass |
rust-minimal |
Rust | push main |
init, build, test, deploy, notify | pass |
dotnet-minimal |
.NET | push main |
init, build, test, deploy, notify | pass |
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
node-full |
Node.js | tag v0.1.0 |
init, release, build, quality, test, package, deploy, notify | pass |
python-full |
Python | tag v0.1.0 |
init, release, build, quality, security, test, package, deploy, notify | pass |
java-full |
Java | tag v0.1.0 |
init, release, build, quality, test, package, deploy, notify | pass |
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
node-complete |
Node.js | tag v0.1.0 |
init, release, build, test, package, notify | pass |
python-complete |
Python | tag v0.1.0 |
init, release, build, test, package, notify | pass |
java-complete |
Java | tag v0.1.0 |
init, release, build, test, package, notify | pass |
rust-complete |
Rust | tag v0.1.0 |
init, release, build, test, package, notify | pass |
dotnet-complete |
.NET | tag v0.1.0 |
init, release, build, test, package, notify | pass |
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
node-security |
Node.js | push main |
init, build, security, test, notify | pass |
node-deploy |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
node-deploy-dryrun |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
node-deploy-k8s |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
node-deploy-ssh |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
node-deploy-gitops |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
node-deploy-rollback |
Node.js | multi-step | v0.1.0 deploy, v0.2.0 deploy, config revert | pass |
node-deploy-dryrunreuses thenode-deployproject withBRIK_DRY_RUN=true.node-deploy-rollbackusesnode-deploy-gitops-rollbackwith a 3-step commit chain: deploy v0.1.0, deploy v0.2.0, revert config repo to verify ArgoCD rolls back to v0.1.0 image.
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
node-deploy-helm |
Node.js | tag v0.1.0 |
init, release, build, test, package, deploy, notify | pass |
| Scenario | Stack | Trigger | Expected failure | Expected |
|---|---|---|---|---|
node-deploy-failure |
Node.js | tag v0.1.0 |
brik-deploy job fails (non-existent namespace) | fail |
| Scenario | Stack | Trigger | Validated stages | Expected |
|---|---|---|---|---|
workflow-trunk-main |
Node.js | push main |
init, build, test, deploy, notify | pass |
workflow-trunk-tag |
Node.js | tag v0.2.0 |
init, release, build, test, package, deploy, notify | pass |
workflow-trunk-feature |
Node.js | push feature/test |
init, build, test, notify | pass |
Workflow scenarios run sequentially:
workflow-trunk-mainpushes to main, thenworkflow-trunk-tagcreates a tag on the same project, thenworkflow-trunk-featurecreates a feature branch. Each depends on the previous one.
| Scenario | Stack | Trigger | Expected failure | Expected |
|---|---|---|---|---|
error-build |
Node.js | push main |
brik-build job fails | fail |
error-test |
Node.js | push main |
brik-test job fails | fail |
error-config |
Node.js | push main |
brik-init job fails (invalid brik.yml) | fail |
Jenkins E2E testing mirrors the GitLab scenarios: pushes the Brik shared library and test projects to Gitea, then triggers Jenkins pipelines via the REST API.
The Jenkins pipeline runs the full Brik fixed flow:
Init -> Release -> Build -> Quality & Security -> Test -> Package -> Deploy -> Notify
Jenkins runs the same 28 scenarios as GitLab (minimal, full, complete, security, deploy, helm, workflow, error). The E2E framework supports CI variable injection via buildWithParameters.
# Run the default Jenkins E2E pipeline (node-minimal)
./scripts/briklab.sh test --jenkins
# Run the full Jenkins E2E suite
./scripts/briklab.sh test --jenkins --all
# Run a specific scenario
./scripts/briklab.sh test --jenkins --project node-deploy-k8s
# List available scenarios
./scripts/briklab.sh test --jenkins --listTest project fixtures live in test-projects/. Each has a brik.yml and platform-specific CI config (.gitlab-ci.yml for GitLab, Jenkinsfile for Jenkins).
| Project | Stack | Runner Image | Purpose |
|---|---|---|---|
node-minimal |
Node.js | brik-runner-node:22 |
Basic flow (init, build, test) |
node-full |
Node.js | brik-runner-node:22 |
All stages (release, quality, package) |
node-security |
Node.js | brik-runner-node:22 |
Security stage (npm audit) |
node-deploy |
Node.js | brik-runner-node:22 |
Deploy stage validation (compose target) |
node-deploy-k8s |
Node.js | brik-runner-node:22 |
Deploy to Kubernetes (k8s target) |
node-deploy-ssh |
Node.js | brik-runner-node:22 |
Deploy via SSH (ssh target) |
node-deploy-gitops |
Node.js | brik-runner-node:22 |
Deploy via GitOps + ArgoCD (gitops target) |
node-deploy-helm |
Node.js | brik-runner-node:22 |
Deploy via Helm chart on k3d |
node-deploy-failure |
Node.js | brik-runner-node:22 |
Intentional deploy failure (non-existent namespace) |
node-deploy-gitops-rollback |
Node.js | brik-runner-node:22 |
GitOps rollback (ArgoCD, 3-step commit chain) |
node-workflow-trunk |
Node.js | brik-runner-node:22 |
Trunk-based workflow (main, tag, feature branch) |
python-minimal |
Python | brik-runner-python:3.13 |
Python stack (pytest) |
python-full |
Python | brik-runner-python:3.13 |
Full Python pipeline (ruff, pip-audit, Docker) |
java-minimal |
Java | brik-runner-java:21 |
Java stack (JUnit 5) |
java-full |
Java | brik-runner-java:21 |
Full Java pipeline (checkstyle, Docker) |
rust-minimal |
Rust | brik-runner-rust:1 |
Rust stack (cargo test) |
dotnet-minimal |
.NET | brik-runner-dotnet:9.0 |
.NET stack (xUnit) |
node-complete |
Node.js | brik-runner-node:22 |
Full pipeline + npm/Docker publish to Nexus |
python-complete |
Python | brik-runner-python:3.13 |
Full pipeline + PyPI/Docker publish to Nexus |
java-complete |
Java | brik-runner-java:21 |
Full pipeline + Maven/Docker publish to Nexus |
rust-complete |
Rust | brik-runner-rust:1 |
Full pipeline + Cargo/Docker publish to Nexus |
dotnet-complete |
.NET | brik-runner-dotnet:9.0 |
Full pipeline + NuGet/Docker publish to Nexus |
node-error-build |
Node.js | brik-runner-node:22 |
Intentionally broken build |
node-error-test |
Node.js | brik-runner-node:22 |
Intentionally failing tests |
invalid-config |
Node.js | brik-runner-base:latest |
Invalid brik.yml (version: 99) |
Runner images are selected automatically by the init job based on
project.stackandproject.stack_versioninbrik.yml. The init job resolves the image and propagates it via dotenv to downstream jobs. Images are published atghcr.io/getbrik/brik-runner-<stack>:<version>.
Full suite run on 2026-04-18
| Issue | Affected scenarios | Root cause |
|---|---|---|
| Runner saturation | various (GitLab timeout) | Single runner overwhelmed by concurrent pipelines. Mitigate with --batch-size |
GitLab won't start -- Check Docker Desktop has at least 18 GB RAM allocated. First start takes 3-5 minutes. Check logs: ./scripts/briklab.sh logs gitlab
Runner errors (runner_system_failure / image_pull_failure) -- Verify helper_image is present in the runner's config.toml. Check logs: ./scripts/briklab.sh logs runner. If needed, re-run ./scripts/briklab.sh setup.
Jenkins CasC errors -- Check ./scripts/briklab.sh logs jenkins for Configuration-as-Code errors. Common issue: plugin not installed. Verify images/jenkins/plugins.txt includes all required plugins. To reload CasC without restarting Jenkins, use the jenkins_reload_casc helper in briklab.sh (only works for CasC YAML changes; env var changes require a full restart).
Jenkins pipeline can't find Brik library -- The Brik shared library must be pushed to Gitea before triggering a pipeline. Run ./scripts/briklab.sh setup to ensure Gitea is configured, then push repos with the E2E test command.
Gitea shows install page -- On first start, Gitea requires initial installation. The setup script handles this automatically. If it fails, check logs: ./scripts/briklab.sh logs gitea
Nexus slow to start -- First start takes 2-3 minutes (JVM + plugin initialization). The healthcheck has a 180s start_period. Check logs: ./scripts/briklab.sh logs nexus
Nexus Docker push fails (HTTP) -- Add "nexus.briklab.test:8082" to insecure-registries in Docker Desktop settings. The Nexus Docker registry uses HTTP, not HTTPS.
Nexus repository creation fails -- If setup is run before Nexus is fully ready, repository creation may fail. Wait for the healthcheck to pass, then re-run: ./scripts/briklab.sh setup
k3d cluster already exists -- k3d cluster delete brik && ./scripts/briklab.sh k3d-start
ArgoCD won't sync -- ArgoCD default polling is ~3 minutes. Use argocd app get <app> --refresh hard to force, or run ./scripts/briklab.sh infra-refresh to renew port-forwards and tokens.
E2E timeout -- Use --batch-size 4 to limit concurrent pipelines. Check runner saturation with ./scripts/briklab.sh logs runner. Run ./scripts/briklab.sh infra-refresh if tokens expired.
Reset between E2E runs -- ./scripts/briklab.sh reset --gitlab cleans repos, k8s namespaces, ArgoCD apps, and Nexus artifacts.
For the complete list of known issues and solutions, see docs/architecture.md - Known Gotchas.
# Stop containers (data preserved)
./scripts/briklab.sh stop
# Delete all data and volumes (irreversible, requires confirmation)
./scripts/briklab.sh clean
# Delete k3d cluster
k3d cluster delete brik
# Full removal: after clean, remove Docker images manually
docker rmi gitlab/gitlab-ce:18.10.1-ce.0 gitlab/gitlab-runner:alpine3.21-bleeding
docker rmi gitea/gitea:1.25.5
docker rmi briklab-jenkins # custom-built Jenkins image
docker rmi sonatype/nexus3:3.90.2-alpine
docker network rm brik-net 2>/dev/nullBriklab scripts are organized into reusable libraries under scripts/lib/:
scripts/
briklab.sh # CLI entry point
lib/
common.sh # Shared utilities (logging, retry, env loading)
infra-verify.sh # Environment verification
infra-refresh.sh # Token/port-forward refresh
auth/
gitlab-pat.sh # GitLab PAT management
gitea-pat.sh # Gitea PAT management
argocd-token.sh # ArgoCD token retrieval
argocd-portfwd.sh # ArgoCD port-forward management
setup/
gitlab.sh # GitLab CE configuration
runner.sh # GitLab Runner registration
gitea.sh # Gitea configuration
jenkins.sh # Jenkins CasC + Job DSL
nexus.sh # Nexus repository creation
k3d.sh # k3d cluster + ArgoCD install
ssh-target.sh # SSH target container setup
smoke-test.sh # Post-setup health checks
e2e/
gitlab-push.sh # Push repos to GitLab
gitlab-test.sh # Single GitLab pipeline test
gitlab-suite.sh # GitLab scenario orchestrator
gitlab-rollback.sh # GitLab rollback E2E
gitea-push.sh # Push repos to Gitea
jenkins-test.sh # Single Jenkins pipeline test
jenkins-suite.sh # Jenkins scenario orchestrator
jenkins-rollback.sh # Jenkins rollback E2E
lib/ # Reusable E2E libraries (17 files)
assert.sh # Assertion framework
auth.sh # PAT validation
suite.sh # Suite orchestrator (groups, batches)
push.sh # Git push helpers
git.sh # Git operations
reset.sh # State cleanup between runs
rollback.sh # Rollback test helpers
gitlab-api.sh # GitLab API client
jenkins-api.sh # Jenkins API client
gitea-api.sh # Gitea API client
nexus.sh # Nexus artifact verification
k8s.sh # Kubernetes assertions
argocd.sh # ArgoCD sync/status
compose.sh # Docker Compose assertions
ssh.sh # SSH deploy assertions
error-patterns.conf # Error detection patterns
error-ignore-patterns.conf # False positive filters
Auth libraries are reusable -- each validates and caches credentials, and can be sourced from any script.
- ✅ 2 CI platforms validated end-to-end (GitLab CE + Jenkins, same scenarios on both)
- ✅ 56 E2E scenarios (28 per platform: minimal, full, complete, security, deploy, helm, workflow, error)
- ✅ 6 Nexus repository formats validated (npm, Maven, PyPI, NuGet, Docker, Cargo)
- ✅ 5 deploy targets validated (Kubernetes, Helm, SSH, Docker Compose, GitOps via ArgoCD)
- ✅ 17 reusable Bash libraries under
scripts/lib/e2e/lib/ - ✅ 1 rollback chain (3-step commit chain verifies ArgoCD rolls back to the previous image)
- ✅ Idempotent setup -- every step under
scripts/setup/re-runs safely;briklab.sh setupreconciles withoutclean
- Brik -- the portable CI/CD pipeline system
- Architecture -- how Briklab works internally
We use AI-assisted development (Claude Code + Everything Claude Code) to accelerate implementation:
- Every contribution (human or AI-generated) follows the same quality gates: code review, test coverage, E2E testing, and CI checks.
- AI-generated code is not perfect. Regular refactoring passes address its shortcomings, and the overall productivity gains are significant.
