WooForge docs

Everything you need to get your code onto WooForge and write a pipeline that builds, tests, and deploys it — including infrastructure changes via Terraform.

Overview

A WooForge project bundles a Git repository, merge requests, issues, and a pipeline runner under one URL. There is no separate app to configure — everything a project needs to build and ship lives in the repository itself, under a single directory: .wooforge/.

Who this is for. Developers pushing code and writing pipeline config, and project admins managing CI/CD variables and protected branches.

Getting started

Every project can be reached over HTTP or SSH, the same as any other Git remote.

Clone over HTTPS

Use a Personal Access Token as your password — generate one under Settings → Access Tokens.

git clone https://<your-host>/group/project.git

Clone over SSH

Add a public key under Settings → SSH Keys, then:

git clone ssh://git@<your-host>:2222/group/project.git

Trigger a pipeline

Pipelines start automatically on every push to a branch that has a .wooforge/ directory, or on demand from the Pipelines tab using “Run pipeline” against any branch.

The .wooforge/ structure

Pipeline configuration is split across three files instead of one, so a long pipeline stays readable: each stage’s jobs live in their own file, and two small entry files say what runs and in what order.

repository layout
.wooforge/
├── cicd.yml          # entry point: metadata, triggers, global variables
├── stage.yml         # ordered stage list + which file implements each
└── stages/
    ├── build.yml     # jobs for the build stage
    ├── test.yml      # jobs for the test stage
    └── deploy.yml    # jobs for the deploy stage (terraform lives here too)

cicd.yml — entry point

version: 1
pipeline:
  name: My Project CI/CD
  triggers: [push, merge_request, manual]
  variables:
    NODE_ENV: production
stages_file: .wooforge/stage.yml

variables here are global — every job gets them, unless a stage or job sets the same key again, which wins.

stage.yml — stage order

stages:
  - name: build
    file: .wooforge/stages/build.yml
  - name: test
    file: .wooforge/stages/test.yml
    depends_on: [build]
  - name: deploy
    file: .wooforge/stages/deploy.yml
    depends_on: [test]

Stage order is resolved from depends_on, not just list order. A cycle here fails the pipeline immediately with the cycle spelled out, rather than hanging.

stages/*.yml — jobs

jobs:
  unit_test:
    image: node:20-alpine
    needs: [build]
    commands:
      - npm run test:unit
  lint:
    image: node:20-alpine
    needs: [build]
    commands:
      - npm run lint
    allow_failure: true

needscan point at a job in an earlier stage or another job in the same file — that’s how a Terraform apply waits for its own plan job even though both live in the deploy stage.

Job field reference

Every job under a stage file’s jobs: map accepts:

type
shell (default) or terraform.
image
Container image the job runs in. Defaults to alpine:latest.
commands / steps
A list of shell commands, run in order. Either key name works — steps reads better for Terraform CLI calls.
variables
Job-scoped variables, layered over stage and global ones with the highest precedence.
needs
Job names that must finish before this one is claimed by a runner.
allow_failure
If true, a failure here doesn’t block dependents or fail the pipeline.
when
on_success (default) or manual — a manual job waits for approval in the Pipeline tab.
rules
A list of if: conditions. If none match, the job is left out of the pipeline entirely.
working_directory
Subdirectory to run commands in, relative to the repo root.
artifacts.paths
Files kept after success so a later job (via its own needs) can pick them up.
environment.name / .url
Labels the job in the Pipeline UI and links out to where it deployed.

Terraform & approval gates

Provisioning infrastructure is a job type, not a bolt-on: set type: terraform and the job gets working_directory, backend_config, and automatic TF_VAR_<name> injection for any CI/CD variable flagged for it.

Apply always requires approval. Any terraform job whose steps run terraform apply is forced onto when: manual— even if you don’t set it, and you cannot turn it off.
.wooforge/stages/deploy.yml
jobs:
  provision_infra:
    type: terraform
    image: hashicorp/terraform:1.8
    working_directory: infra/
    backend_config: infra/backend.hcl
    variables:
      TF_VAR_environment: production
    steps:
      - terraform init -backend-config=backend.hcl
      - terraform validate
      - terraform plan -out=tfplan
    artifacts:
      paths: [infra/tfplan]
    rules:
      - if: '$TARGET_BRANCH == "main"'

  apply_infra:
    type: terraform
    image: hashicorp/terraform:1.8
    working_directory: infra/
    needs: [provision_infra]
    steps:
      - terraform apply -auto-approve tfplan
    environment:
      name: production

Plan and apply are deliberately separate jobs: the plan runs unattended and uploads tfplan as an artifact; apply pulls that exact plan back down and runs it only once someone clicks Approve & run in the Pipeline view, where it shows an Infrabadge and an “Awaiting approval” state until then.

Rules & variable interpolation

rules conditions and variables values can reference other variables with $NAME or ${NAME}. Only == / != string comparisons are supported in if:, which covers the common case: branch-gating a job.

rules:
  - if: '$TARGET_BRANCH == "main"'

An unresolved reference is left as literal text, so it can still be picked up by the shell at run time — useful for CI/CD variables that are secret and shouldn’t be baked into stored config.

CI/CD variables

Set project-level variables under Settings → CI/CD Variables. Every variable becomes a plain environment variable in every job; three flags change how far it reaches:

Masked
Value is redacted (••••••••) anywhere it would appear in job logs.
Protected
Only injected into jobs running on a protected branch.
Expose to Terraform
Additionally injected as TF_VAR_<name>.

Predefined variables

Available in every job without declaring anything:

VariableValue
TARGET_BRANCHThe branch the pipeline is running against
PIPELINE_SOURCEpush or manual
WOO_COMMIT_SHA / _SHORT_SHAFull and 8-character commit SHA
WOO_COMMIT_REF_NAMESame as TARGET_BRANCH
WOO_PROJECT_ID / _PATH / _NAMEIdentify the project the pipeline belongs to
WOO_PIPELINE_ID / _IIDInternal and project-scoped pipeline numbers
WOO_JOB_ID / _NAME / _STAGEIdentify the running job itself
WOO_SERVER_URLBase URL of this WooForge instance
CI / WOOFORGE_CI"true" — useful for scripts that behave differently in CI