Table of Contents
CI/CD
Isurus includes a built-in CI/CD system that automatically runs pipelines when you push to a repository. Define your build, test, and deploy steps in a YAML file, and Isurus handles the rest.
Overview
The CI/CD system works as follows:
- You commit a
.isurus-ci.ymlfile to your repository root. - When you push, Isurus detects the pipeline configuration and creates a new pipeline.
- A CI agent picks up the pipeline and executes each step.
- Results are streamed in real time to the web UI.
Setting Up CI
1. Create the Pipeline File
Add a file named .isurus-ci.yml to the root of your repository:
steps:
- name: test
docker:
image: golang:1.26
commands:
- go test -v ./...
2. Commit and Push
hg add .isurus-ci.yml
hg commit -m "Add CI pipeline configuration"
hg push
3. View Results
Navigate to your repository and click the CI/CD tab to see the pipeline status, step logs, and results.
Enabling and Disabling CI
CI/CD is enabled by default for all repositories. To disable CI for a repository:
- Navigate to your repository.
- Go to Settings > General.
- Uncheck Enable CI/CD pipelines.
- Click Save.
When CI is disabled, pushes will not trigger pipelines. Existing pipeline history is preserved. Re-enable at any time to resume CI on future pushes.
Pipeline YAML Reference
The pipeline configuration file is .isurus-ci.yml, placed at the repository root.
Top-Level Structure
when:
event: [push]
branch: [default, stable]
steps:
- name: step-name
...
services:
- name: service-name
...
when — Pipeline Filters
The top-level when block controls when the entire pipeline runs. If omitted, the pipeline runs on every push.
| Field | Type | Description |
|---|---|---|
event |
string or list | Event types to match. Currently supported: push |
branch |
string or list | Branch names to match (e.g., default, stable) |
Both fields accept a single string or a list of strings:
# Single value
when:
branch: default
# Multiple values
when:
branch: [default, stable]
event: [push]
If both event and branch are specified, both must match for the pipeline to run.
steps — Pipeline Steps
Each step defines a unit of work in the pipeline. Steps execute sequentially in the order listed.
| Field | Required | Type | Description |
|---|---|---|---|
name |
Yes | string | Unique name for the step |
docker |
Depends | object | Docker executor config. Contains image: (required for Docker executor) |
incus |
Depends | object | Incus executor config. Contains image: (required for Incus executor) |
image |
Depends | string | Shorthand for docker: { image: ... } — supported for backward compatibility |
commands |
Yes | list | Shell commands to execute |
environment |
No | map | Key-value environment variables |
secrets |
No | list | Secret names to inject as environment variables |
when |
No | object | Per-step branch/event filter (same format as top-level when) |
Backward compatibility:
image:is supported as a shorthand fordocker: { image: ... }for backward compatibility.
Step Example
steps:
- name: test
docker:
image: golang:1.26
commands:
- go vet ./...
- go test -v ./...
environment:
CGO_ENABLED: "0"
- name: build
docker:
image: golang:1.26
commands:
- go build -o app ./cmd/server
secrets:
- deploy_token
Per-Step when Filter
Individual steps can have their own when filter. A step is skipped if its filter does not match the current event and branch:
steps:
- name: test
docker:
image: golang:1.26
commands:
- go test ./...
- name: deploy
docker:
image: alpine
commands:
- ./deploy.sh
when:
branch: default
event: push
In this example, the deploy step only runs on pushes to the default branch.
services — Background Containers
Services are background containers that start before the steps and remain running for the duration of the pipeline. Use them for databases, caches, or other dependencies.
| Field | Required | Type | Description |
|---|---|---|---|
name |
Yes | string | Service name (used as hostname for networking) |
image |
Yes | string | Docker image to run |
environment |
No | map | Key-value environment variables |
Services Example
services:
- name: db
image: postgres:16
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
- name: redis
image: redis:7
steps:
- name: test
docker:
image: golang:1.26
commands:
- go test -v ./...
environment:
DATABASE_URL: "postgres://test:test@db:5432/testdb?sslmode=disable"
REDIS_URL: "redis://redis:6379"
Note: Services always use Docker and take
image:directly. Thedocker:block syntax applies to pipeline steps only.
Full Example
A complete .isurus-ci.yml with filters, multiple steps, services, and secrets:
when:
event: [push]
branch: [default, stable]
services:
- name: db
image: postgres:16
environment:
POSTGRES_PASSWORD: test
steps:
- name: lint
docker:
image: golangci/golangci-lint:latest
commands:
- golangci-lint run ./...
- name: test
docker:
image: golang:1.26
commands:
- go test -v -race ./...
environment:
CGO_ENABLED: "1"
DATABASE_URL: "postgres://postgres:test@db:5432/postgres?sslmode=disable"
- name: build
docker:
image: golang:1.26
commands:
- go build -o app ./cmd/server
- name: deploy
docker:
image: alpine
commands:
- apk add --no-cache openssh-client
- ./scripts/deploy.sh
secrets:
- deploy_key
- deploy_host
when:
branch: default
Pipeline Detail Page
The pipeline detail page uses a split-panel layout for inspecting builds:
- Left sidebar — Lists each step with its status icon and duration. Click a step to view its log.
- Right panel — Displays the log output for the selected step.
Status Icons
| Icon | Meaning |
|---|---|
| Green checkmark | Success |
| Red X | Failure |
| Yellow spinner | Running (pulsing indicator) |
| Gray circle | Pending |
| Slash | Cancelled |
| Dash | Skipped |
Log Viewer Features
- Real-time streaming — Running steps stream logs via Server-Sent Events (SSE). Lines appear as they are produced.
- Copy to clipboard — Copy the full log output.
- Download — Download the log as a file (
pipeline-N-stepname.log). - Show Config — View the commands configured for the step.
Pipeline Metadata
The detail page also shows:
- Commit hash and branch name
- Event type and who triggered the pipeline
- Queue time, execution duration, and total time
- Agent name (which CI agent executed the pipeline)
Pipeline List Page
The pipeline list provides an overview of all pipelines for a repository.
Filtering
| Filter | Options |
|---|---|
| Status | All, Running, Success, Failure, Pending, Cancelled, Error |
| Branch | Any branch that has had pipelines |
| Event | Push (and other event types) |
| Triggered by | Filter by the user who triggered the pipeline |
Sorting
| Sort field | Options |
|---|---|
| Pipeline number | Ascending or descending |
| Creation date | Ascending or descending |
Pagination
| Control | Options |
|---|---|
| Page size | 20, 50, or 100 per page |
| Navigation | Previous / Next page links |
Managing Pipelines
Re-run a Pipeline
On the pipeline detail page, click Re-run to create a new pipeline from the same commit. This is useful when a failure was caused by a transient issue (e.g., a network timeout).
Cancel a Running Pipeline
On the pipeline detail page, click Cancel to stop a running or pending pipeline. All pending and running steps are marked as cancelled.
Pipeline Parse Errors
If your .isurus-ci.yml has a syntax error, Isurus creates a pipeline with an error status instead of silently failing. Click through to the pipeline detail to see the exact parse error in the step log. Common causes:
- Invalid YAML syntax (indentation, missing colons)
- Missing required fields (step name, commands)
- Empty executor image (
docker:block withoutimage:)
Tip: Use single quotes around commands that contain $ variables to prevent YAML from interpreting them:
commands:
- 'echo "Commit: $CI_COMMIT"'
Delete a Pipeline
Organization owners and admins can delete pipelines:
- Single delete — On the pipeline detail page, click Delete to remove one pipeline and its logs.
- Bulk delete — On the pipeline list page, select multiple pipelines using the checkboxes and click Delete Selected.
- Delete all failed — On the pipeline list page, click Delete Failed to remove all pipelines with a failure status.
CI Secrets
Secrets store sensitive values (API keys, deploy tokens, passwords) that are injected into pipeline steps as environment variables. All secrets are encrypted at rest using AES-256-GCM.
Repo-Level Secrets
- Navigate to your repository.
- Go to Settings > CI/CD Secrets.
- Enter a Name (used as the environment variable name) and Value.
- Click Add Secret.
Org-Level Secrets
Organization-level secrets are inherited by all repositories in the organization:
- Navigate to your organization.
- Go to Settings > CI Secrets.
- Enter a Name and Value.
- Click Add Secret.
If a repo-level secret has the same name as an org-level secret, the repo-level secret takes precedence (override).
Using Secrets in Pipelines
Reference secrets by name in the secrets field of a step. They are injected as environment variables:
steps:
- name: deploy
docker:
image: alpine
commands:
- echo "Deploying to $DEPLOY_HOST"
- ./deploy.sh
secrets:
- DEPLOY_HOST
- DEPLOY_KEY
Security
- Secrets are encrypted at rest with AES-256-GCM.
- Secret values are masked in pipeline logs — any output matching a secret value is replaced with
***. - Secret values are never displayed in the web UI after creation. Only the secret name is shown.
- Only organization owners can create or delete secrets.
CI Badges
Embed a build status badge in your README or documentation to show the current CI status.
Badge URL
https://your-isurus-instance/:org/:repo/ci/badge
Embedding in Markdown

