├── ACKNOWLEDGEMENTS.md ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── doc └── source │ └── images │ └── crd.png ├── knative └── picalc.yaml ├── src ├── Dockerfile ├── picalc.go └── picalc_test.go └── tekton ├── pipeline-account.yaml ├── pipeline └── build-and-deploy-pipeline.yaml ├── resources └── picalc-git.yaml ├── run └── picalc-pipeline-run.yaml └── tasks ├── deploy-using-kubectl.yaml └── source-to-image.yaml /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | ## Acknowledgements 2 | 3 | * Credit goes to [Andrea Frittoli](https://github.com/afrittoli) for his invaluable [blog](https://developer.ibm.com/blogs/define-a-simple-cd-pipeline-with-knative/) about tekton. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This is an open source project, and we appreciate your help! 4 | 5 | We use the GitHub issue tracker to discuss new features and non-trivial bugs. 6 | 7 | In addition to the issue tracker, [#journeys on 8 | Slack](https://dwopen.slack.com) is the best way to get into contact with the 9 | project's maintainers. 10 | 11 | To contribute code, documentation, or tests, please submit a pull request to 12 | the GitHub repository. Generally, we expect two maintainers to review your pull 13 | request before it is approved for merging. For more details, see the 14 | [MAINTAINERS](MAINTAINERS.md) page. 15 | 16 | Contributions are subject to the [Developer Certificate of Origin, Version 1.1](https://developercertificate.org/) and the [Apache License, Version 2](https://www.apache.org/licenses/LICENSE-2.0.txt). 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers Guide 2 | 3 | This guide is intended for maintainers - anybody with commit access to one or 4 | more Code Pattern repositories. 5 | 6 | ## Methodology 7 | 8 | This repository does not have a traditional release management cycle, but 9 | should instead be maintained as a useful, working, and polished reference at 10 | all times. While all work can therefore be focused on the master branch, the 11 | quality of this branch should never be compromised. 12 | 13 | The remainder of this document details how to merge pull requests to the 14 | repositories. 15 | 16 | ## Merge approval 17 | 18 | The project maintainers use LGTM (Looks Good To Me) in comments on the pull 19 | request to indicate acceptance prior to merging. A change requires LGTMs from 20 | two project maintainers. If the code is written by a maintainer, the change 21 | only requires one additional LGTM. 22 | 23 | ## Reviewing Pull Requests 24 | 25 | We recommend reviewing pull requests directly within GitHub. This allows a 26 | public commentary on changes, providing transparency for all users. When 27 | providing feedback be civil, courteous, and kind. Disagreement is fine, so long 28 | as the discourse is carried out politely. If we see a record of uncivil or 29 | abusive comments, we will revoke your commit privileges and invite you to leave 30 | the project. 31 | 32 | During your review, consider the following points: 33 | 34 | ### Does the change have positive impact? 35 | 36 | Some proposed changes may not represent a positive impact to the project. Ask 37 | whether or not the change will make understanding the code easier, or if it 38 | could simply be a personal preference on the part of the author (see 39 | [bikeshedding](https://en.wiktionary.org/wiki/bikeshedding)). 40 | 41 | Pull requests that do not have a clear positive impact should be closed without 42 | merging. 43 | 44 | ### Do the changes make sense? 45 | 46 | If you do not understand what the changes are or what they accomplish, ask the 47 | author for clarification. Ask the author to add comments and/or clarify test 48 | case names to make the intentions clear. 49 | 50 | At times, such clarification will reveal that the author may not be using the 51 | code correctly, or is unaware of features that accommodate their needs. If you 52 | feel this is the case, work up a code sample that would address the pull 53 | request for them, and feel free to close the pull request once they confirm. 54 | 55 | ### Does the change introduce a new feature? 56 | 57 | For any given pull request, ask yourself "is this a new feature?" If so, does 58 | the pull request (or associated issue) contain narrative indicating the need 59 | for the feature? If not, ask them to provide that information. 60 | 61 | Are new unit tests in place that test all new behaviors introduced? If not, do 62 | not merge the feature until they are! Is documentation in place for the new 63 | feature? (See the documentation guidelines). If not do not merge the feature 64 | until it is! Is the feature necessary for general use cases? Try and keep the 65 | scope of any given component narrow. If a proposed feature does not fit that 66 | scope, recommend to the user that they maintain the feature on their own, and 67 | close the request. You may also recommend that they see if the feature gains 68 | traction among other users, and suggest they re-submit when they can show such 69 | support. 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Knative Deployment using Tekton Pipelines 2 | 3 | > **_NOTE:_** This content applies to the Tekton alpha release. Please [switch to the beta-update branch](https://github.com/IBM/tekton-tutorial/tree/beta-update) for content about the Tekton beta release. 4 | 5 | Tekton is an open source project to configure and run CI/CD pipelines within a Kubernetes cluster. 6 | 7 | 8 | ## Introduction 9 | 10 | In this tutorial you'll learn 11 | * what are the basic concepts used by Tekton pipelines 12 | * how to create a pipeline to build and deploy a Knative application 13 | * how to run the pipeline, check its status and troubleshoot problems 14 | 15 | 16 | ## Prerequisites 17 | 18 | Before you start the tutorial you must set up a Kubernetes environment with Knative and Tekton installed. 19 | 20 | * [Install the CLIs to manage a cluster](https://cloud.ibm.com/docs/containers?topic=containers-cs_cli_install#cs_cli_install_steps) 21 | 22 | * [Create a standard Kubernetes cluster in IBM Kubernetes Service](https://cloud.ibm.com/docs/containers?topic=containers-clusters#clusters_ui_standard) 23 | 24 | **Note**: The managed Knative add-on requires Kubernetes version 1.16 or later. 25 | 26 | * [Create a private container registry in IBM Container Service](https://cloud.ibm.com/docs/services/Registry?topic=registry-registry_setup_cli_namespace#registry_setup_cli_namespace) 27 | 28 | * [Install Knative in your cluster using the managed Knative add-on](https://cloud.ibm.com/docs/containers?topic=containers-knative_tutorial#knative_setup) 29 | 30 | **Note**: The managed Knative add-on also installs Tekton. 31 | 32 | ## Estimated time 33 | 34 | 1 hour 35 | 36 | 37 | ## Steps 38 | 39 | 40 | ### 1. Tekton pipeline concepts 41 | 42 | Tekton provides a set of extensions to Kubernetes, in the form of [Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/), for defining pipelines. 43 | The following diagram shows the resources used in this tutorial. The arrows depict references from one resource to another resource. 44 | 45 | ![crd](doc/source/images/crd.png) 46 | 47 | The resources are used as follows. 48 | 49 | * A **PipelineResource** defines an object that is an input (such as a git repository) or an output (such as a docker image) of the pipeline. 50 | * A **PipelineRun** defines an execution of a pipeline. It references the **Pipeline** to run and the **PipelineResources** to use as inputs and outputs. 51 | * A **Pipeline** defines the set of **Tasks** that compose a pipeline. 52 | * A **Task** defines a set of build steps such as compiling code, running tests, and building and deploying images. 53 | 54 | We will go into more detail about each resource during the walkthrough of the example. 55 | 56 | 57 | ### 2. Sample pipeline 58 | 59 | Let's create a simple pipeline that 60 | 61 | * builds a Docker image from source files and pushes it to your private container registry 62 | * deploys the image as a Knative service in your Kubernetes cluster 63 | 64 | You should clone this project to your workstation since you will need to edit some of the yaml files before applying them to your cluster. 65 | 66 | ``` 67 | git clone https://github.com/IBM/tekton-tutorial 68 | ``` 69 | 70 | We will work from the bottom-up, i.e. first we will define the Task resources needed to build and deploy the image, 71 | then we'll define the Pipeline resource that references the tasks, 72 | and finally we'll create the PipelineRun and PipelineResource resources needed to run the pipeline. 73 | 74 | 75 | #### Create a task to build an image and push it to a container registry 76 | 77 | Below is a Tekton task that builds a docker image and pushes it to a container registry. 78 | You can find this yaml file at [tekton/tasks/source-to-image.yaml](tekton/tasks/source-to-image.yaml). 79 | 80 | ``` 81 | apiVersion: tekton.dev/v1alpha1 82 | kind: Task 83 | metadata: 84 | name: source-to-image 85 | spec: 86 | inputs: 87 | resources: 88 | - name: git-source 89 | type: git 90 | params: 91 | - name: pathToContext 92 | description: The path to the build context, used by Kaniko - within the workspace 93 | default: . 94 | - name: pathToDockerFile 95 | description: The path to the dockerfile to build (relative to the context) 96 | default: Dockerfile 97 | - name: imageUrl 98 | description: Url of image repository 99 | - name: imageTag 100 | description: Tag to apply to the built image 101 | default: "latest" 102 | steps: 103 | - name: build-and-push 104 | image: gcr.io/kaniko-project/executor 105 | command: 106 | - /kaniko/executor 107 | args: 108 | - --dockerfile=$(inputs.params.pathToDockerFile) 109 | - --destination=$(inputs.params.imageUrl):$(inputs.params.imageTag) 110 | - --context=/workspace/git-source/$(inputs.params.pathToContext) 111 | ``` 112 | 113 | A task can have one or more steps. Each step defines an image to run to perform the function of the step. 114 | This task has one step that uses the [kaniko](https://github.com/GoogleContainerTools/kaniko) project to build a docker image from source and push it to a registry. 115 | 116 | The task requires an input resource of type `git` that defines where the source is located. 117 | The `git` source is cloned to a local volume at path `/workspace/git-source` where `git-source` comes from the name we gave to the resource. 118 | Note that a resource is simply an abstract argument to the task. 119 | We'll see later how it becomes bound to a PipelineResources which defines the actual resource to be used. 120 | This makes the task reusable with different git repositories. 121 | 122 | A task also can have input parameters. Parameters help to make a Task more reusable. 123 | This task accepts the following parameters: 124 | * a path to the Docker build context inside the git source 125 | * a path to the Dockerfile inside the build context 126 | * the URL of the image repository where the image should be stored 127 | * an image tag to apply to the built image 128 | 129 | You may be wondering about how the task authenticates to the image repository for permission to push the image. 130 | This will be covered later on in the tutorial. 131 | 132 | Apply the file to your cluster to create the task. 133 | 134 | ``` 135 | kubectl apply -f tekton/tasks/source-to-image.yaml 136 | ``` 137 | 138 | 139 | ### 3. Create a task to deploy an image to a Kubernetes cluster 140 | 141 | Below is a Tekton task that deploys a docker image to a Kubernetes cluster. 142 | You can find this yaml file at [tekton/tasks/deploy-using-kubectl.yaml](tekton/tasks/deploy-using-kubectl.yaml). 143 | 144 | ``` 145 | apiVersion: tekton.dev/v1alpha1 146 | kind: Task 147 | metadata: 148 | name: deploy-using-kubectl 149 | spec: 150 | inputs: 151 | resources: 152 | - name: git-source 153 | type: git 154 | params: 155 | - name: pathToYamlFile 156 | description: The path to the yaml file to deploy within the git source 157 | - name: imageUrl 158 | description: Url of image repository 159 | - name: imageTag 160 | description: Tag of the images to be used. 161 | default: "latest" 162 | steps: 163 | - name: update-yaml 164 | image: alpine 165 | command: ["sed"] 166 | args: 167 | - "-i" 168 | - "-e" 169 | - "s;__IMAGE__;$(inputs.params.imageUrl):$(inputs.params.imageTag);g" 170 | - "/workspace/git-source/$(inputs.params.pathToYamlFile)" 171 | - name: run-kubectl 172 | image: lachlanevenson/k8s-kubectl 173 | command: ["kubectl"] 174 | args: 175 | - "apply" 176 | - "-f" 177 | - "/workspace/git-source/$(inputs.params.pathToYamlFile)" 178 | ``` 179 | 180 | This task has two steps. 181 | 182 | 1. The first step runs `sed` in an Alpine Linux container to update the yaml file used for deployment with the image that was built by the source-to-image task. 183 | The step requires the yaml file to have a character string `__IMAGE__` at the point where this update needs to occur. 184 | 185 | 2. The second step runs `kubectl` using Lachlan Evenson's popular `k8s-kubectl` container image to apply the yaml file to the same cluster where the pipeline is running. 186 | 187 | As was the case in the source-to-image task, this task makes use of an input PipelineResource and parameters in order to make the task as reusable as possible. 188 | 189 | You may be wondering about how the task authenticates to the cluster for permission to apply the resource(s) in the yaml file. 190 | This will be covered later on in the tutorial. 191 | 192 | Apply the file to your cluster to create the task. 193 | 194 | ``` 195 | kubectl apply -f tekton/tasks/deploy-using-kubectl.yaml 196 | ``` 197 | 198 | 199 | ### 4. Create a pipeline 200 | 201 | Below is a Tekton pipeline that runs the two tasks we defined above. 202 | You can find this yaml file at [tekton/pipeline/build-and-deploy-pipeline.yaml](tekton/pipeline/build-and-deploy-pipeline.yaml). 203 | 204 | ``` 205 | apiVersion: tekton.dev/v1alpha1 206 | kind: Pipeline 207 | metadata: 208 | name: build-and-deploy-pipeline 209 | spec: 210 | resources: 211 | - name: git-source 212 | type: git 213 | params: 214 | - name: pathToContext 215 | description: The path to the build context, used by Kaniko - within the workspace 216 | default: src 217 | - name: pathToYamlFile 218 | description: The path to the yaml file to deploy within the git source 219 | - name: imageUrl 220 | description: Url of image repository 221 | - name: imageTag 222 | description: Tag to apply to the built image 223 | tasks: 224 | - name: source-to-image 225 | taskRef: 226 | name: source-to-image 227 | params: 228 | - name: pathToContext 229 | value: "$(params.pathToContext)" 230 | - name: imageUrl 231 | value: "$(params.imageUrl)" 232 | - name: imageTag 233 | value: "$(params.imageTag)" 234 | resources: 235 | inputs: 236 | - name: git-source 237 | resource: git-source 238 | - name: deploy-to-cluster 239 | taskRef: 240 | name: deploy-using-kubectl 241 | runAfter: 242 | - source-to-image 243 | params: 244 | - name: pathToYamlFile 245 | value: "$(params.pathToYamlFile)" 246 | - name: imageUrl 247 | value: "$(params.imageUrl)" 248 | - name: imageTag 249 | value: "$(params.imageTag)" 250 | resources: 251 | inputs: 252 | - name: git-source 253 | resource: git-source 254 | ``` 255 | 256 | A Pipeline resource lists the tasks to run and provides the input and output resources and input parameters required by each task. 257 | All resources must be exposed as inputs or outputs of the pipeline; the pipeline cannot bind one to an actual PipelineResource. 258 | However you can choose whether to expose a task's input parameter as a pipeline input parameter, set the value directly, or let the value 259 | default inside the task (if it's an optional parameter). For example this pipeline exposes the `pathToContext` parameter from the 260 | source-to-image task but does not expose the `pathToDockerFile` parameter and allows it to default inside the task. 261 | 262 | Dependencies between tasks can be expressed by using the `runAfter` key. 263 | It specifies that the task must run after the given list of one of tasks has completed. 264 | In this example, this pipeline specifies that the `deploy-using-kubectl` task must run after the `source-to-image` task. 265 | Tekton will order to the execution of the tasks to satisfy this dependency. 266 | 267 | Dependencies between tasks also can be expressed by using the `from` key which isn't used in this tutorial 268 | but which you can [read about in the official Tekton documentation](https://github.com/tektoncd/pipeline/blob/master/docs/pipelines.md#from). 269 | 270 | Apply the file to your cluster to create the pipeline. 271 | 272 | ``` 273 | kubectl apply -f tekton/pipeline/build-and-deploy-pipeline.yaml 274 | ``` 275 | 276 | 277 | ### 5. Create PipelineRun and PipelineResources 278 | 279 | We've defined reusable Pipeline and Task resources for building and deploying an image. 280 | It is now time to look at how one runs the pipeline with an actual input resource and parameters. 281 | 282 | Below is a Tekton PipelineRun resource that runs the pipeline we defined above. 283 | You can find this yaml file at [tekton/run/picalc-pipeline-run.yaml](tekton/run/picalc-pipeline-run.yaml). 284 | 285 | ``` 286 | apiVersion: tekton.dev/v1alpha1 287 | kind: PipelineRun 288 | metadata: 289 | generateName: picalc-pr- 290 | spec: 291 | pipelineRef: 292 | name: build-and-deploy-pipeline 293 | resources: 294 | - name: git-source 295 | resourceRef: 296 | name: picalc-git 297 | params: 298 | - name: pathToYamlFile 299 | value: "knative/picalc.yaml" 300 | - name: imageUrl 301 | value: //picalc 302 | - name: imageTag 303 | value: "1.0" 304 | serviceAccountName: pipeline-account 305 | ``` 306 | 307 | Although this file is small there is a lot going on here. Let's break it down from top to bottom: 308 | 309 | * The PipelineRun does not have a fixed name. It uses `generateName` to generate a name each time it is created. 310 | This is because a particular PipelineRun resource executes the pipeline only once. If you want to run the pipeline again, 311 | you cannot modify an existing PipelineRun resource to request it to re-run -- you must create a new PipelineRun resource. 312 | While you could use `name` to assign a unique name to your PipelineRun each time you create one, it is much easier to use `generateName`. 313 | 314 | * The Pipeline resource is identified under the `pipelineRef` key. 315 | 316 | * The git resource required by the pipeline is bound to specific PipelineResources named `picalc-git`. 317 | We will define it in a moment. 318 | 319 | * Parameters exposed by the pipeline are set to specific values. 320 | 321 | * A service account named `pipeline-account` is specified to provide the credentials needed for the pipeline to run successfully. 322 | We will define this service account in the next part of the tutorial. 323 | 324 | You must edit this file to substitute the values of `` and `` with the information for your private container registry. 325 | 326 | * To find the value for ``, enter the command `ibmcloud cr region`. 327 | * To find the value of ``, enter the command `ibmcloud cr namespace-list`. 328 | 329 | Below is the Tekton PipelineResource for `picalc-git` which defines the git source. 330 | You can find this yaml file at [tekton/resources/picalc-git.yaml](tekton/resources/picalc-git.yaml). 331 | 332 | ``` 333 | apiVersion: tekton.dev/v1alpha1 334 | kind: PipelineResource 335 | metadata: 336 | name: picalc-git 337 | spec: 338 | type: git 339 | params: 340 | - name: revision 341 | value: master 342 | - name: url 343 | value: https://github.com/IBM/tekton-tutorial 344 | ``` 345 | 346 | The source code for this example is a [go program that calculates an approximation of pi](src/picalc.go). 347 | The source includes a [Dockerfile](src/Dockerfile) which runs tests, compiles the code, and builds an image for execution. 348 | 349 | You can apply the PipelineResource file to your cluster now. 350 | Do not apply the PipelineRun file yet because we still need to define the service account for it. 351 | 352 | ``` 353 | kubectl apply -f tekton/resources/picalc-git.yaml 354 | ``` 355 | 356 | 357 | ### 6. Define a service account 358 | 359 | The last step before running the pipeline is to set up a service account so that it can access protected resources. 360 | The service account ties together a couple of secrets containing credentials for authentication 361 | along with RBAC-related resources for permission to create and modify certain Kubernetes resources. 362 | 363 | First you need to enable programmatic access to your private container registry by creating 364 | an IBM Cloud Identity and Access Management (IAM) API key. 365 | The process for creating a user API key is described [here](https://cloud.ibm.com/docs/services/Registry?topic=registry-registry_access#registry_access). 366 | 367 | After you have the API key, you can create the following secret. 368 | 369 | ``` 370 | kubectl create secret generic ibm-cr-push-secret --type="kubernetes.io/basic-auth" --from-literal=username=iamapikey --from-literal=password= 371 | kubectl annotate secret ibm-cr-push-secret tekton.dev/docker-0= 372 | ``` 373 | 374 | where 375 | 376 | * `` is either the API key that you created 377 | * `` is the URL of your container registry, such as `us.icr.io` 378 | 379 | Now you can create the service account using the following yaml. 380 | You can find this yaml file at [tekton/pipeline-account.yaml](tekton/pipeline-account.yaml). 381 | 382 | ``` 383 | apiVersion: v1 384 | kind: ServiceAccount 385 | metadata: 386 | name: pipeline-account 387 | secrets: 388 | - name: ibm-cr-push-secret 389 | 390 | --- 391 | 392 | apiVersion: v1 393 | kind: Secret 394 | metadata: 395 | name: kube-api-secret 396 | annotations: 397 | kubernetes.io/service-account.name: pipeline-account 398 | type: kubernetes.io/service-account-token 399 | 400 | --- 401 | 402 | kind: Role 403 | apiVersion: rbac.authorization.k8s.io/v1 404 | metadata: 405 | name: pipeline-role 406 | rules: 407 | - apiGroups: ["serving.knative.dev"] 408 | resources: ["services"] 409 | verbs: ["get", "create", "update", "patch"] 410 | 411 | --- 412 | 413 | apiVersion: rbac.authorization.k8s.io/v1 414 | kind: RoleBinding 415 | metadata: 416 | name: pipeline-role-binding 417 | roleRef: 418 | apiGroup: rbac.authorization.k8s.io 419 | kind: Role 420 | name: pipeline-role 421 | subjects: 422 | - kind: ServiceAccount 423 | name: pipeline-account 424 | ``` 425 | 426 | This yaml creates the following Kubernetes resources: 427 | 428 | * A ServiceAccount named `pipeline-account`. This is the name that the PipelineRun seen earlier uses to reference this account. 429 | The service account references the `ibm-cr-push-secret` secret so that the pipeline can authenticate to your private container registry 430 | when it pushes a container image. 431 | 432 | * A Secret named `kube-api-secret` which contains an API credential (generated by Kubernetes) for accessing the Kubernetes API. 433 | This allows the pipeline to use `kubectl` to talk to your cluster. 434 | 435 | * A Role named `pipeline-role` and a RoleBinding named `pipeline-role-binding` which provide the resource-based access control permissions 436 | needed for this pipeline to create and modify Knative services. 437 | 438 | Apply the file to your cluster to create the service account and related resources. 439 | 440 | ``` 441 | kubectl apply -f tekton/pipeline-account.yaml 442 | ``` 443 | 444 | 445 | ### 7. Run the pipeline 446 | 447 | All the pieces are in place to run the pipeline. 448 | 449 | ``` 450 | kubectl create -f tekton/run/picalc-pipeline-run.yaml 451 | ``` 452 | 453 | Note that we're using `create` here instead of `apply`. 454 | As mentioned previously a given PipelineRun resource can run a pipeline only once so you need to create a new one each time you want to run the pipeline. 455 | `kubectl` will respond with the generated name of the PipelineRun resource. 456 | 457 | ``` 458 | pipelinerun.tekton.dev/picalc-pr-db6p6 created 459 | ``` 460 | 461 | You can check that status of the pipeline using the `kubectl describe` command: 462 | 463 | ``` 464 | kubectl describe pipelinerun picalc-pr-db6p6 465 | ``` 466 | 467 | If you enter the command relatively quickly after creating the PipelineRun, you may see output similar to this: 468 | 469 | ``` 470 | Name: picalc-pr-db6p6 471 | Namespace: default 472 | Labels: tekton.dev/pipeline=build-and-deploy-pipeline 473 | Annotations: 474 | API Version: tekton.dev/v1alpha1 475 | Kind: PipelineRun 476 | Metadata: 477 | Creation Timestamp: 2019-04-15T14:29:23Z 478 | Generate Name: picalc-pr- 479 | Generation: 1 480 | Resource Version: 3893390 481 | Self Link: /apis/tekton.dev/v1alpha1/namespaces/default/pipelineruns/picalc-pr-db6p6 482 | UID: dd207211-5f8a-11e9-b66d-8eb09a9ab3eb 483 | Spec: 484 | Status: 485 | Params: 486 | Name: pathToYamlFile 487 | Value: knative/picalc.yaml 488 | Name: imageUrl 489 | Value: us.icr.io/mynamespace/picalc 490 | Name: imageTag 491 | Value: 1.3 492 | Pipeline Ref: 493 | Name: build-and-deploy-pipeline 494 | Resources: 495 | Name: git-source 496 | Resource Ref: 497 | Name: picalc-git 498 | Service Account: pipeline-account 499 | Trigger: 500 | Type: manual 501 | Status: 502 | Conditions: 503 | Last Transition Time: 2019-04-15T14:29:23Z 504 | Message: Not all Tasks in the Pipeline have finished executing 505 | Reason: Running 506 | Status: Unknown 507 | Type: Succeeded 508 | Start Time: 2019-04-15T14:29:23Z 509 | Task Runs: 510 | Picalc - Pr - Db 6 P 6 - Source - To - Image - Kczdb: 511 | Pipeline Task Name: source-to-image 512 | Status: 513 | Conditions: 514 | Last Transition Time: 2019-04-15T14:29:28Z 515 | Reason: Building 516 | Status: Unknown 517 | Type: Succeeded 518 | Pod Name: picalc-pr-db6p6-source-to-image-kczdb-pod-7b4e7c 519 | Start Time: 2019-04-15T14:29:23Z 520 | Steps: 521 | Running: 522 | Started At: 2019-04-15T14:29:26Z 523 | Terminated: 524 | Container ID: containerd://b8f770e2b57d59c2bce76c63713d0b0a33f3fd02a14bad6b96978012060a436a 525 | Exit Code: 0 526 | Finished At: 2019-04-15T14:29:26Z 527 | Reason: Completed 528 | Started At: 2019-04-15T14:29:26Z 529 | Terminated: 530 | Container ID: containerd://a637b1cb5d83b1ad2aa0dbecd962bb70b0452900189f611e404c0c9515262443 531 | Exit Code: 0 532 | Finished At: 2019-04-15T14:29:26Z 533 | Reason: Completed 534 | Started At: 2019-04-15T14:29:26Z 535 | Events: 536 | ``` 537 | 538 | Note the status message which says `Not all Tasks in the Pipeline have finished executing`. 539 | Continue to rerun the command to check the status. If the pipeline runs successfully, the overall status eventually should look like this: 540 | 541 | ``` 542 | Status: 543 | Conditions: 544 | Last Transition Time: 2019-04-15T14:30:46Z 545 | Message: All Tasks have completed executing 546 | Reason: Succeeded 547 | Status: True 548 | Type: Succeeded 549 | Start Time: 2019-04-15T14:29:23Z 550 | ``` 551 | 552 | Check the status of the deployed Knative service. It should be ready. 553 | 554 | ``` 555 | $ kubectl get ksvc picalc 556 | NAME DOMAIN LATESTCREATED LATESTREADY READY REASON 557 | picalc picalc-default.mycluster6.us-south.containers.appdomain.cloud picalc-00001 picalc-00001 True 558 | ``` 559 | 560 | You can use the URL in the response to curl the service. 561 | 562 | ``` 563 | $ curl picalc-default.mycluster6.us-south.containers.appdomain.cloud?iterations=20000000 564 | 3.1415926036 565 | ``` 566 | 567 | If the pipeline did not run successfully, the overall status may look like this: 568 | 569 | ``` 570 | Status: 571 | Conditions: 572 | Last Transition Time: 2019-04-15T14:30:46Z 573 | Message: TaskRun picalc-pr-db6p6-deploy-to-cluster-7h8pm has failed 574 | Reason: Failed 575 | Status: False 576 | Type: Succeeded 577 | Start Time: 2019-04-15T14:29:23Z 578 | ``` 579 | 580 | Under the task run status you should find a message that tells you how to get the logs from the failed build step. 581 | You will need to look at the logs to identify the problem. 582 | 583 | ``` 584 | build step "build-step-deploy-using-kubectl" exited with code 1 (image: "docker.io/library/alpine@sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913"); for logs run: kubectl -n default logs picalc-pr-db6p6-deploy-to-cluster-7h8pm-pod-582c73 -c build-step-deploy-using-kubectl 585 | ``` 586 | 587 | 588 | ### 8. Tips 589 | 590 | Be careful when defining a PipelineResource as output from one task and input to another task. 591 | For example, in this tutorial an [image PipelineResource](https://github.com/tektoncd/pipeline/blob/master/docs/resources.md#image-resource) could have been used 592 | to define an output image from the source-to-image task and an input image to the deploy-using-kubectl task. 593 | This causes Tekton to [create a PersistentVolumeClaim for sharing data between tasks](https://github.com/tektoncd/pipeline/blob/master/docs/developers/README.md#how-are-resources-shared-between-tasks). 594 | This functionality is not completely implemented at the point of this writing so it was not used in the tutorial. 595 | 596 | 597 | ## Summary 598 | 599 | Tekton provides simple, easy-to-learn features for constructing CI/CD pipelines that run on Kubernetes. 600 | This tutorial covered the basics to get you started building your own pipelines. 601 | There are more features available and many more planned for upcoming releases. 602 | 603 | 604 | ## Related Links 605 | 606 | You can check out our other Knative-related tutorials and blogs at https://developer.ibm.com/components/knative/ 607 | -------------------------------------------------------------------------------- /doc/source/images/crd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/tekton-tutorial/745a71aa85922d15414c6df1bb925a6429e5773a/doc/source/images/crd.png -------------------------------------------------------------------------------- /knative/picalc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1alpha1 2 | kind: Service 3 | metadata: 4 | name: picalc 5 | namespace: default 6 | spec: 7 | runLatest: 8 | configuration: 9 | revisionTemplate: 10 | spec: 11 | container: 12 | image: __IMAGE__ 13 | -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Golang image to create a build artifact. 2 | # This is based on Debian and sets the GOPATH to /go. 3 | # https://hub.docker.com/_/golang 4 | FROM golang:1.12 as builder 5 | 6 | # Copy local code to the container image. 7 | WORKDIR /go/src/github.com/IBM/tekton-tutorial 8 | COPY picalc.go picalc_test.go . 9 | 10 | # Run unit tests 11 | RUN CGO_ENABLED=0 GOOS=linux go test 12 | 13 | # Build the code inside the container. 14 | RUN CGO_ENABLED=0 GOOS=linux go build -v -o picalc 15 | 16 | # Use a Docker multi-stage build to create a lean production image. 17 | # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds 18 | FROM alpine 19 | 20 | # Copy the binary to the production image from the builder stage. 21 | COPY --from=builder /go/src/github.com/IBM/tekton-tutorial/picalc /picalc 22 | 23 | # Service must listen to $PORT environment variable. 24 | # This default value facilitates local development. 25 | ENV PORT 8080 26 | 27 | # Run the web service on container startup. 28 | CMD ["/picalc"] 29 | -------------------------------------------------------------------------------- /src/picalc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | // Calculate pi using Gregory-Leibniz series: (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) - (4/15) ... 12 | func calculatePi(iterations int) float64 { 13 | var result float64 = 0.0 14 | var sign float64 = 1.0 15 | var denominator float64 = 1.0 16 | for i := 0; i < iterations; i++ { 17 | result = result + (sign * 4/denominator) 18 | denominator = denominator + 2 19 | sign = -sign 20 | } 21 | return result 22 | } 23 | 24 | func handler(w http.ResponseWriter, r *http.Request) { 25 | log.Print("Pi calculator received a request.") 26 | iterations, err := strconv.Atoi(r.URL.Query()["iterations"][0]) 27 | if err != nil { 28 | fmt.Fprintf(w, "iterations parameter not valid\n") 29 | return 30 | } 31 | fmt.Fprintf(w, "%.10f\n", calculatePi(iterations)) 32 | } 33 | 34 | func main() { 35 | log.Print("Pi calculator started.") 36 | 37 | http.HandleFunc("/", handler) 38 | 39 | port := os.Getenv("PORT") 40 | if port == "" { 41 | port = "8080" 42 | } 43 | 44 | log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil)) 45 | } 46 | -------------------------------------------------------------------------------- /src/picalc_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestCalculatePi(t *testing.T) { 6 | var pi float64 7 | pi = calculatePi(20000000) 8 | if pi < 3.1415926 || pi >= 3.1415927 { 9 | t.Errorf("Value %.10f is incorrect", pi) 10 | } 11 | } -------------------------------------------------------------------------------- /tekton/pipeline-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: pipeline-account 5 | secrets: 6 | - name: ibm-cr-push-secret 7 | 8 | --- 9 | 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: kube-api-secret 14 | annotations: 15 | kubernetes.io/service-account.name: pipeline-account 16 | type: kubernetes.io/service-account-token 17 | 18 | --- 19 | 20 | kind: Role 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | metadata: 23 | name: pipeline-role 24 | rules: 25 | - apiGroups: ["serving.knative.dev"] 26 | resources: ["services"] 27 | verbs: ["get", "create", "update", "patch"] 28 | 29 | --- 30 | 31 | apiVersion: rbac.authorization.k8s.io/v1 32 | kind: RoleBinding 33 | metadata: 34 | name: pipeline-role-binding 35 | roleRef: 36 | apiGroup: rbac.authorization.k8s.io 37 | kind: Role 38 | name: pipeline-role 39 | subjects: 40 | - kind: ServiceAccount 41 | name: pipeline-account 42 | -------------------------------------------------------------------------------- /tekton/pipeline/build-and-deploy-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: Pipeline 3 | metadata: 4 | name: build-and-deploy-pipeline 5 | spec: 6 | resources: 7 | - name: git-source 8 | type: git 9 | params: 10 | - name: pathToContext 11 | description: The path to the build context, used by Kaniko - within the workspace 12 | default: src 13 | - name: pathToYamlFile 14 | description: The path to the yaml file to deploy within the git source 15 | - name: imageUrl 16 | description: Url of image repository 17 | - name: imageTag 18 | description: Tag to apply to the built image 19 | tasks: 20 | - name: source-to-image 21 | taskRef: 22 | name: source-to-image 23 | params: 24 | - name: pathToContext 25 | value: "$(params.pathToContext)" 26 | - name: imageUrl 27 | value: "$(params.imageUrl)" 28 | - name: imageTag 29 | value: "$(params.imageTag)" 30 | resources: 31 | inputs: 32 | - name: git-source 33 | resource: git-source 34 | - name: deploy-to-cluster 35 | taskRef: 36 | name: deploy-using-kubectl 37 | runAfter: 38 | - source-to-image 39 | params: 40 | - name: pathToYamlFile 41 | value: "$(params.pathToYamlFile)" 42 | - name: imageUrl 43 | value: "$(params.imageUrl)" 44 | - name: imageTag 45 | value: "$(params.imageTag)" 46 | resources: 47 | inputs: 48 | - name: git-source 49 | resource: git-source -------------------------------------------------------------------------------- /tekton/resources/picalc-git.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: PipelineResource 3 | metadata: 4 | name: picalc-git 5 | spec: 6 | type: git 7 | params: 8 | - name: revision 9 | value: master 10 | - name: url 11 | value: https://github.com/IBM/tekton-tutorial -------------------------------------------------------------------------------- /tekton/run/picalc-pipeline-run.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: PipelineRun 3 | metadata: 4 | generateName: picalc-pr- 5 | spec: 6 | pipelineRef: 7 | name: build-and-deploy-pipeline 8 | resources: 9 | - name: git-source 10 | resourceRef: 11 | name: picalc-git 12 | params: 13 | - name: pathToYamlFile 14 | value: "knative/picalc.yaml" 15 | - name: imageUrl 16 | value: //picalc 17 | - name: imageTag 18 | value: "1.0" 19 | serviceAccountName: pipeline-account -------------------------------------------------------------------------------- /tekton/tasks/deploy-using-kubectl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: Task 3 | metadata: 4 | name: deploy-using-kubectl 5 | spec: 6 | inputs: 7 | resources: 8 | - name: git-source 9 | type: git 10 | params: 11 | - name: pathToYamlFile 12 | description: The path to the yaml file to deploy within the git source 13 | - name: imageUrl 14 | description: Url of image repository 15 | - name: imageTag 16 | description: Tag of the images to be used. 17 | default: "latest" 18 | steps: 19 | - name: update-yaml 20 | image: alpine 21 | command: ["sed"] 22 | args: 23 | - "-i" 24 | - "-e" 25 | - "s;__IMAGE__;$(inputs.params.imageUrl):$(inputs.params.imageTag);g" 26 | - "/workspace/git-source/$(inputs.params.pathToYamlFile)" 27 | - name: run-kubectl 28 | image: lachlanevenson/k8s-kubectl 29 | command: ["kubectl"] 30 | args: 31 | - "apply" 32 | - "-f" 33 | - "/workspace/git-source/$(inputs.params.pathToYamlFile)" -------------------------------------------------------------------------------- /tekton/tasks/source-to-image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: Task 3 | metadata: 4 | name: source-to-image 5 | spec: 6 | inputs: 7 | resources: 8 | - name: git-source 9 | type: git 10 | params: 11 | - name: pathToContext 12 | description: The path to the build context, used by Kaniko - within the workspace 13 | default: . 14 | - name: pathToDockerFile 15 | description: The path to the dockerfile to build (relative to the context) 16 | default: Dockerfile 17 | - name: imageUrl 18 | description: Url of image repository 19 | - name: imageTag 20 | description: Tag to apply to the built image 21 | default: "latest" 22 | steps: 23 | - name: build-and-push 24 | image: gcr.io/kaniko-project/executor 25 | command: 26 | - /kaniko/executor 27 | args: 28 | - --dockerfile=$(inputs.params.pathToDockerFile) 29 | - --destination=$(inputs.params.imageUrl):$(inputs.params.imageTag) 30 | - --context=/workspace/git-source/$(inputs.params.pathToContext) 31 | --------------------------------------------------------------------------------