Agents cicd-tekton-tasks-dev

Develop custom Tekton Tasks and reusable pipeline components. Use when creating Tasks for Tekton Hub, building ClusterTasks, designing parameterized Task libraries, or packaging Tekton components for distribution.

install
source · Clone the upstream repo
git clone https://github.com/aRustyDev/agents
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/skills/cicd-tekton-tasks-dev" ~/.claude/skills/arustydev-agents-cicd-tekton-tasks-dev && rm -rf "$T"
manifest: content/skills/cicd-tekton-tasks-dev/SKILL.md
source content

Tekton Tasks Development

Guide for developing custom, reusable Tekton Tasks - the fundamental building blocks of Tekton Pipelines.

When to Use This Skill

  • Creating custom Tekton Tasks
  • Building reusable Task libraries
  • Publishing Tasks to Tekton Hub
  • Designing parameterized, flexible Tasks
  • Creating ClusterTasks for organization-wide use
  • Understanding Task structure, steps, and sidecars

Task Structure

Basic Task Definition

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: my-task
  labels:
    app.kubernetes.io/version: "1.0.0"
  annotations:
    tekton.dev/categories: Build
    tekton.dev/tags: docker, build
    tekton.dev/displayName: "Build Docker Image"
    tekton.dev/platforms: "linux/amd64,linux/arm64"
spec:
  description: >-
    This task builds a Docker image from source code.

  params:
    - name: IMAGE
      description: Name of the image to build
      type: string

    - name: DOCKERFILE
      description: Path to Dockerfile
      type: string
      default: ./Dockerfile

  workspaces:
    - name: source
      description: Source code workspace

  results:
    - name: IMAGE_DIGEST
      description: Digest of the built image
    - name: IMAGE_URL
      description: URL of the built image

  steps:
    - name: build
      image: gcr.io/kaniko-project/executor:latest
      args:
        - --dockerfile=$(params.DOCKERFILE)
        - --destination=$(params.IMAGE)
        - --context=$(workspaces.source.path)
        - --digest-file=$(results.IMAGE_DIGEST.path)
      script: |
        #!/usr/bin/env sh
        echo -n "$(params.IMAGE)" > $(results.IMAGE_URL.path)

Parameters

Parameter Types

spec:
  params:
    # String parameter (default type)
    - name: message
      description: Message to display
      type: string
      default: "Hello World"

    # Array parameter
    - name: extra-args
      description: Additional arguments
      type: array
      default:
        - "--verbose"
        - "--debug"

    # Object parameter
    - name: config
      description: Configuration object
      type: object
      properties:
        url:
          type: string
        timeout:
          type: string
      default:
        url: "https://api.example.com"
        timeout: "30s"

Using Parameters

steps:
  - name: run
    image: alpine
    script: |
      echo "Message: $(params.message)"

  # Array parameters in args
  - name: build
    image: node:20
    args:
      - "npm"
      - "run"
      - "build"
      - "$(params.extra-args[*])"  # Expands array

  # Object parameter access
  - name: call-api
    image: curlimages/curl
    script: |
      curl -X GET "$(params.config.url)" \
           --max-time "$(params.config.timeout)"

Parameter Validation

params:
  - name: environment
    description: Target environment
    type: string
    enum:
      - development
      - staging
      - production

  - name: replicas
    description: Number of replicas
    type: string
    default: "1"
    # Pattern validation (via webhook/policy)

Steps

Step Definition

steps:
  - name: step-name
    image: alpine:3.19
    imagePullPolicy: IfNotPresent

    # Working directory
    workingDir: $(workspaces.source.path)

    # Command and args
    command: ["/bin/sh"]
    args: ["-c", "echo hello"]

    # Or use script (preferred)
    script: |
      #!/usr/bin/env bash
      set -euo pipefail
      echo "Running in $(pwd)"

    # Environment variables
    env:
      - name: MY_VAR
        value: "static-value"
      - name: PARAM_VAR
        value: $(params.my-param)
      - name: SECRET_VAR
        valueFrom:
          secretKeyRef:
            name: my-secret
            key: api-key

    # Resource limits
    resources:
      requests:
        memory: 256Mi
        cpu: 100m
      limits:
        memory: 512Mi
        cpu: 500m

    # Security context
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000

    # Timeout
    timeout: 10m