Badge Statuses
| Status | Color |
|---|---|
| Success | Green |
| Failure / Error | Red |
| Running / Pending | Yellow |
| Cancelled | Gray |
| Unknown (no pipelines) | Gray |
The badge always reflects the latest pipeline for the repository.
Publishing Releases from CI
CI pipelines can create releases and upload artifacts via the Isurus API. This is useful for automated release workflows triggered by tag pushes.
Example: Release Pipeline
steps:
- name: build
docker:
image: golang:1.26
commands:
- make build-release
- name: publish
docker:
image: alpine:latest
commands:
- apk add --no-cache curl jq
- |
RELEASE_ID=$(curl -s -X POST \
-H "Authorization: Bearer $RELEASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"tag_name":"'$CI_TAG'","name":"Release '$CI_TAG'","body":"Automated release from CI"}' \
$CI_SERVER/api/v1/repos/$CI_ORG/$CI_REPO/releases | jq -r .id)
for f in dist/*; do
curl -s -X POST \
-H "Authorization: Bearer $RELEASE_TOKEN" \
-F "attachment=@$f" \
$CI_SERVER/api/v1/repos/$CI_ORG/$CI_REPO/releases/$RELEASE_ID/assets
done
secrets:
- RELEASE_TOKEN
when:
event: [tag]
How It Works
- Tag your release in Mercurial:
hg tag v1.0.0 && hg push - The push triggers a CI pipeline (the
when: event: [tag]filter matches tag events) - The
buildstep compiles your release artifacts - The
publishstep usescurlto:- Create a release via
POST /api/v1/repos/:org/:repo/releases - Upload each artifact via
POST /api/v1/repos/:org/:repo/releases/:id/assets
- Create a release via
- The release appears on the Releases tab with all uploaded files
API Token
Create an API token with write scope at Settings > API Tokens. Add it as a CI secret named RELEASE_TOKEN in your repository's CI/CD settings.
Scheduled Releases
Releases can be scheduled for future publication, useful for coordinating launches or timed announcements.
How to Schedule a Release
- Navigate to your repository's Releases tab and create a new release (or edit an existing draft).
- Fill in the release details (tag, title, description, attachments) as usual.
- Set a date and time in the Schedule Publication field.
- Save the release as a draft. It will not be published immediately.
How It Works
- The Isurus background job system checks for releases whose scheduled publication time has arrived and publishes them automatically.
- Scheduled releases display a blue Scheduled badge on both the release list and the release detail page, along with the scheduled date and time.
- You can cancel or reschedule a pending release at any time from the release edit page.
- Once the scheduled time passes and the release is published, it behaves like any other published release.
Executors
CI agents support three execution modes: shell, docker, and incus. The agent auto-detects which executors are available on the host machine at startup and only accepts jobs that match an available executor.
Shell Executor
The shell executor runs commands directly on the agent's host machine. No executor block is needed — simply omit docker: and incus::
steps:
- name: test
commands:
- go test ./...
The shell executor is useful when:
- You need direct access to host tools and files.
- Docker is not available on the agent.
- You want faster execution without container overhead.
Note: The shell executor is disabled by default for security reasons. An administrator must enable it during agent registration.
Docker Executor
The Docker executor runs each step inside a container with the specified image. Use the docker: block with an image field:
steps:
- name: test
docker:
image: golang:1.26
commands:
- go test ./...
The Docker executor provides:
- Consistent, reproducible build environments.
- Isolation between steps.
- Access to any image from a container registry.
Docker must be installed and accessible on the agent host. The agent auto-detects Docker availability at startup.
Incus Executor
The Incus executor runs each step inside a system container or virtual machine managed by Incus. Use the incus: block with an image field referencing an Incus image:
steps:
- name: build
incus:
image: images:ubuntu/24.04
commands:
- apt-get update
- apt-get install -y golang
- go build ./...
The Incus executor provides:
- Full OS-level isolation (system containers or VMs).
- Access to the Linux Containers image server and custom Incus remotes.
- A good fit for agents running on bare metal or illumos/FreeBSD hosts where Docker is unavailable.
Incus must be installed and the agent user must have permission to manage instances. The agent auto-detects Incus availability at startup.
CI Agents
CI agents are worker processes that poll the Isurus forge for pipeline jobs, execute them, and report results.
How Agents Work
- An agent registers with the forge and receives an authentication token.
- The agent polls the forge for pending pipelines.
- When a job is available, the agent claims it, clones the repository, and executes each step.
- Step logs are streamed back to the forge in real time.
- The agent reports the final status (success, failure, error) when the pipeline completes.
Agent Registration
Agents are registered through the Admin > CI Agents panel in the web UI. Each agent gets a unique token for authentication.
Supported Platforms
The isurus-agent binary is cross-compiled for:
| Platform | Architecture |
|---|---|
| Linux | amd64 |
| Linux | arm64 |
| illumos | amd64 |
| FreeBSD | amd64 |
| macOS | arm64 (Apple Silicon) |
CI Environment Variables
The following environment variables are automatically available in every pipeline step:
| Variable | Description |
|---|---|
CI |
Always true |
CI_PIPELINE_ID |
Pipeline database ID |
CI_PIPELINE_NUMBER |
Pipeline sequential number for the repo |
CI_PIPELINE_NUM |
Alias for CI_PIPELINE_NUMBER |
CI_COMMIT |
Full commit hash |
CI_BRANCH |
Branch name |
CI_EVENT |
Event type (push or tag) |
CI_REPO |
Repository name |
CI_REPO_NAME |
Repository name (alias) |
CI_ORG |
Organization name |
CI_REPO_URL |
Full URL to the repository |
CI_SERVER |
Server base URL |
CI_SERVER_URL |
Server base URL (alias) |
CI_TAG |
Tag name (only set for tag events) |