My simplified DSL for Tekton is coming along nicely, I’ve spent a fair bit of time in the evenings and at the weekends exploring how to integrate a script-based DSL with Tekton Tasks and I’ve made some progress.

This is a minimal .tekton_ci.yaml file that will use a buildah Task in Tekton to build and push an image to Quay.io (or any Image Repository).

This service is still alpha quality, and not for use in production.

The pipeline definition

build-image:
  rules:
    - if: vars.CI_COMMIT_BRANCH != 'master'
      when: never
  tekton:
    taskRef: buildah
    params:
     - name: IMAGE
       expr: "'quay.io/bigkevmcd/github-tool:master-' + vars.CI_COMMIT_SHORT_SHA"

This is executed with the default ServiceAccount parameter passed to the container, this ServiceAccount needs to have a DockerConfigJSON type secret available to it, to authenticate the buildah image push.

$ kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson

You can provide the serviceAccountName for a PipelineRun explicitly in the configuration:

tekton:
  serviceAccountName: my-test-account
build-image:
  tekton:
    taskRef: buildah
    params:
     - name: IMAGE
       expr: "'quay.io/bigkevmcd/github-tool:master-' + vars.CI_COMMIT_SHORT_SHA"

The rules indicate that this should only build on pushes to the master branch (the syntax for rules is probably going to diverge1 further from GitLab’s CI rules).

The rules are implemented at PipelineRun generation time, so the build-image task would not be included in the PipelineRun when the code is building the Pipeline if the expression didn’t match.

The tekton key in the build-image task can replace a script element, and this is referencing a Task named buildah.

Finally, the IMAGE is dynamically calculated based on the commit.

"'quay.io/bigkevmcd/github-tool:master-' + vars.CI_COMMIT_SHORT_SHA"

This is a CEL expression, the CEL context has some keys which can be used and this is using a trimmed version of the commit SHA, the image tag will look like master-4b84d50.

While this is fairly simple, in practice, running tests before build is more normal.

Running the tests before building an image

image: golang:latest

before_script:
  - wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.24.0

stages:
  - test
  - update-image

test-go:
  stage: test
  script:
    - go mod download
    - go fmt ./...
    - go vet ./...
    - ./bin/golangci-lint run
    - go test -race ./...

build-image:
  stage: update-image
  rules:
    - if: vars.CI_COMMIT_BRANCH != 'master'
      when: never
  tekton:
    taskRef: buildah
    params:
     - name: IMAGE
       expr: "'quay.io/bigkevmcd/github-tool:master-' + vars.CI_COMMIT_SHORT_SHA"

In this case, I’ve sequenced the test stage before the update-image, which means that the build-image task will not execute unless the test-go task is successful, for pushes to master, no image will be built if the test stage fails.

The buildah task

This is using the standard Tekton catalog buildah task straight from the catalog with:

$ kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/v1beta1/buildah/buildah.yaml

Splitting the Go tests out to a task

If you want to standardise your Go tests, it’s easy to extract the script out into a Task.

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: go-test-task
spec:
  workspaces:
    - name: source
      description: the git source to execute on
  steps:
    - name: go-mod-setup
      image: golang:latest
      workingDir: $(workspaces.source.path)
      command: ["go", "mod", "download"]

    - name: go-vet
      image: golang:latest
      workingDir: $(workspaces.source.path)
      command: ["go", "vet", "./..."]

    - name: ci-lint
      image: golangci/golangci-lint:v1.24.0
      workingDir: $(workspaces.source.path)
      command: ["golangci-lint", "run"]

    - name: go-test
      image: golang:latest
      workingDir: $(workspaces.source.path)
      command: ["go", "test", "./..."]

This means that the .tekton_ci.yaml file can be simplified to just.

stages:
  - test
  - update-image

test-go:
  stage: test
  tekton:
    taskRef: go-test-task

build-image:
  stage: update-image
  rules:
    - if: vars.CI_COMMIT_BRANCH != 'master'
      when: never
  tekton:
    taskRef: buildah
    params:
     - name: IMAGE
       expr: "'quay.io/bigkevmcd/github-tool:master-' + vars.CI_COMMIT_SHORT_SHA"
  1. The Tekton-CI implementation uses CEL syntax, the GitLab-CI implementation uses Ruby, refer to the CEL context documentation for more information.