├── .gitignore ├── config ├── image │ └── base │ │ ├── Dockerfile │ │ └── exploit │ │ └── Dockerfile ├── opa │ ├── certificate-common-name.json │ └── maintainers.rego ├── tekton │ ├── trigger │ │ ├── 04_event_listener.yaml │ │ ├── 01_binding.yaml │ │ ├── 03_trigger.yaml │ │ └── 02_template.yaml │ ├── task │ │ ├── apply_manifest_task.yaml │ │ ├── update_deployment_task.yaml │ │ ├── conftest.yaml │ │ ├── base_image.yaml │ │ └── cosign_task.yaml │ └── pipeline │ │ └── pipeline.yaml └── k8s │ ├── service.yaml │ ├── route.yaml │ └── deployment.yaml ├── Dockerfile ├── MAINTAINERS.json ├── scripts ├── conftest-verify.sh ├── cleanup.sh ├── util.sh └── sigstore-demo.sh ├── main.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /config/image/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/ubi-minimal:latest 2 | -------------------------------------------------------------------------------- /config/opa/certificate-common-name.json: -------------------------------------------------------------------------------- 1 | { "certificate-common-name": "ifont@redhat.com" } 2 | -------------------------------------------------------------------------------- /config/image/base/exploit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/ubi-minimal:latest 2 | 3 | CMD ["echo", "something malicious"] 4 | -------------------------------------------------------------------------------- /config/tekton/trigger/04_event_listener.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: EventListener 3 | metadata: 4 | name: sigstore-demo-app 5 | spec: 6 | serviceAccountName: pipeline 7 | triggers: 8 | - triggerRef: sigstore-demo-app-trigger 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/go-toolset as builder 2 | COPY main.go . 3 | RUN go build -o ./app . 4 | 5 | FROM gcr.io/ifontlabs/ubi8-minimal:stable 6 | LABEL base.image="gcr.io/ifontlabs/ubi8-minimal:stable" 7 | CMD ["./app"] 8 | COPY --from=builder /opt/app-root/src/app . 9 | -------------------------------------------------------------------------------- /MAINTAINERS.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainers": [ 3 | { 4 | "name": "Bob Callaway", 5 | "email": "bcallawa@redhat.com" 6 | }, 7 | { 8 | "name": "Ivan Font", 9 | "email": "ifont@redhat.com" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /config/k8s/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: sigstore-demo-app 5 | labels: 6 | app: sigstore-demo-app 7 | spec: 8 | ports: 9 | - port: 8080 10 | protocol: TCP 11 | targetPort: 8080 12 | selector: 13 | app: sigstore-demo-app 14 | type: ClusterIP 15 | -------------------------------------------------------------------------------- /scripts/conftest-verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | source "$(dirname "${BASH_SOURCE}")/util.sh" 5 | 6 | podman run --rm -v "${ROOT_DIR}":/project:Z docker.io/openpolicyagent/conftest:v0.23.0 test \ 7 | --data "${MAINTAINERS}" -p "${MAINTAINERS_POLICY}" --parser json "${CERTIFICATE_COMMON_NAME}" 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func helloHandler(w http.ResponseWriter, req *http.Request) { 10 | io.WriteString(w, "Hello World!\n") 11 | } 12 | 13 | func main() { 14 | // Hello world, the web server 15 | http.HandleFunc("/", helloHandler) 16 | log.Fatal(http.ListenAndServe(":8080", nil)) 17 | } 18 | -------------------------------------------------------------------------------- /config/tekton/trigger/01_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: TriggerBinding 3 | metadata: 4 | name: sigstore-demo-app 5 | spec: 6 | params: 7 | - name: git-repo-url 8 | value: $(body.repository.url) 9 | - name: git-repo-name 10 | value: $(body.repository.name) 11 | - name: git-revision 12 | value: $(body.head_commit.id) 13 | -------------------------------------------------------------------------------- /config/opa/maintainers.rego: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | deny_not_maintainer[msg] { 4 | cert_cname := input["certificate-common-name"] 5 | maintainers := [ maintainer | maintainer := data.maintainers[_]; maintainer.email == cert_cname ] 6 | count(maintainers) == 0 7 | msg := sprintf("Certificate with email=%v does not match list of maintainers=%v", [cert_cname, data.maintainers]) 8 | } 9 | -------------------------------------------------------------------------------- /config/tekton/trigger/03_trigger.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: Trigger 3 | metadata: 4 | name: sigstore-demo-app-trigger 5 | spec: 6 | serviceAccountName: pipeline 7 | bindings: 8 | - ref: sigstore-demo-app 9 | template: 10 | # In newer trigger versions the 'name' field will be deprecated and is 11 | # replaced by 'ref' field. 12 | name: sigstore-demo-app 13 | -------------------------------------------------------------------------------- /config/k8s/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: sigstore-demo-app 5 | labels: 6 | app: sigstore-demo-app 7 | spec: 8 | host: sigstore-demo-app.apps.sigstore-demo.bcallawa.dev # Modify to use your FQDN e.g. .apps... 9 | tls: 10 | termination: edge # Relies on OpenShift setup with Let's Encrypt. 11 | to: 12 | kind: Service 13 | name: sigstore-demo-app 14 | -------------------------------------------------------------------------------- /config/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sigstore-demo-app 5 | labels: 6 | app: sigstore-demo-app 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: sigstore-demo-app 12 | template: 13 | metadata: 14 | labels: 15 | app: sigstore-demo-app 16 | spec: 17 | containers: 18 | - name: sigstore-demo-app 19 | image: quay.io/ifont/sigstore-demo-app:latest 20 | ports: 21 | - containerPort: 8080 22 | imagePullPolicy: Always 23 | -------------------------------------------------------------------------------- /config/tekton/task/apply_manifest_task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: apply-manifests 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: manifest_dir 10 | description: The directory in source that contains yaml manifests 11 | type: string 12 | default: "config/k8s" 13 | steps: 14 | - name: apply 15 | image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest 16 | workingDir: /workspace/source 17 | script: | 18 | #!/usr/bin/env bash 19 | echo Applying manifests in $(inputs.params.manifest_dir) directory 20 | oc apply -f $(inputs.params.manifest_dir) 21 | echo ----------------------------------- 22 | -------------------------------------------------------------------------------- /config/tekton/task/update_deployment_task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: update-deployment 5 | spec: 6 | params: 7 | - name: deployment 8 | description: The name of the deployment patch the image 9 | type: string 10 | - name: IMAGE 11 | description: Location of image to be patched with 12 | type: string 13 | steps: 14 | - name: patch 15 | image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest 16 | script: | 17 | #!/usr/bin/env bash 18 | oc patch deployment $(inputs.params.deployment) --patch='{"spec":{"template":{"spec":{ 19 | "containers":[{ 20 | "name": "$(inputs.params.deployment)", 21 | "image":"$(inputs.params.IMAGE)" 22 | }] 23 | }}}}' 24 | -------------------------------------------------------------------------------- /scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Removes all Tekton PipelineRuns and TaskRuns 4 | tkn pipelinerun delete --all -f 5 | tkn taskrun delete --all -f 6 | 7 | # Removes all tagged and untagged images for the base image 8 | for i in $(gcloud container images list-tags gcr.io/ifontlabs/ubi8-minimal --filter='tags:*' --format="get(digest)" --limit=100); do 9 | gcloud container images delete --force-delete-tags --quiet gcr.io/ifontlabs/ubi8-minimal@${i} 10 | done 11 | for i in $(gcloud container images list-tags gcr.io/ifontlabs/ubi8-minimal --filter='-tags:*' --format="get(digest)" --limit=100); do 12 | gcloud container images delete --force-delete-tags --quiet gcr.io/ifontlabs/ubi8-minimal@${i} 13 | done 14 | 15 | # Make sure we're at the latest HEAD on the main branch 16 | git fetch --all --prune 17 | git merge --ff-only 18 | -------------------------------------------------------------------------------- /scripts/util.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | ROOT_DIR="$(cd "$(dirname "$0")/.." ; pwd)" 5 | MAINTAINERS="${MAINTAINERS:-MAINTAINERS.json}" 6 | CERTIFICATE_COMMON_NAME="${CERTIFICATE_COMMON_NAME:-config/opa/certificate-common-name.json}" 7 | MAINTAINERS_POLICY="${MAINTAINERS_POLICY:-config/opa/maintainers.rego}" 8 | 9 | print-error-and-exit() { 10 | echo "$0: ERROR: ${1} not found." 11 | exit 1 12 | } 13 | 14 | if [[ ! -f ${ROOT_DIR}/${MAINTAINERS} ]]; then 15 | print-error-and-exit "MAINTAINERS=${ROOT_DIR}/${MAINTAINERS}" 16 | elif [[ ! -f ${ROOT_DIR}/${CERTIFICATE_COMMON_NAME} ]]; then 17 | print-error-and-exit "CERTIFICATE_COMMON_NAME=${ROOT_DIR}/${CERTIFICATE_COMMON_NAME}" 18 | elif [[ ! -f ${ROOT_DIR}/${MAINTAINERS_POLICY} ]]; then 19 | print-error-and-exit "MAINTAINERS_POLICY=${ROOT_DIR}/${MAINTAINERS_POLICY}" 20 | fi 21 | 22 | -------------------------------------------------------------------------------- /config/tekton/task/conftest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: conftest 5 | labels: 6 | app.kubernetes.io/version: "0.1" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: test 10 | spec: 11 | description: >- 12 | These tasks make it possible to use Conftest within your Tekton pipelines 13 | 14 | Conftest is a tool for testing configuration files using Open Policy Agent. 15 | 16 | workspaces: 17 | - name: source 18 | params: 19 | - name: files 20 | type: string 21 | - name: policy 22 | default: "policy" 23 | - name: output 24 | default: "stdout" 25 | - name: args 26 | type: array 27 | default: [] 28 | 29 | steps: 30 | - name: conftest 31 | workingDir: $(workspaces.source.path) 32 | image: docker.io/openpolicyagent/conftest:v0.23.0@sha256:e278dc39560217ab4d0478fc7db2f8aae26e09e572564a4567cf953c56812f8c #tag: v0.23.0 33 | command: 34 | - conftest 35 | - test 36 | - $(params.files) 37 | - -p 38 | - $(params.policy) 39 | - -o 40 | - $(params.output) 41 | - $(params.args) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sigstore-demo 2 | 3 | ## Setup 4 | 5 | ### Create Tekton Tasks 6 | 7 | ```shell 8 | oc apply -f ./config/tekton/task 9 | ``` 10 | 11 | ### Create Tekton Pipeline 12 | 13 | ```shell 14 | oc apply -f ./config/tekton/pipeline 15 | ``` 16 | 17 | ### Create Tekton Trigger 18 | 19 | ```shell 20 | oc apply -f ./config/tekton/trigger 21 | ``` 22 | 23 | ### Expose Tekton Event Listener Service 24 | 25 | Once the `el-sigstore-demo-app` service has been created by Tekton, expose it 26 | by running: 27 | 28 | ```shell 29 | oc expose service el-sigstore-demo-app 30 | ``` 31 | 32 | ### Add GitHub Webhook Manually 33 | 34 | Open GitHub repo (Go to Settings > Webhooks) click on `Add webhook`. Under 35 | Payload URL, paste the output of: 36 | 37 | ```shell 38 | echo $(oc get route el-sigstore-demo-app --template='http://{{.spec.host}}') 39 | ``` 40 | 41 | Select Content type as `application/json`. Add secret eg: `sigstore`. Click on 42 | `Add Webhook`. 43 | 44 | ### Test It 45 | 46 | Now when we perform any push event on the repo, it will trigger the pipeline 47 | with a new pipeline run. To test it, run: 48 | 49 | ```shell 50 | git commit -m "empty-commit" --allow-empty && git push origin main 51 | ``` 52 | -------------------------------------------------------------------------------- /config/tekton/task/base_image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: base-image 5 | spec: 6 | description: >- 7 | This task extracts the base image from a given container image by examining 8 | the container image labels. 9 | 10 | workspaces: 11 | - name: source 12 | params: 13 | - name: tlsverify 14 | type: string 15 | default: "true" 16 | - name: image 17 | type: string 18 | - name: base-image-label 19 | type: string 20 | results: 21 | - name: base-image 22 | description: The base image extracted from the label of the container image passed in 23 | steps: 24 | - name: inspect-image 25 | workingDir: $(workspaces.source.path) 26 | image: quay.io/skopeo/stable 27 | script: | 28 | #!/usr/bin/env bash 29 | skopeo inspect --tls-verify=$(params.tlsverify) docker://$(params.image) > inspect.json 30 | - name: extract-base-image 31 | workingDir: $(workspaces.source.path) 32 | image: docker.io/stedolan/jq:latest 33 | script: | 34 | #!/usr/bin/env bash 35 | cat inspect.json | jq -r '.Labels["base.image"]' > $(results.base-image.path) 36 | echo -n "IMAGE=$(params.image) was built using BASE_IMAGE=" 37 | cat $(results.base-image.path) 38 | -------------------------------------------------------------------------------- /config/tekton/trigger/02_template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: TriggerTemplate 3 | metadata: 4 | name: sigstore-demo-app 5 | spec: 6 | params: 7 | - name: git-repo-url 8 | description: The git repository url 9 | - name: git-revision 10 | description: The git revision 11 | default: main 12 | - name: git-repo-name 13 | description: The name of the deployment to be created / patched 14 | 15 | resourcetemplates: 16 | - apiVersion: tekton.dev/v1beta1 17 | kind: PipelineRun 18 | metadata: 19 | name: build-verify-deploy-$(tt.params.git-repo-name)-$(uid) 20 | spec: 21 | serviceAccountName: pipeline 22 | pipelineRef: 23 | name: build-verify-deploy 24 | params: 25 | - name: deployment-name 26 | value: $(tt.params.git-repo-name)-app 27 | - name: git-url 28 | value: $(tt.params.git-repo-url) 29 | - name: git-revision 30 | value: $(tt.params.git-revision) 31 | - name: IMAGE 32 | value: image-registry.openshift-image-registry.svc:5000/sigstore-demo/$(tt.params.git-repo-name)-app 33 | workspaces: 34 | - name: shared-workspace 35 | volumeClaimTemplate: 36 | spec: 37 | accessModes: 38 | - ReadWriteOnce 39 | resources: 40 | requests: 41 | storage: 500Mi 42 | -------------------------------------------------------------------------------- /config/tekton/task/cosign_task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: cosign 5 | labels: 6 | app.kubernetes.io/version: "0.1.0" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | spec: 10 | description: >- 11 | These tasks make it possible to use cosign within your Tekton pipelines 12 | 13 | cosign is a tool for signing and verifying software artifacts. 14 | 15 | workspaces: 16 | - name: source 17 | params: 18 | - name: command 19 | type: string 20 | - name: image 21 | type: string 22 | results: 23 | - name: email 24 | description: The email from the certificate common name in JSON format 25 | steps: 26 | - name: cosign 27 | workingDir: $(workspaces.source.path) 28 | image: gcr.io/projectsigstore/cosign/ci/cosign:v0.2.0 29 | env: 30 | - name: COSIGN_EXPERIMENTAL 31 | value: "1" 32 | script: | 33 | #!/busybox/env /busybox/sh 34 | CMD="cosign $(params.command) $(params.image)" 35 | echo "${CMD}" 36 | ERR_MSG="Failed to $(params.command) $(params.image)" 37 | ${CMD} &> result 38 | err=$? 39 | cat result 40 | if [[ ${err} -ne 0 ]]; then 41 | echo "${ERR_MSG}" 42 | exit 1 43 | fi 44 | - name: get-email 45 | workingDir: $(workspaces.source.path) 46 | image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest 47 | script: | 48 | #!/usr/bin/env bash 49 | set -o errexit 50 | CERT_CN=$(awk -F " " '/^Certificate common name/ {print $2}' result) 51 | echo "{ \"certificate-common-name\": \"${CERT_CN}\" }" > $(results.email.path) 52 | cat $(results.email.path) 53 | cat $(results.email.path) > ./certificate-common-name.json 54 | -------------------------------------------------------------------------------- /scripts/sigstore-demo.sh: -------------------------------------------------------------------------------- 1 | #/usr/bin/env bash 2 | 3 | # Recorded with the doitlive recorder 4 | #doitlive shell: /bin/bash 5 | #doitlive prompt: damoekri 6 | #doitlive speed: 3 7 | #doitlive env: COSIGN_EXPERIMENTAL=1 8 | #doitlive commentecho: true 9 | 10 | ## DEMO THE RUNNING APP 11 | google-chrome https://sigstore-demo-app.apps.sigstore-demo.bcallawa.dev/ 12 | 13 | # contents of repo 14 | ls -tr 15 | 16 | # list of maintainers 17 | vi MAINTAINERS.json 18 | 19 | ## Dockerfile for running app 20 | vi Dockerfile 21 | 22 | ## UNSIGNED CONTAINER IMAGE 23 | 24 | # build base container image 25 | podman build --no-cache -f config/image/base/Dockerfile . -t gcr.io/ifontlabs/ubi8-minimal:stable 26 | 27 | # push base container image 28 | podman push gcr.io/ifontlabs/ubi8-minimal:stable 29 | 30 | # make change to app 31 | vi main.go 32 | 33 | # commit change 34 | git commit -a -m "Update Hello World message" 35 | 36 | # push change 37 | git push 38 | 39 | # pipelinerun 40 | 41 | ## SIGNED CONTAINER IMAGE 42 | 43 | # sign existing container image 44 | cosign sign gcr.io/ifontlabs/ubi8-minimal:stable 45 | 46 | # re-run pipeline and deploy app changes 47 | 48 | ## STOLEN CREDENTIALS 49 | 50 | # Dockerfile for malicious image 51 | cat config/image/base/exploit/Dockerfile 52 | 53 | # build malicious image 54 | podman build --no-cache -f config/image/base/exploit/Dockerfile . -t gcr.io/ifontlabs/ubi8-minimal:stable 55 | 56 | # push malicious image 57 | podman push gcr.io/ifontlabs/ubi8-minimal:stable 58 | 59 | # sign malicious image 60 | cosign sign gcr.io/ifontlabs/ubi8-minimal:stable 61 | 62 | # make another change to app 63 | vi main.go 64 | 65 | # commit change 66 | git commit -a -m "Update Hello World message again" 67 | 68 | # push change 69 | git push 70 | 71 | # list of maintainers 72 | vi MAINTAINERS.json 73 | -------------------------------------------------------------------------------- /config/tekton/pipeline/pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: build-verify-deploy 5 | spec: 6 | workspaces: 7 | - name: shared-workspace 8 | params: 9 | - name: deployment-name 10 | type: string 11 | description: name of the deployment to be patched 12 | - name: git-url 13 | type: string 14 | description: url of the git repo for the code of deployment 15 | - name: git-revision 16 | type: string 17 | description: revision to be used from repo of the code for deployment 18 | default: "main" 19 | - name: IMAGE 20 | type: string 21 | description: image to be built from the code 22 | tasks: 23 | - name: fetch-repository 24 | taskRef: 25 | name: git-clone 26 | kind: ClusterTask 27 | workspaces: 28 | - name: output 29 | workspace: shared-workspace 30 | params: 31 | - name: url 32 | value: $(params.git-url) 33 | - name: subdirectory 34 | value: "" 35 | - name: deleteExisting 36 | value: "true" 37 | - name: revision 38 | value: $(params.git-revision) 39 | 40 | - name: build-image 41 | taskRef: 42 | name: buildah 43 | kind: ClusterTask 44 | params: 45 | - name: TLSVERIFY 46 | value: "false" 47 | - name: IMAGE 48 | value: $(params.IMAGE) 49 | workspaces: 50 | - name: source 51 | workspace: shared-workspace 52 | runAfter: 53 | - fetch-repository 54 | 55 | - name: get-base-image 56 | taskRef: 57 | name: base-image 58 | params: 59 | - name: tlsverify 60 | value: "false" 61 | - name: image 62 | value: $(params.IMAGE) 63 | - name: base-image-label 64 | value: "base.image" 65 | workspaces: 66 | - name: source 67 | workspace: shared-workspace 68 | runAfter: 69 | - build-image 70 | 71 | - name: verify-base-image 72 | taskRef: 73 | name: cosign 74 | params: 75 | - name: command 76 | value: "verify" 77 | - name: image 78 | value: $(tasks.get-base-image.results.base-image) 79 | workspaces: 80 | - name: source 81 | workspace: shared-workspace 82 | runAfter: 83 | - get-base-image 84 | 85 | - name: apply-opa-maintainers-policy 86 | taskRef: 87 | name: conftest 88 | params: 89 | - name: files 90 | value: certificate-common-name.json # This file is created in ./ by verify-base-image task 91 | - name: policy 92 | value: "config/opa/maintainers.rego" 93 | - name: args 94 | value: 95 | - --data 96 | - MAINTAINERS.json 97 | - --parser 98 | - json 99 | workspaces: 100 | - name: source 101 | workspace: shared-workspace 102 | runAfter: 103 | - verify-base-image 104 | 105 | - name: apply-manifests 106 | taskRef: 107 | name: apply-manifests 108 | workspaces: 109 | - name: source 110 | workspace: shared-workspace 111 | runAfter: 112 | - apply-opa-maintainers-policy 113 | 114 | - name: update-deployment 115 | taskRef: 116 | name: update-deployment 117 | params: 118 | - name: deployment 119 | value: $(params.deployment-name) 120 | - name: IMAGE 121 | value: $(params.IMAGE) 122 | runAfter: 123 | - apply-manifests 124 | --------------------------------------------------------------------------------