How hard can it be to build a CI/CD system?
How hard can it be to build a CI/CD system? That question stuck with me long enough that I actually started building one. Not because someone asked me to. Not because I spotted a market gap. Just because the question wouldn't go away. The trigger was Concourse CI. I've been using it for a while and what I love about it is the resource abstraction, an interface that anything external has to follow. Check for new versions, pull them, push back. As a Go developer this kind of clean interface resonates with me. Everything in the pipeline is just something that implements that contract. But the operational overhead is significant. And I needed CI for my own side projects anyway, games and open source tools that require custom environments GitHub Actions can't provide. So I started building. A single binary that could also scale horizontally when needed. Start with nothing, grow when you need to. You start like this: ./pikoci server \ --db-system mem \ --pubsub-system mem \ --run-worker \ --pipeline-config pipeline.hcl That's a complete CI/CD system. In memory, no files, no external services. When you want persistence, add --db-system sqlite. When you need distributed workers, add NATS and start workers on other machines. The pipeline config never changes. Four pluggable abstractions PikoCI has four concepts you define in HCL and can source from a URL: resource types, runners, service types, and secret types. Each follows the same pattern: define the type once, instantiate it with params. A resource_type defines how to watch something for changes and fetch it. A resource is an instance of it: resource_type "git" { source = "pikoci://git" # built-in } resource "git" "my-app" { params { url = "https://github.com/org/app" name = "app" } check_interval = "@every 1m" } A runner_type defines where tasks execute. The docker and exec runners are built-in, no declaration needed. Here's what the docker runner looks like under the hood, in case you want to define your own: # this is