Multi-Step Tasks

steps:
  - name: fetch-dependencies
    image: node:20
    workingDir: $(workspaces.source.path)
    script: |
      npm ci

  - name: run-tests
    image: node:20
    workingDir: $(workspaces.source.path)
    script: |
      npm test

  - name: build
    image: node:20
    workingDir: $(workspaces.source.path)
    script: |
      npm run build

Step Ordering

Steps execute sequentially in order defined. Use separate Tasks with

runAfter
in Pipelines for parallel execution.

Workspaces

Workspace Types

spec:
  workspaces:
    # Required workspace
    - name: source
      description: Source code
      mountPath: /workspace/source

    # Optional workspace
    - name: cache
      description: Build cache
      optional: true
      mountPath: /workspace/cache

    # Read-only workspace
    - name: config
      description: Configuration files
      readOnly: true

Using Workspaces

steps:
  - name: build
    image: node:20
    script: |
      cd $(workspaces.source.path)
      npm ci
      npm run build

      # Use optional workspace if available
      if [ -d "$(workspaces.cache.path)" ]; then
        cp -r node_modules $(workspaces.cache.path)/
      fi

Results

Defining Results

spec:
  results:
    - name: digest
      description: Image digest
      type: string

    - name: urls
      description: List of URLs
      type: array

    - name: metadata
      description: Build metadata
      type: object
      properties:
        version:
          type: string
        timestamp:
          type: string

Writing Results

steps:
  - name: build
    image: alpine
    script: |
      # String result
      echo -n "sha256:abc123" > $(results.digest.path)

      # Array result (JSON)
      echo '["http://url1", "http://url2"]' > $(results.urls.path)

      # Object result (JSON)
      cat > $(results.metadata.path) << EOF
      {
        "version": "1.0.0",
        "timestamp": "$(date -Iseconds)"
      }
      EOF

Result Size Limits

Results are limited to 4KB. For larger data, use workspaces or external storage.

Sidecars

Database Sidecar

spec:
  sidecars:
    - name: postgres
      image: postgres:15
      env:
        - name: POSTGRES_DB
          value: testdb
        - name: POSTGRES_USER
          value: test
        - name: POSTGRES_PASSWORD
          value: test
      readinessProbe:
        exec:
          command: ["pg_isready", "-U", "postgres"]
        initialDelaySeconds: 5
        periodSeconds: 2

  steps:
    - name: test
      image: node:20
      env:
        - name: DATABASE_URL
          value: postgres://test:test@localhost:5432/testdb
      script: |
        npm test

Service Mesh Sidecar

sidecars:
  - name: nginx
    image: nginx:alpine
    ports:
      - containerPort: 8080
    volumeMounts:
      - name: config
        mountPath: /etc/nginx/conf.d

  volumes:
    - name: config
      configMap:
        name: nginx-config

Volume Mounts

EmptyDir Volumes

spec:
  stepTemplate:
    volumeMounts:
      - name: temp
        mountPath: /tmp/shared

  volumes:
    - name: temp
      emptyDir: {}

  steps:
    - name: generate
      image: alpine
      script: |
        echo "data" > /tmp/shared/file.txt

    - name: consume
      image: alpine
      script: |
        cat /tmp/shared/file.txt

Secret Volumes

spec:
  volumes:
    - name: docker-config
      secret:
        secretName: docker-credentials

  steps:
    - name: push
      image: gcr.io/kaniko-project/executor
      volumeMounts:
        - name: docker-config
          mountPath: /kaniko/.docker

StepTemplate

Apply common configuration to all steps:

spec:
  stepTemplate:
    image: node:20
    workingDir: $(workspaces.source.path)
    env:
      - name: NODE_ENV
        value: production
    resources:
      requests:
        memory: 256Mi

  steps:
    - name: install
      script: npm ci

    - name: build
      script: npm run build

    - name: test
      script: npm test

ClusterTasks

Organization-wide reusable Tasks:

apiVersion: tekton.dev/v1beta1
kind: ClusterTask
metadata:
  name: organization-build
spec:
  params:
    - name: image
      type: string
  steps:
    - name: build
      image: gcr.io/kaniko-project/executor
      args:
        - --destination=$(params.image)

Note: ClusterTasks are deprecated in favor of remote resolution.

Task Bundles

