Skip to content

Coming from Concourse

PikoCI's resource model is directly inspired by Concourse CI. If you're familiar with Concourse, you'll find the concepts similar but with some key differences.

Concept mapping

Concourse PikoCI Notes
Resource type resource_type Same concept. PikoCI uses shell commands instead of Docker images.
Resource resource Same concept.
Job job Same concept.
Get step get Same. Fetches a resource version.
Task step task Similar. Uses a runner instead of a task config with image_resource.
Put step put Same. Pushes to a resource.
image_resource runner Runners replace task image configuration. Define once, use everywhere.
Pipeline (YAML) Pipeline (HCL) HCL instead of YAML.
fly CLI pikoci client Similar commands: set-pipeline -> pipelines create/update.
Web UI Built-in UI Similar functionality.
passed constraint passed Same. Gates a resource version through upstream jobs.
trigger: true trigger = true Same. Auto-triggers the job on new versions.
on_success / on_failure / on_cancel / ensure Same names Same semantics. Available on both steps and jobs.
Webhook triggers Webhook triggers Same. POST /webhooks/<token> triggers a resource check.
serial_groups serial_groups Same concept. Cross-job mutual exclusion within a pipeline.
Teams Teams Similar. PikoCI has team-based scoping.

Key differences

Runners replace image_resource

In Concourse, every task requires an image_resource (a Docker image). In PikoCI, you define a runner that wraps execution:

Concourse:

jobs:
- name: test
  plan:
  - get: repo
    trigger: true
  - task: run-tests
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: golang}
      inputs:
      - name: repo
      run:
        path: sh
        args: ["-c", "cd repo && go test ./..."]

PikoCI:

runner_type "docker" {
  run {
    path = "docker"
    args = ["run", "--rm", "-v", "$WORKDIR:/workdir", "-w", "/workdir", "$image", "$cmd"]
  }
}

job "test" {
  get "git" "repo" {
    trigger = true
  }
  task "run-tests" {
    run "docker" {
      image = "golang:1.25"
      cmd   = "go test ./..."
    }
  }
}

Resource types use shell commands, not Docker images

Concourse resource types are Docker images implementing the check/in/out interface. PikoCI resource types are shell commands:

Concourse:

resource_types:
- name: git
  type: docker-image
  source:
    repository: concourse/git-resource

PikoCI:

resource_type "git" {
  params = ["url", "name"]
  check "exec" {
    path = "/bin/sh"
    args = ["-ec", "git ls-remote $param_url HEAD | awk '{print $1}'"]
  }
  pull "exec" {
    path = "/bin/sh"
    args = ["-ec", "git clone $param_url $param_name && git checkout $version_ref"]
  }
  push "exec" {
    path = "/bin/sh"
    args = ["-ec", "cd $param_name && git push"]
  }
}

Single binary deployment

Concourse requires PostgreSQL, a web node, and one or more worker nodes. PikoCI runs as a single binary with optional external dependencies:

# Concourse: multiple services
concourse web --postgres-* ... &
concourse worker --tsa-host ... &

# PikoCI: one command
pikoci server --jwt-secret my-secret

HCL instead of YAML

Pipelines use HCL syntax, which supports variables, string interpolation, and is less error-prone than YAML.

Known gaps vs Concourse

  • No built-in resource registry: Concourse ships with built-in resource types (git, s3, time, etc.). PikoCI ships with cron, git, and trigger resource types, plus github-check, slack, and discord notification types, but does not have the breadth of Concourse's registry. You can define your own resource types and notification types in HCL.

Migration tips

  1. Start by defining your resource types as shell commands
  2. Create a runner for your execution environment (e.g., Docker)
  3. Convert your pipeline YAML to HCL, mapping get/task/put steps
  4. Use variable blocks for values that were in your Concourse credential manager

A Concourse pipeline importer is planned.