Appearance
Proposed syntax for v1
INFO
Most of the syntax proposed on this site is already implemented. Not all features are covered here, just the main ones.
This syntax aims to be as simple as possible whilst covering most ci/cd use cases.
There are plenty of advanced features such as user inputs, delaying workflows, etc... which aren't covered in this document
Workflows
A workflow file is the entry point for each of our pipelines. We define a workflow in a project with a new .pandaci/[name].workflow.ts
.
We run workflows using the DENO runtime. This enables us to run a given workflow without needing to first install all dependencies (and irrelevant dependencies e.g. in a monorepo). This would be really slow!
Jobs
We define jobs within a workflow. They describe the runner (e.g. a vm) in which we are to run our tasks.
When self-hosting, the default runner is 'local' which runs the job directly on the api backend.
You can specify a self-hosted runner by providing a http(s) address for the runner e.g. 'https://my-panda-ci-runner.com'
ts
import { job } from "@pandaci/core";
job("dev - deploy", { runner: "local" }, () => {
// ...
});
Tasks
A job contains 1 or more tasks. These are where we run our commands.
A task can either be docker
or native
Docker tasks
Specify a docker image and all the commands within the task will be executed within that docker image.
Since this image is run inside a job, multiple tasks can communicate with one another. We can use tasks to spin up temporary services such as a database for integration testing
ts
import { job, task } from "@pandaci/core";
job("dev - deploy", () => {
task.docker("Build", { image: "node:22" }, () => {
// ...
});
});
Docker volumes
To share data between tasks or cache files, we can create volumes which can be added to a task.
A volume can optionally be mounted to a path directly on the job runner using the host option. This can enable persistent data between runs.
ts
import { job, task, volume } from "@pandaci/core";
job("dev - deploy", () => {
const cache = volume({ name: "cache", host: ".cache" });
task.docker("Build", { image: "node:22", volumes: [cache(".cache")] }, () => {
// ...
});
});
Native tasks
These tasks run the shell commands directly in the job/runner.
ts
import { job, task } from "@pandaci/core";
job("dev - deploy", () => {
task.native("Build", () => {
// ...
});
});
Exec
These are the commands/functions we are running inside our tasks.
Shell commands
Runs the given command
ts
import { job, task, $ } from "@pandaci/core";
job("dev - deploy", () => {
task.native("Build", () => {
$`echo 'Hello World'`;
});
});
$ api
$.cwd("./src")
Sets the directory to run the command in.text()
Parses the given output into a string.json()
Parses the given output into a json.nothrow()
Avoids throwing exceptions on non 0 exit codes.throws()
Ensures we throw on a non 0 exit code (default)
Evaluating Typescript
We can pass ts/js code which will be executed in the task
ts
job("dev - deploy", () => {
task.native("Build", () => {
$.evaluate(() => {
console.log("hello world");
});
});
});
This ts/js will need to be serializable so in order to use any external inputs, we will need to pass them in as variables.