Package Tasks as OCI artifacts:

# Bundle a Task
tkn bundle push docker.io/myorg/my-task:v1 \
  -f my-task.yaml

# Use in TaskRun
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: my-run
spec:
  taskRef:
    resolver: bundles
    params:
      - name: bundle
        value: docker.io/myorg/my-task:v1
      - name: name
        value: my-task

Testing Tasks

TaskRun for Testing

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: test-my-task-
spec:
  taskRef:
    name: my-task
  params:
    - name: IMAGE
      value: test-image:latest
  workspaces:
    - name: source
      emptyDir: {}

Local Testing with tkn

# Start a TaskRun
tkn task start my-task \
  -p IMAGE=test:latest \
  -w name=source,emptyDir="" \
  --showlog

# View logs
tkn taskrun logs my-task-run-xyz -f

# Describe run
tkn taskrun describe my-task-run-xyz

Tekton Hub Publication

Catalog Structure

catalog/
└── task/
    └── my-task/
        ├── 0.1/
        │   ├── my-task.yaml
        │   └── README.md
        └── 0.2/
            ├── my-task.yaml
            └── README.md

Required Annotations

metadata:
  annotations:
    tekton.dev/pipelines.minVersion: "0.50.0"
    tekton.dev/categories: Build
    tekton.dev/tags: docker, build
    tekton.dev/displayName: "My Task"
    tekton.dev/platforms: "linux/amd64,linux/arm64"

README Template

# My Task

This task does X.

## Parameters

| Name | Description | Default |
|------|-------------|---------|
| IMAGE | Image to build | - |
| DOCKERFILE | Dockerfile path | ./Dockerfile |

## Workspaces

| Name | Description |
|------|-------------|
| source | Source code |

## Results

| Name | Description |
|------|-------------|
| IMAGE_DIGEST | Built image digest |

## Usage

\`\`\`yaml
- name: build
  taskRef:
    name: my-task
  params:
    - name: IMAGE
      value: myimage:latest
  workspaces:
    - name: source
      workspace: shared-workspace
\`\`\`

## Example TaskRun

\`\`\`yaml
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: my-task-run
spec:
  taskRef:
    name: my-task
  params:
    - name: IMAGE
      value: myimage:latest
  workspaces:
    - name: source
      persistentVolumeClaim:
        claimName: source-pvc
\`\`\`

Advanced Patterns

Conditional Execution in Steps

steps:
  - name: maybe-deploy
    image: alpine
    script: |
      if [ "$(params.DEPLOY)" = "true" ]; then
        echo "Deploying..."
        # deployment commands
      else
        echo "Skipping deployment"
      fi

Retry Logic

steps:
  - name: flaky-operation
    image: alpine
    script: |
      MAX_RETRIES=3
      RETRY_COUNT=0

      until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
        if curl -f https://api.example.com/health; then
          exit 0
        fi
        RETRY_COUNT=$((RETRY_COUNT+1))
        sleep $((RETRY_COUNT * 5))
      done

      exit 1

Dynamic Script Generation

steps:
  - name: generate-script
    image: alpine
    script: |
      cat > /workspace/run.sh << 'EOF'
      #!/bin/sh
      $(params.CUSTOM_SCRIPT)
      EOF
      chmod +x /workspace/run.sh

  - name: execute
    image: alpine
    script: |
      /workspace/run.sh

Debugging

Debug Step

steps:
  - name: debug
    image: busybox
    script: |
      echo "=== Workspace contents ==="
      ls -la $(workspaces.source.path)

      echo "=== Environment ==="
      env | sort

      echo "=== Parameters ==="
      echo "IMAGE: $(params.IMAGE)"

Interactive Debugging

# Add a sleep step for debugging
steps:
  - name: debug-pause
    image: busybox
    script: |
      echo "Pausing for debugging..."
      sleep 3600
# Exec into the pod
kubectl exec -it my-taskrun-pod-xyz -c step-debug-pause -- /bin/sh

Debugging Checklist

  • Verify Task YAML syntax with
    kubectl apply --dry-run=client
  • Check all required params have values or defaults
  • Verify workspace paths are correct
  • Check step images are accessible
  • Validate result paths match declarations
  • Test with minimal TaskRun first
  • Check resource limits aren't too restrictive
  • Verify secrets and configmaps exist

References