Understanding CI Syntax:
A Complete Documentation

As developers who frequently utilize various CI platforms in our workflows, we found that many existing solutions are often complex and costly. This realization led to the creation of TurtleCI—a user-friendly and affordable alternative, inspired by the best CI platforms available today.


Below is a straightforward and comprehensive guide to help you understand how to use TurtleCI effectively.

In our CI we value the value of composition, mix-ins so there are 2 file type you would need to know:

<workflow-file-type>.yml

<module-file-type>.yml
We would organize <module-file-type>.yml into a special mods folder and in other .yml files, you can compile them to use. This should enable a large multitude of custom logic and help organize your deployment code logic neatly by reducing repeated codes.
Anatomy of <workflow-file-type>.yml
  1. version
  2. name
  3. on.<action-type>
  4. on.<action-type>.branches.<branch>
  5. on.<action-type>.tags.<tag>
  6. env
  7. jobs
  8. jobs.<jobId>
  9. jobs.<jobId>.builder
  10. jobs.<jobId>.requires
  11. jobs.<jobId>.steps
  12. jobs.<jobId>.steps[*].uses
  13. jobs.<jobId>.steps[*].run
  14. jobs.<jobId>.steps[*].name
Anatomy of <module-file-type>.yml
  1. modules
  2. modules.<moduleId>.steps
  3. modules.<moduleId>.steps[*].uses
  4. modules.<moduleId>.steps[*].run
  5. modules.<moduleId>.steps[*].name

Here is the folder structure:

.turtleci/
  mods/
    module-1.yml
    module-2.yml
    module-3.yml
  workflow-id-1.yml
  workflow-id-2.yml
Version (optional)
The version of the configuration file, right now we are at our first release so version 1 is expected. You can also removed this option and we would default to the latest version.
Name (optional)
If a workflow name is not specified, the system defaults to using the file's name. While this approach saves time, it may result in less descriptive workflow identifiers. Therefore, we recommend assigning meaningful names to your workflows to enhance clarity and maintainability.

on.<action-type>

We are currently supporting these <action-type>:
  • push - When a branch is received a push, we would trigger the CI on the branch that received the new codes.
  • pull_request - For platform that supports creating pull requests, otherwise you can safely ignore this.

on.<action-type>.branches.<branch>

For filtering action type to specific branches only. We accept glob patterns, wild cards to match branches easier.
Example
on:
  push:
    branches:
      - production
      - test
      - staging
      - 'features/**'
      - 'fix/*'

on.<action-type>.tags.<tag>

For filtering action type to specific tags only. We accept glob patterns, wild cards to match tags easier.
Example
on:
  push:
    tags:
      - 'release-1'
      - 'release-2-*'
      - 'dist-*'
Environment
A list of possible environment, right now we haven’t supported the ability to separate run time env and bundled app env yet. You would need to write logic to add desired env to your app’s env file. You can have 2 types of env:
  • Hard-coded env
  • Secrets env
Hard-coded environment
We supported these primitive types:
  • boolean
  • string
  • number
Example
on:
  push:
    tags:
      - 'release-1'
      - 'release-2-*'
      - 'dist-*'
Currently, all configurable secrets are shared across environments. We recognize the importance of environment-specific secret management and are actively working to implement this feature in the near future.
Secrets environment
You can reference configurable env from the dashboard using ${{ secrets.<name> }} syntax.
jobs
In this section, you define your workflows. By default, TurtleCI executes jobs concurrently to optimize time efficiency. To specify dependencies between jobs, use the requires keyword to indicate that a job should wait for the completion of its prerequisite jobs. For detailed documentation, please refer to the section below.
jobs.<jobId>
Job names must be alphanumeric strings without special characters. While there is no strict limit on the number of jobs you can define, please be aware that jobs exceeding the current execution time threshold of one hour may be terminated. This threshold is subject to change in the future.
Example
jobs:
  myAwesomeJobThatIsTooCoolToHandleIthink:
    steps:
      - runs: echo "foo"
jobs.<jobId>.builder
Right now we are only supporting tags of the operational system that you desire. Later we would also support selecting pre-built container images. For now, you can provider builder as:
  • string
  • array
We support the following keywords for OS:
  • linux - coming soon
  • ubuntu - coming soon
  • redhat - coming soon
  • macos - We are using a fleet of MacOS Ventura and MacOS Monterey, in the future we would add more granular control over how you can specify the OS version
Example
jobs:
  myAwesomeJobThatIsTooCoolToHandleIthink:
    builder:
      - linux
      - macos
jobs.<jobId>.requires
Because by default our jobs is running in parallel, you would need this keywords to create an order in your jobs:
jobs:
  myAwesomeJobThatIsTooCoolToHandleIthink:
    builder:
      - macos
  mySecondAwesomeJobThatIsTooCoolToHandleIthink:
    requires:
      - myAwesomeJobThatIsTooCoolToHandleIthink
    builder:
      - linux
jobs.<jobId>.steps
These are the steps that would be execute in order inside of each jobs.
jobs.<jobId>.steps[*].uses
This is where you would declare which part you would want to re-use from your modules. When compiling the scripts, we would read the id in here and execute the scripts accordingly.
By default, we create the function:
  • checkout - to clone and checkout code from the current interacted branch in your repository.
jobs:
  myAwesomeJobThatIsTooCoolToHandleIthink:
    builder:
      - macos
    steps:
      - uses: checkout
      - uses: otherPrecompliedModule
jobs.<jobId>.steps[*].run
This is where you specify your command. Remember that each run is a different process, so if you have process specific variables, you can place them in a single run.
Example 1:
- name: Install Bun dependencies
  run: bun install
Example 2:
- name: Install and build because why no
  run: bun install && bun run build
jobs.<jobId>.steps[*].name
Assigning a name to your workflow is optional; if omitted, the system will default to using the file's name.
jobs.<jobId>.steps[*].working-directory
This is a place where you want to run your command.
Example:
- name: Install Bun dependencies
  run: bun install
  working-directory: ./app

modules

This is where you compose re-usable commands across files.
modules.<moduleId>.steps
The way to define is similar to jobs.<jobId>.steps you can read how to define steps here Ci Syntax