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.mdsource 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