├── .chglog
├── CHANGELOG.tpl.md
└── config.yml
├── .github
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── README.md
├── SUPPORT.md
├── renovate.json
└── workflows
│ ├── codeql-analysis.yml
│ ├── release.yml
│ ├── reviewdog.yml
│ ├── test.yml
│ └── validate.yml
├── .gitignore
├── .golangci.yml
├── LICENSE
├── Makefile
├── NOTICE
├── codecov.yml
├── compiler
├── context.go
├── context_test.go
├── doc.go
├── engine.go
└── native
│ ├── clone.go
│ ├── clone_test.go
│ ├── compile.go
│ ├── compile_test.go
│ ├── doc.go
│ ├── environment.go
│ ├── environment_test.go
│ ├── expand.go
│ ├── expand_test.go
│ ├── initialize.go
│ ├── initialize_test.go
│ ├── native.go
│ ├── native_test.go
│ ├── parse.go
│ ├── parse_test.go
│ ├── script.go
│ ├── script_test.go
│ ├── substitute.go
│ ├── substitute_test.go
│ ├── testdata
│ ├── clone_false.yml
│ ├── clone_replace.yml
│ ├── clone_true.yml
│ ├── invalid.yml
│ ├── invalid_type.yml
│ ├── metadata.yml
│ ├── parameters.yml
│ ├── pipeline_type.star
│ ├── pipeline_type_default.yml
│ ├── pipeline_type_go.yml
│ ├── secrets.yml
│ ├── stages.yml
│ ├── stages_pipeline.yml
│ ├── stages_pipeline_template.yml
│ ├── steps.yml
│ ├── steps_and_stages.yml
│ ├── steps_pipeline.yml
│ ├── steps_pipeline_template.yml
│ ├── template-gradle.json
│ ├── template-maven.json
│ ├── template-starlark.json
│ ├── template.json
│ ├── template.star
│ └── template.yml
│ ├── transform.go
│ ├── transform_test.go
│ ├── validate.go
│ └── validate_test.go
├── go.mod
├── go.sum
├── registry
├── doc.go
├── github
│ ├── doc.go
│ ├── github.go
│ ├── github_test.go
│ ├── parse.go
│ ├── parse_test.go
│ ├── template.go
│ ├── template_test.go
│ └── testdata
│ │ ├── template.json
│ │ └── template.yml
├── registry.go
└── source.go
└── template
├── doc.go
├── native
├── convert.go
├── convert_test.go
├── doc.go
├── render.go
├── render_test.go
└── testdata
│ ├── build
│ ├── basic
│ │ ├── build.yml
│ │ └── want.yml
│ ├── basic_stages
│ │ ├── build.yml
│ │ └── want.yml
│ └── conditional
│ │ ├── build.yml
│ │ └── want.yml
│ └── step
│ ├── basic
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
│ ├── conditional
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
│ ├── disallowed
│ ├── tmpl_env.yml
│ └── tmpl_expandenv.yml
│ ├── invalid.yml
│ ├── invalid_template.yml
│ ├── invalid_variables.yml
│ ├── loop_map
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
│ ├── loop_slice
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
│ ├── multiline
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
│ └── with_vars_plat
│ ├── step.yml
│ ├── tmpl.yml
│ └── want.yml
├── starlark
├── convert.go
├── convert_test.go
├── render.go
├── render_test.go
├── starlark.go
├── starlark_test.go
└── testdata
│ ├── build
│ ├── basic
│ │ ├── build.star
│ │ └── want.yml
│ ├── basic_stages
│ │ ├── build.star
│ │ └── want.yml
│ └── conditional
│ │ ├── build.star
│ │ └── want.yml
│ └── step
│ ├── basic
│ ├── step.yml
│ ├── template.py
│ └── want.yml
│ ├── cancel
│ ├── step.yml
│ └── template.star
│ ├── with_method
│ ├── step.yml
│ ├── template.star
│ └── want.yml
│ ├── with_vars
│ ├── step.yml
│ ├── template.star
│ └── want.yml
│ └── with_vars_plat
│ ├── step.yml
│ ├── template.star
│ └── want.yml
└── template.go
/.chglog/CHANGELOG.tpl.md:
--------------------------------------------------------------------------------
1 | {{ range .Versions }}
2 |
3 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
4 |
5 | {{ range .CommitGroups -}}
6 | ### {{ .Title }}
7 |
8 | {{ range .Commits -}}
9 | * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
10 | {{ end }}
11 | {{ end -}}
12 |
13 | {{- if .RevertCommits -}}
14 | ### Reverts
15 |
16 | {{ range .RevertCommits -}}
17 | * {{ .Revert.Header }}
18 | {{ end }}
19 | {{ end -}}
20 |
21 | {{- if .NoteGroups -}}
22 | {{ range .NoteGroups -}}
23 | ### {{ .Title }}
24 |
25 | {{ range .Notes }}
26 | {{ .Body }}
27 | {{ end }}
28 | {{ end -}}
29 | {{ end -}}
30 | {{ end -}}
--------------------------------------------------------------------------------
/.chglog/config.yml:
--------------------------------------------------------------------------------
1 | style: github
2 | template: CHANGELOG.tpl.md
3 | info:
4 | title: CHANGELOG
5 | repository_url: https://github.com/go-vela/compiler
6 | options:
7 | commits:
8 | # filters:
9 | # Type:
10 | # - feat
11 | # - fix
12 | # - perf
13 | # - refactor
14 | commit_groups:
15 | # title_maps:
16 | # feat: Features
17 | # fix: Bug Fixes
18 | # perf: Performance Improvements
19 | # refactor: Code Refactoring
20 | header:
21 | pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
22 | pattern_maps:
23 | - Type
24 | - Scope
25 | - Subject
26 | notes:
27 | keywords:
28 | - BREAKING CHANGE
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in the repo.
2 | * @go-vela/admins
3 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to make participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at
59 | [TTS-OpenSource-Office@target.com](mailto:TTS-OpenSource-Office@target.com). All
60 | complaints will be reviewed and investigated and will result in a response that
61 | is deemed necessary and appropriate to the circumstances. The project team is
62 | obligated to maintain confidentiality with regard to the reporter of an incident.
63 | Further details of specific enforcement policies may be posted separately.
64 |
65 | Project maintainers who do not follow or enforce the Code of Conduct in good
66 | faith may face temporary or permanent repercussions as determined by other
67 | members of the project's leadership.
68 |
69 | ## Attribution
70 |
71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
73 |
74 | [homepage]: https://www.contributor-covenant.org
75 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | > DISCLAIMER:
4 | >
5 | > The contents of this repository have been migrated into [go-vela/server](https://github.com/go-vela/server).
6 | >
7 | > This was done as a part of [go-vela/community#394](https://github.com/go-vela/community/issues/394) to deliver [on a proposal](https://github.com/go-vela/community/blob/master/proposals/2021/08-25_repo-structure.md).
8 |
9 | We'd love to accept your contributions to this project! There are just a few guidelines you need to follow.
10 |
11 | ## Bugs
12 |
13 | Bug reports should be opened up as [issues](https://help.github.com/en/github/managing-your-work-on-github/about-issues) on the [go-vela/community](https://github.com/go-vela/community) repository!
14 |
15 | ## Feature Requests
16 |
17 | Feature Requests should be opened up as [issues](https://help.github.com/en/github/managing-your-work-on-github/about-issues) on the [go-vela/community](https://github.com/go-vela/community) repository!
18 |
19 | ## Pull Requests
20 |
21 | **NOTE: We recommend you start by opening a new issue describing the bug or feature you're intending to fix. Even if you think it's relatively minor, it's helpful to know what people are working on.**
22 |
23 | We are always open to new PRs! You can follow the below guide for learning how you can contribute to the project!
24 |
25 | ## Getting Started
26 |
27 | ### Prerequisites
28 |
29 | * [Review the commit guide we follow](https://chris.beams.io/posts/git-commit/#seven-rules) - ensure your commits follow our standards
30 | * [Golang](https://golang.org/dl/) - for source code and [dependency management](https://github.com/golang/go/wiki/Modules)
31 |
32 | ### Setup
33 |
34 | * [Fork](/fork) this repository
35 |
36 | * Clone this repository to your workstation:
37 |
38 | ```bash
39 | # Clone the project
40 | git clone git@github.com:go-vela/compiler.git $HOME/go-vela/compiler
41 | ```
42 |
43 | * Navigate to the repository code:
44 |
45 | ```bash
46 | # Change into the project directory
47 | cd $HOME/go-vela/compiler
48 | ```
49 |
50 | * Point the original code at your fork:
51 |
52 | ```bash
53 | # Add a remote branch pointing to your fork
54 | git remote add fork https://github.com/your_fork/compiler
55 | ```
56 |
57 | ### Development
58 |
59 | * Navigate to the repository code:
60 |
61 | ```bash
62 | # Change into the project directory
63 | cd $HOME/go-vela/compiler
64 | ```
65 |
66 | * Write your code
67 | * Please be sure to [follow our commit rules](https://chris.beams.io/posts/git-commit/#seven-rules)
68 | * Please address linter warnings appropriately. If you are intentionally violating a rule that triggers a linter, please annotate the respective code with `nolint` declarations [[docs](https://golangci-lint.run/usage/false-positives/)]. we are using the following format for `nolint` declarations:
69 |
70 | ```go
71 | // nolint: //
72 | ```
73 |
74 | Example:
75 |
76 | ```go
77 | // nolint:gocyclo // legacy function is complex, needs simplification
78 | func superComplexFunction() error {
79 | // ..
80 | }
81 | ```
82 |
83 | Check the [documentation for more examples](https://golangci-lint.run/usage/false-positives/).
84 |
85 | * Write tests for your changes and ensure they pass:
86 |
87 | ```bash
88 | # Test the code with `go`
89 | go test ./...
90 | ```
91 |
92 | * Ensure your code meets the project standards:
93 |
94 | ```bash
95 | # Clean the code with `go`
96 | go mod tidy
97 | go fmt ./...
98 | go vet ./...
99 | ```
100 |
101 | * Push to your fork:
102 |
103 | ```bash
104 | # Push your code up to your fork
105 | git push fork master
106 | ```
107 |
108 | * Open a pull request!
109 | * For the title of the pull request, please use the following format for the title:
110 |
111 | ```text
112 | feat(wobble): add hat wobble
113 | ^--^^------^ ^------------^
114 | | | |
115 | | | +---> Summary in present tense.
116 | | +---> Scope: a noun describing a section of the codebase (optional)
117 | +---> Type: chore, docs, feat, fix, refactor, or test.
118 | ```
119 |
120 | * feat: adds a new feature (equivalent to a MINOR in Semantic Versioning)
121 | * fix: fixes a bug (equivalent to a PATCH in Semantic Versioning)
122 | * docs: changes to the documentation
123 | * refactor: refactors production code, eg. renaming a variable; doesn't change public API
124 | * test: adds missing tests, refactors tests; no production code change
125 | * chore: updates something without impacting the user (ex: bump a dependency in package.json or go.mod); no production code change
126 |
127 | If a code change introduces a breaking change, place ! suffix after type, ie. feat(change)!: adds breaking change. correlates with MAJOR in semantic versioning.
128 |
129 | Thank you for your contribution!
130 |
--------------------------------------------------------------------------------
/.github/README.md:
--------------------------------------------------------------------------------
1 | # compiler
2 |
3 | > DISCLAIMER:
4 | >
5 | > The contents of this repository have been migrated into [go-vela/server](https://github.com/go-vela/server).
6 | >
7 | > This was done as a part of [go-vela/community#394](https://github.com/go-vela/community/issues/394) to deliver [on a proposal](https://github.com/go-vela/community/blob/master/proposals/2021/08-25_repo-structure.md).
8 |
9 | [](../LICENSE)
10 | [](https://godoc.org/github.com/go-vela/compiler)
11 | [](https://goreportcard.com/report/go-vela/compiler)
12 | [](https://codecov.io/gh/go-vela/compiler)
13 |
14 | > Vela is in active development and is a pre-release product.
15 | >
16 | > Feel free to send us feedback at https://github.com/go-vela/community/issues/new.
17 |
18 | Vela is a Pipeline Automation (CI/CD) framework built on [Linux container](https://linuxcontainers.org/) technology written in [Golang](https://golang.org/).
19 |
20 | Vela uses a syntax similar to [Docker Compose](https://docs.docker.com/compose/) to define its configuration. This structure for repeated use, within the application, is called a pipeline and a single execution of a pipeline is referenced as a build.
21 |
22 | ## Documentation
23 |
24 | For installation and usage, please [visit our docs](https://go-vela.github.io/docs).
25 |
26 | ## Contributing
27 |
28 | We are always welcome to new pull requests!
29 |
30 | Please see our [contributing](CONTRIBUTING.md) documentation for further instructions.
31 |
32 | ## Support
33 |
34 | We are always here to help!
35 |
36 | Please see our [support](SUPPORT.md) documentation for further instructions.
37 |
38 | ## Copyright and License
39 |
40 | ```
41 | Copyright (c) 2021 Target Brands, Inc.
42 | ```
43 |
44 | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
45 |
--------------------------------------------------------------------------------
/.github/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | Welcome to Vela! To get help with a specific aspect of Vela, we've provided the below information.
4 |
5 | ## Bugs or Feature Requests
6 |
7 | We use GitHub for tracking bugs and feature requests.
8 |
9 | Please see our [contributing](CONTRIBUTING.md) documentation for further instructions.
10 |
11 | ## Installation and Usage
12 |
13 | We use GitHub pages for hosting our installation and usage documentation.
14 |
15 | Please see our [documentation](https://go-vela.github.io/docs) site for more information.
16 |
17 | ## Questions
18 |
19 | We use Slack for supporting questions not already covered in the above documents.
20 |
21 | Please join the [#vela](https://gophers.slack.com/app_redirect?channel=CNRRKE8KY) channel.
22 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "local>go-vela/renovate-config"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '29 4 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'go' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v2
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # name of the action
2 | name: release
3 |
4 | # trigger on push events with `v*` in tag
5 | # ignore push events with `v*-rc*` in tag
6 | on:
7 | push:
8 | tags:
9 | - 'v*'
10 | - '!v*-rc*'
11 |
12 | # pipeline to execute
13 | jobs:
14 | release:
15 | runs-on: ubuntu-latest
16 | container:
17 | image: golang:1.16
18 | steps:
19 | - name: clone
20 | uses: actions/checkout@v2
21 |
22 | - name: tags
23 | run: |
24 | git fetch --tags
25 |
26 | - name: version
27 | id: version
28 | run: |
29 | echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
30 |
31 | - name: install
32 | run: |
33 | go get github.com/git-chglog/git-chglog/cmd/git-chglog
34 | go get github.com/github-release/github-release
35 |
36 | - name: changelog
37 | run: |
38 | # https://github.com/git-chglog/git-chglog#git-chglog
39 | $(go env GOPATH)/bin/git-chglog \
40 | -o $GITHUB_WORKSPACE/CHANGELOG.md \
41 | ${{ steps.version.outputs.VERSION }}
42 |
43 | - name: release
44 | env:
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 | run: |
47 | # https://github.com/github-release/github-release#how-to-use
48 | $(go env GOPATH)/bin/github-release edit \
49 | --user go-vela \
50 | --repo compiler \
51 | --tag ${{ steps.version.outputs.VERSION }} \
52 | --name ${{ steps.version.outputs.VERSION }} \
53 | --description "$(cat $GITHUB_WORKSPACE/CHANGELOG.md)"
54 |
--------------------------------------------------------------------------------
/.github/workflows/reviewdog.yml:
--------------------------------------------------------------------------------
1 | # name of the action
2 | name: reviewdog
3 |
4 | # trigger on pull_request events
5 | on:
6 | pull_request:
7 |
8 | # pipeline to execute
9 | jobs:
10 | diff-review:
11 | runs-on: ubuntu-latest
12 | container:
13 | image: golang:1.16
14 | steps:
15 | - name: clone
16 | uses: actions/checkout@v2
17 |
18 | - name: golangci-lint
19 | uses: reviewdog/action-golangci-lint@v2
20 | with:
21 | github_token: ${{ secrets.github_token }}
22 | golangci_lint_flags: "--config=.golangci.yml"
23 | fail_on_error: true
24 | filter_mode: diff_context
25 | reporter: github-pr-review
26 |
27 | full-review:
28 | runs-on: ubuntu-latest
29 | container:
30 | image: golang:1.16
31 | steps:
32 | - name: clone
33 | uses: actions/checkout@v2
34 |
35 | - name: golangci-lint
36 | uses: reviewdog/action-golangci-lint@v2
37 | with:
38 | github_token: ${{ secrets.github_token }}
39 | golangci_lint_flags: "--config=.golangci.yml"
40 | fail_on_error: false
41 | filter_mode: nofilter
42 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # name of the action
2 | name: test
3 |
4 | # trigger on pull_request or push events
5 | on:
6 | pull_request:
7 | push:
8 |
9 | # pipeline to execute
10 | jobs:
11 | test:
12 | runs-on: ubuntu-latest
13 | container:
14 | image: golang:1.16
15 | steps:
16 | - name: clone
17 | uses: actions/checkout@v2
18 |
19 | - name: install
20 | run: |
21 | go get github.com/mattn/goveralls
22 |
23 | - name: test
24 | run: |
25 | go test -race -covermode=atomic -coverprofile=coverage.out ./...
26 |
27 | - name: coverage
28 | uses: codecov/codecov-action@v2
29 | with:
30 | token: ${{ secrets.CODECOV_TOKEN }}
31 | file: coverage.out
32 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | # name of the action
2 | name: validate
3 |
4 | # trigger on pull_request or push events
5 | on:
6 | pull_request:
7 | push:
8 |
9 | # pipeline to execute
10 | jobs:
11 | validate:
12 | runs-on: ubuntu-latest
13 | container:
14 | image: golang:1.16
15 | steps:
16 | - name: clone
17 | uses: actions/checkout@v2
18 |
19 | - name: validate
20 | run: |
21 | # Check that go mod tidy produces a zero diff; clean up any changes afterwards.
22 | go mod tidy && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
23 | # Check that go vet ./... produces a zero diff; clean up any changes afterwards.
24 | go vet ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
25 | # Check that go fmt ./... produces a zero diff; clean up any changes afterwards.
26 | go fmt ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
27 | # Check that go fix ./... produces a zero diff; clean up any changes afterwards.
28 | go fix ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/go
2 |
3 | ### Go ###
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 |
11 | # Test binary, build with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | ### Go Patch ###
18 | /vendor/
19 | /Godeps/
20 |
21 | # End of https://www.gitignore.io/api/go
22 |
23 | # IntelliJ project folder
24 | .idea/
25 |
26 | # IntelliJ project files
27 | *.iml
28 | *.ipr
29 | *.iws
30 | *.xml
31 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | # This is a manually created golangci.com yaml configuration with
2 | # some defaults explicitly provided. There is a large number of
3 | # linters we've enabled that are usually disabled by default.
4 | #
5 | # https://golangci-lint.run/usage/configuration/#config-file
6 |
7 | # This section provides the configuration for how golangci
8 | # outputs it results from the linters it executes.
9 | output:
10 | # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
11 | format: colored-line-number
12 |
13 | # print lines of code with issue, default is true
14 | print-issued-lines: true
15 |
16 | # print linter name in the end of issue text, default is true
17 | print-linter-name: true
18 |
19 | # make issues output unique by line, default is true
20 | uniq-by-line: true
21 |
22 | # This section provides the configuration for each linter
23 | # we've instructed golangci to execute.
24 | linters-settings:
25 | # https://github.com/mibk/dupl
26 | dupl:
27 | threshold: 100
28 |
29 | # https://github.com/ultraware/funlen
30 | funlen:
31 | lines: 100
32 | statements: 50
33 |
34 | # https://github.com/tommy-muehle/go-mnd
35 | gomnd:
36 | settings:
37 | mnd:
38 | # don't include the "operation" and "assign"
39 | checks: argument,case,condition,return
40 |
41 | # https://github.com/walle/lll
42 | lll:
43 | line-length: 100
44 |
45 | # https://github.com/mdempsky/maligned
46 | maligned:
47 | suggest-new: true
48 |
49 | # https://github.com/client9/misspell
50 | misspell:
51 | locale: US
52 |
53 | # https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint
54 | nolintlint:
55 | allow-leading-space: true # allow non-"machine-readable" format (ie. with leading space)
56 | allow-unused: false # allow nolint directives that don't address a linting issue
57 | require-explanation: true # require an explanation for nolint directives
58 | require-specific: true # require nolint directives to be specific about which linter is being skipped
59 |
60 | # This section provides the configuration for which linters
61 | # golangci will execute. Several of them were disabled by
62 | # default but we've opted to enable them.
63 | linters:
64 | # disable all linters as new linters might be added to golangci
65 | disable-all: true
66 |
67 | # enable a specific set of linters to run
68 | enable:
69 | - bodyclose
70 | - deadcode # enabled by default
71 | - dupl
72 | - errcheck # enabled by default
73 | - funlen
74 | - goconst
75 | - gocyclo
76 | - godot
77 | - gofmt
78 | - goimports
79 | - revive
80 | - gomnd
81 | - goprintffuncname
82 | - gosec
83 | - gosimple # enabled by default
84 | - govet # enabled by default
85 | - ineffassign # enabled by default
86 | - lll
87 | - misspell
88 | - nakedret
89 | - nolintlint
90 | - staticcheck # enabled by default
91 | - structcheck # enabled by default
92 | - stylecheck
93 | - typecheck # enabled by default
94 | - unconvert
95 | - unparam
96 | - unused # enabled by default
97 | - varcheck # enabled by default
98 | - whitespace
99 |
100 | # static list of linters we know golangci can run but we've
101 | # chosen to leave disabled for now
102 | # - asciicheck
103 | # - depguard
104 | # - dogsled
105 | # - exhaustive
106 | # - gochecknoinits
107 | # - gochecknoglobals
108 | # - gocognit
109 | # - gocritic
110 | # - godox
111 | # - goerr113
112 | # - interfacer
113 | # - nestif
114 | # - noctx
115 | # - prealloc
116 | # - rowserrcheck
117 | # - scopelint
118 | # - testpackage
119 | # - wsl
120 |
121 | # This section provides the configuration for how golangci
122 | # will report the issues it finds.
123 | issues:
124 | # Excluding configuration per-path, per-linter, per-text and per-source
125 | exclude-rules:
126 | # prevent linters from running on *_test.go files
127 | - path: _test\.go
128 | linters:
129 | - dupl
130 | - funlen
131 | - goconst
132 | - gocyclo
133 | - gomnd
134 | - lll
135 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | #
3 | # Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | # The `clean` target is intended to clean the workspace
6 | # and prepare the local changes for submission.
7 | #
8 | # Usage: `make clean`
9 | .PHONY: clean
10 | clean: tidy vet fmt fix
11 |
12 | # The `tidy` target is intended to clean up
13 | # the Go module files (go.mod & go.sum).
14 | #
15 | # Usage: `make tidy`
16 | .PHONY: tidy
17 | tidy:
18 | @echo
19 | @echo "### Tidying Go module"
20 | @go mod tidy
21 |
22 | # The `vet` target is intended to inspect the
23 | # Go source code for potential issues.
24 | #
25 | # Usage: `make vet`
26 | .PHONY: vet
27 | vet:
28 | @echo
29 | @echo "### Vetting Go code"
30 | @go vet ./...
31 |
32 | # The `fmt` target is intended to format the
33 | # Go source code to meet the language standards.
34 | #
35 | # Usage: `make fmt`
36 | .PHONY: fmt
37 | fmt:
38 | @echo
39 | @echo "### Formatting Go Code"
40 | @go fmt ./...
41 |
42 | # The `fix` target is intended to rewrite the
43 | # Go source code using old APIs.
44 | #
45 | # Usage: `make fix`
46 | .PHONY: fix
47 | fix:
48 | @echo
49 | @echo "### Fixing Go Code"
50 | @go fix ./...
51 |
52 | # The `test` target is intended to run
53 | # the tests for the Go source code.
54 | #
55 | # Usage: `make test`
56 | .PHONY: test
57 | test:
58 | @echo
59 | @echo "### Testing Go Code"
60 | @go test -race ./...
61 |
62 | # The `test-cover` target is intended to run
63 | # the tests for the Go source code and then
64 | # open the test coverage report.
65 | #
66 | # Usage: `make test-cover`
67 | .PHONY: test-cover
68 | test-cover:
69 | @echo
70 | @echo "### Creating test coverage report"
71 | @go test -race -covermode=atomic -coverprofile=coverage.out ./...
72 | @echo
73 | @echo "### Opening test coverage report"
74 | @go tool cover -html=coverage.out
75 |
76 | # The `check` target is intended to output all
77 | # dependencies from the Go module that need updates.
78 | #
79 | # Usage: `make check`
80 | .PHONY: check
81 | check: check-install
82 | @echo
83 | @echo "### Checking dependencies for updates"
84 | @go list -u -m -json all | go-mod-outdated -update
85 |
86 | # The `check-direct` target is intended to output direct
87 | # dependencies from the Go module that need updates.
88 | #
89 | # Usage: `make check-direct`
90 | .PHONY: check-direct
91 | check-direct: check-install
92 | @echo
93 | @echo "### Checking direct dependencies for updates"
94 | @go list -u -m -json all | go-mod-outdated -direct
95 |
96 | # The `check-full` target is intended to output
97 | # all dependencies from the Go module.
98 | #
99 | # Usage: `make check-full`
100 | .PHONY: check-full
101 | check-full: check-install
102 | @echo
103 | @echo "### Checking all dependencies for updates"
104 | @go list -u -m -json all | go-mod-outdated
105 |
106 | # The `check-install` target is intended to download
107 | # the tool used to check dependencies from the Go module.
108 | #
109 | # Usage: `make check-install`
110 | .PHONY: check-install
111 | check-install:
112 | @echo
113 | @echo "### Installing psampaz/go-mod-outdated"
114 | @go get -u github.com/psampaz/go-mod-outdated
115 |
116 | # The `bump-deps` target is intended to upgrade
117 | # non-test dependencies for the Go module.
118 | #
119 | # Usage: `make bump-deps`
120 | .PHONY: bump-deps
121 | bump-deps: check
122 | @echo
123 | @echo "### Upgrading dependencies"
124 | @go get -u ./...
125 |
126 | # The `bump-deps-full` target is intended to upgrade
127 | # all dependencies for the Go module.
128 | #
129 | # Usage: `make bump-deps-full`
130 | .PHONY: bump-deps-full
131 | bump-deps-full: check
132 | @echo
133 | @echo "### Upgrading all dependencies"
134 | @go get -t -u ./...
135 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | # The is the default codecov.io yaml configuration all projects use
2 | # that do not have their own codecov.yml already in the project.
3 | #
4 | # https://docs.codecov.io/docs/codecov-yaml#section-default-yaml
5 |
6 | # This section provides the generic configuration for codecov.
7 | #
8 | # https://docs.codecov.io/docs/codecovyml-reference#section-codecov
9 | codecov:
10 |
11 | require_ci_to_pass: yes
12 |
13 | # This section provides the configuration for the
14 | # coverage report codecov analyzes for results.
15 | #
16 | # https://docs.codecov.io/docs/codecovyml-reference#section-coverage
17 | coverage:
18 |
19 | precision: 2
20 | round: down
21 | range: "70...100"
22 |
23 | # https://docs.codecov.io/docs/commit-status
24 | status:
25 | # set rules for the project status report
26 | project:
27 | default:
28 | target: 90%
29 | threshold: 0%
30 | # disable the patch status report
31 | patch: off
32 | # disable the changes status report
33 | changes: off
34 |
35 | # This section provides the configuration for the
36 | # parsers codecov uses for the coverage report.
37 | #
38 | # https://docs.codecov.io/docs/codecovyml-reference#section-parsers
39 | parsers:
40 |
41 | gcov:
42 |
43 | branch_detection:
44 | conditional: yes
45 | loop: yes
46 | method: no
47 | macro: no
48 |
49 | # This section provides the configuration for the
50 | # comments codecov makes to open pull requests.
51 | #
52 | # https://docs.codecov.io/docs/codecovyml-reference#section-comment
53 | comment:
54 |
55 | layout: "reach, diff, flags, files"
56 | behavior: default
57 | require_changes: no
58 |
--------------------------------------------------------------------------------
/compiler/context.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package compiler
6 |
7 | import (
8 | "context"
9 |
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | // key defines the key type for storing
14 | // the compiler Engine in the context.
15 | const key = "compiler"
16 |
17 | // FromContext retrieves the compiler Engine from the context.Context.
18 | func FromContext(c context.Context) Engine {
19 | // get compiler value from context.Context
20 | v := c.Value(key)
21 | if v == nil {
22 | return nil
23 | }
24 |
25 | // cast compiler value to expected Engine type
26 | e, ok := v.(Engine)
27 | if !ok {
28 | return nil
29 | }
30 |
31 | return e
32 | }
33 |
34 | // FromGinContext retrieves the compiler Engine from the gin.Context.
35 | func FromGinContext(c *gin.Context) Engine {
36 | // get compiler value from gin.Context
37 | //
38 | // https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#Context.Get
39 | v, ok := c.Get(key)
40 | if !ok {
41 | return nil
42 | }
43 |
44 | // cast compiler value to expected Engine type
45 | e, ok := v.(Engine)
46 | if !ok {
47 | return nil
48 | }
49 |
50 | return e
51 | }
52 |
53 | // WithContext inserts the compiler Engine into the context.Context.
54 | func WithContext(c context.Context, e Engine) context.Context {
55 | // set the compiler Engine in the context.Context
56 | //
57 | // nolint: revive,staticcheck // ignore using string with context value
58 | return context.WithValue(c, key, e)
59 | }
60 |
61 | // WithGinContext inserts the compiler Engine into the gin.Context.
62 | func WithGinContext(c *gin.Context, e Engine) {
63 | // set the compiler Engine in the gin.Context
64 | //
65 | // https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#Context.Set
66 | c.Set(key, e)
67 | }
68 |
--------------------------------------------------------------------------------
/compiler/context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package compiler
6 |
7 | import (
8 | "context"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func TestCompiler_FromContext(t *testing.T) {
16 | // setup types
17 | var _engine Engine
18 |
19 | // setup tests
20 | tests := []struct {
21 | context context.Context
22 | want Engine
23 | }{
24 | {
25 | // nolint: staticcheck // ignore using string with context value
26 | context: context.WithValue(context.Background(), key, _engine),
27 | want: _engine,
28 | },
29 | {
30 | context: context.Background(),
31 | want: nil,
32 | },
33 | {
34 | // nolint: staticcheck // ignore using string with context value
35 | context: context.WithValue(context.Background(), key, "foo"),
36 | want: nil,
37 | },
38 | }
39 |
40 | // run tests
41 | for _, test := range tests {
42 | got := FromContext(test.context)
43 |
44 | if !reflect.DeepEqual(got, test.want) {
45 | t.Errorf("FromContext is %v, want %v", got, test.want)
46 | }
47 | }
48 | }
49 |
50 | func TestCompiler_FromGinContext(t *testing.T) {
51 | // setup types
52 | var _engine Engine
53 |
54 | // setup tests
55 | tests := []struct {
56 | context *gin.Context
57 | value interface{}
58 | want Engine
59 | }{
60 | {
61 | context: new(gin.Context),
62 | value: _engine,
63 | want: _engine,
64 | },
65 | {
66 | context: new(gin.Context),
67 | value: nil,
68 | want: nil,
69 | },
70 | {
71 | context: new(gin.Context),
72 | value: "foo",
73 | want: nil,
74 | },
75 | }
76 |
77 | // run tests
78 | for _, test := range tests {
79 | if test.value != nil {
80 | test.context.Set(key, test.value)
81 | }
82 |
83 | got := FromGinContext(test.context)
84 |
85 | if !reflect.DeepEqual(got, test.want) {
86 | t.Errorf("FromGinContext is %v, want %v", got, test.want)
87 | }
88 | }
89 | }
90 |
91 | func TestCompiler_WithContext(t *testing.T) {
92 | // setup types
93 | var _engine Engine
94 |
95 | // nolint: staticcheck // ignore using string with context value
96 | want := context.WithValue(context.Background(), key, _engine)
97 |
98 | // run test
99 | got := WithContext(context.Background(), _engine)
100 |
101 | if !reflect.DeepEqual(got, want) {
102 | t.Errorf("WithContext is %v, want %v", got, want)
103 | }
104 | }
105 |
106 | func TestCompiler_WithGinContext(t *testing.T) {
107 | // setup types
108 | var _engine Engine
109 |
110 | want := new(gin.Context)
111 | want.Set(key, _engine)
112 |
113 | // run test
114 | got := new(gin.Context)
115 | WithGinContext(got, _engine)
116 |
117 | if !reflect.DeepEqual(got, want) {
118 | t.Errorf("WithGinContext is %v, want %v", got, want)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/compiler/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package compiler provides the ability for Vela to
6 | // reconstruct a yaml configuration into an executable
7 | // pipeline.
8 | //
9 | // Usage:
10 | //
11 | // import "github.com/go-vela/compiler/compiler"
12 | package compiler
13 |
--------------------------------------------------------------------------------
/compiler/engine.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package compiler
6 |
7 | import (
8 | "github.com/go-vela/types"
9 | "github.com/go-vela/types/library"
10 | "github.com/go-vela/types/pipeline"
11 | "github.com/go-vela/types/raw"
12 | "github.com/go-vela/types/yaml"
13 | )
14 |
15 | // Engine represents an interface for converting a yaml
16 | // configuration to an executable pipeline for Vela.
17 | type Engine interface {
18 | // Compiler Interface Functions
19 |
20 | // Compile defines a function that produces an executable
21 | // representation of a pipeline from an object. This calls
22 | // Parse internally to convert the object to a yaml configuration.
23 | Compile(interface{}) (*pipeline.Build, error)
24 |
25 | // Duplicate defines a function that
26 | // creates a clone of the Engine.
27 | Duplicate() Engine
28 |
29 | // Parse defines a function that converts
30 | // an object to a yaml configuration.
31 | Parse(interface{}) (*yaml.Build, error)
32 |
33 | // ParseRaw defines a function that converts
34 | // an object to a string.
35 | ParseRaw(interface{}) (string, error)
36 |
37 | // Validate defines a function that verifies
38 | // the yaml configuration is accurate.
39 | Validate(*yaml.Build) error
40 |
41 | // Clone Compiler Interface Functions
42 |
43 | // CloneStage defines a function that injects the
44 | // clone stage process into a yaml configuration.
45 | CloneStage(*yaml.Build) (*yaml.Build, error)
46 | // CloneStep defines a function that injects the
47 | // clone step process into a yaml configuration.
48 | CloneStep(*yaml.Build) (*yaml.Build, error)
49 |
50 | // Environment Compiler Interface Functions
51 |
52 | // EnvironmentStages defines a function that injects the environment
53 | // variables for each step in every stage into a yaml configuration.
54 | EnvironmentStages(yaml.StageSlice, raw.StringSliceMap) (yaml.StageSlice, error)
55 | // EnvironmentSteps defines a function that injects the environment
56 | // variables for each step into a yaml configuration.
57 | EnvironmentSteps(yaml.StepSlice, raw.StringSliceMap) (yaml.StepSlice, error)
58 | // EnvironmentStep defines a function that injects the environment
59 | // variables for a single step into a yaml configuration.
60 | EnvironmentStep(*yaml.Step, raw.StringSliceMap) (*yaml.Step, error)
61 | // EnvironmentServices defines a function that injects the environment
62 | // variables for each service into a yaml configuration.
63 | EnvironmentServices(yaml.ServiceSlice, raw.StringSliceMap) (yaml.ServiceSlice, error)
64 |
65 | // Expand Compiler Interface Functions
66 |
67 | // ExpandStages defines a function that injects the template
68 | // for each templated step in every stage in a yaml configuration.
69 | // nolint: lll // ignore long line length due to return args
70 | ExpandStages(*yaml.Build, map[string]*yaml.Template) (yaml.StageSlice, yaml.SecretSlice, yaml.ServiceSlice, raw.StringSliceMap, error)
71 | // ExpandSteps defines a function that injects the template
72 | // for each templated step in a yaml configuration.
73 | // nolint: lll // ignore long line length due to return args
74 | ExpandSteps(*yaml.Build, map[string]*yaml.Template) (yaml.StepSlice, yaml.SecretSlice, yaml.ServiceSlice, raw.StringSliceMap, error)
75 |
76 | // Init Compiler Interface Functions
77 |
78 | // InitStage defines a function that injects the
79 | // init stage process into a yaml configuration.
80 | InitStage(*yaml.Build) (*yaml.Build, error)
81 | // InitStep step process into a yaml configuration.
82 | InitStep(*yaml.Build) (*yaml.Build, error)
83 |
84 | // Script Compiler Interface Functions
85 |
86 | // ScriptStages defines a function that injects the script
87 | // for each step in every stage in a yaml configuration.
88 | ScriptStages(yaml.StageSlice) (yaml.StageSlice, error)
89 | // ScriptSteps defines a function that injects the script
90 | // for each step in a yaml configuration.
91 | ScriptSteps(yaml.StepSlice) (yaml.StepSlice, error)
92 |
93 | // Substitute Compiler Interface Functions
94 |
95 | // SubstituteStages defines a function that replaces every
96 | // declared environment variable with it's corresponding
97 | // value for each step in every stage in a yaml configuration.
98 | SubstituteStages(yaml.StageSlice) (yaml.StageSlice, error)
99 | // SubstituteSteps defines a function that replaces every
100 | // declared environment variable with it's corresponding
101 | // value for each step in a yaml configuration.
102 | SubstituteSteps(yaml.StepSlice) (yaml.StepSlice, error)
103 |
104 | // Transform Compiler Interface Functions
105 |
106 | // TransformStages defines a function that converts a yaml
107 | // configuration with stages into an executable pipeline.
108 | TransformStages(*pipeline.RuleData, *yaml.Build) (*pipeline.Build, error)
109 | // TransformSteps defines a function that converts a yaml
110 | // configuration with steps into an executable pipeline.
111 | TransformSteps(*pipeline.RuleData, *yaml.Build) (*pipeline.Build, error)
112 |
113 | // With Compiler Interface Functions
114 |
115 | // WithBuild defines a function that sets
116 | // the library build type in the Engine.
117 | WithBuild(*library.Build) Engine
118 | // WithComment defines a function that sets
119 | // the comment in the Engine.
120 | WithComment(string) Engine
121 | // WithFiles defines a function that sets
122 | // the changeset files in the Engine.
123 | WithFiles([]string) Engine
124 | // WithLocal defines a function that sets
125 | // the compiler local field in the Engine.
126 | WithLocal(bool) Engine
127 | // WithMetadata defines a function that sets
128 | // the compiler Metadata type in the Engine.
129 | WithMetadata(*types.Metadata) Engine
130 | // WithRepo defines a function that sets
131 | // the library repo type in the Engine.
132 | WithRepo(*library.Repo) Engine
133 | // WithUser defines a function that sets
134 | // the library user type in the Engine.
135 | WithUser(*library.User) Engine
136 | // WithUser defines a function that sets
137 | // the private github client in the Engine.
138 | WithPrivateGitHub(string, string) Engine
139 | }
140 |
--------------------------------------------------------------------------------
/compiler/native/clone.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "github.com/go-vela/types/constants"
9 | "github.com/go-vela/types/yaml"
10 | )
11 |
12 | const (
13 | // default image for clone process.
14 | cloneImage = "target/vela-git:v0.4.0"
15 | // default name for clone stage.
16 | cloneStageName = "clone"
17 | // default name for clone step.
18 | cloneStepName = "clone"
19 | )
20 |
21 | // CloneStage injects the clone stage process into a yaml configuration.
22 | func (c *client) CloneStage(p *yaml.Build) (*yaml.Build, error) {
23 | // check if the compiler is setup for a local pipeline
24 | if c.local {
25 | // skip injecting the clone process
26 | return p, nil
27 | }
28 |
29 | stages := yaml.StageSlice{}
30 |
31 | // create new clone stage
32 | clone := &yaml.Stage{
33 | Name: cloneStageName,
34 | Steps: yaml.StepSlice{
35 | &yaml.Step{
36 | Detach: false,
37 | Image: cloneImage,
38 | Name: cloneStepName,
39 | Privileged: false,
40 | Pull: constants.PullNotPresent,
41 | },
42 | },
43 | }
44 |
45 | // add clone stage as first stage
46 | stages = append(stages, clone)
47 |
48 | // add existing stages after clone stage
49 | stages = append(stages, p.Stages...)
50 |
51 | // overwrite existing stages
52 | p.Stages = stages
53 |
54 | return p, nil
55 | }
56 |
57 | // CloneStep injects the clone step process into a yaml configuration.
58 | func (c *client) CloneStep(p *yaml.Build) (*yaml.Build, error) {
59 | // check if the compiler is setup for a local pipeline
60 | if c.local {
61 | // skip injecting the clone process
62 | return p, nil
63 | }
64 |
65 | steps := yaml.StepSlice{}
66 |
67 | // create new clone step
68 | clone := &yaml.Step{
69 | Detach: false,
70 | Image: cloneImage,
71 | Name: cloneStepName,
72 | Privileged: false,
73 | Pull: constants.PullNotPresent,
74 | }
75 |
76 | // add clone step as first step
77 | steps = append(steps, clone)
78 |
79 | // add existing steps after clone step
80 | steps = append(steps, p.Steps...)
81 |
82 | // overwrite existing steps
83 | p.Steps = steps
84 |
85 | return p, nil
86 | }
87 |
--------------------------------------------------------------------------------
/compiler/native/clone_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "flag"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/go-vela/types/yaml"
13 | "github.com/urfave/cli/v2"
14 | )
15 |
16 | func TestNative_CloneStage(t *testing.T) {
17 | // setup types
18 | set := flag.NewFlagSet("test", 0)
19 | c := cli.NewContext(nil, set, nil)
20 |
21 | str := "foo"
22 | p := &yaml.Build{
23 | Version: "v1",
24 | Stages: yaml.StageSlice{
25 | &yaml.Stage{
26 | Name: str,
27 | Steps: yaml.StepSlice{
28 | &yaml.Step{
29 | Image: "alpine",
30 | Name: str,
31 | Pull: "not_present",
32 | },
33 | },
34 | },
35 | },
36 | }
37 |
38 | // setup tests
39 | tests := []struct {
40 | failure bool
41 | local bool
42 | pipeline *yaml.Build
43 | want *yaml.Build
44 | }{
45 | {
46 | failure: false,
47 | local: false,
48 | pipeline: p,
49 | want: &yaml.Build{
50 | Version: "v1",
51 | Stages: yaml.StageSlice{
52 | &yaml.Stage{
53 | Name: "clone",
54 | Steps: yaml.StepSlice{
55 | &yaml.Step{
56 | Image: "target/vela-git:v0.4.0",
57 | Name: "clone",
58 | Pull: "not_present",
59 | },
60 | },
61 | },
62 | &yaml.Stage{
63 | Name: str,
64 | Steps: yaml.StepSlice{
65 | &yaml.Step{
66 | Image: "alpine",
67 | Name: str,
68 | Pull: "not_present",
69 | },
70 | },
71 | },
72 | },
73 | },
74 | },
75 | {
76 | failure: false,
77 | local: true,
78 | pipeline: p,
79 | want: p,
80 | },
81 | }
82 |
83 | // run tests
84 | for _, test := range tests {
85 | compiler, err := New(c)
86 | if err != nil {
87 | t.Errorf("unable to create new compiler: %v", err)
88 | }
89 |
90 | // set the local field for the test
91 | compiler.WithLocal(test.local)
92 |
93 | got, err := compiler.CloneStage(test.pipeline)
94 |
95 | if test.failure {
96 | if err == nil {
97 | t.Errorf("CloneStage should have returned err")
98 | }
99 |
100 | continue
101 | }
102 |
103 | if err != nil {
104 | t.Errorf("CloneStage returned err: %v", err)
105 | }
106 |
107 | if !reflect.DeepEqual(got, test.want) {
108 | t.Errorf("CloneStage is %v, want %v", got, test.want)
109 | }
110 | }
111 | }
112 |
113 | func TestNative_CloneStep(t *testing.T) {
114 | // setup types
115 | set := flag.NewFlagSet("test", 0)
116 | c := cli.NewContext(nil, set, nil)
117 |
118 | str := "foo"
119 | p := &yaml.Build{
120 | Version: "v1",
121 | Steps: yaml.StepSlice{
122 | &yaml.Step{
123 | Image: "alpine",
124 | Name: str,
125 | Pull: "not_present",
126 | },
127 | },
128 | }
129 |
130 | // setup tests
131 | tests := []struct {
132 | failure bool
133 | local bool
134 | pipeline *yaml.Build
135 | want *yaml.Build
136 | }{
137 | {
138 | failure: false,
139 | local: false,
140 | pipeline: p,
141 | want: &yaml.Build{
142 | Version: "v1",
143 | Steps: yaml.StepSlice{
144 | &yaml.Step{
145 | Image: "target/vela-git:v0.4.0",
146 | Name: "clone",
147 | Pull: "not_present",
148 | },
149 | &yaml.Step{
150 | Image: "alpine",
151 | Name: str,
152 | Pull: "not_present",
153 | },
154 | },
155 | },
156 | },
157 | {
158 | failure: false,
159 | local: true,
160 | pipeline: p,
161 | want: p,
162 | },
163 | }
164 |
165 | // run tests
166 | for _, test := range tests {
167 | compiler, err := New(c)
168 | if err != nil {
169 | t.Errorf("Unable to create new compiler: %v", err)
170 | }
171 |
172 | // set the local field for the test
173 | compiler.WithLocal(test.local)
174 |
175 | got, err := compiler.CloneStep(test.pipeline)
176 |
177 | if test.failure {
178 | if err == nil {
179 | t.Errorf("CloneStep should have returned err")
180 | }
181 |
182 | continue
183 | }
184 |
185 | if err != nil {
186 | t.Errorf("CloneStep returned err: %v", err)
187 | }
188 |
189 | if !reflect.DeepEqual(got, test.want) {
190 | t.Errorf("CloneStep is %v, want %v", got, test.want)
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/compiler/native/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package native provides the ability for Vela to
6 | // reconstruct a yaml configuration into an executable
7 | // pipeline.
8 | //
9 | // Usage:
10 | //
11 | // import "github.com/go-vela/compiler/compiler/native"
12 | package native
13 |
--------------------------------------------------------------------------------
/compiler/native/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "github.com/go-vela/types/constants"
9 | "github.com/go-vela/types/yaml"
10 | )
11 |
12 | const (
13 | // default image for init process.
14 | initImage = "#init"
15 | // default name for init stage.
16 | initStageName = "init"
17 | // default name for init step.
18 | initStepName = "init"
19 | )
20 |
21 | // InitStage injects the init stage process into a yaml configuration.
22 | func (c *client) InitStage(p *yaml.Build) (*yaml.Build, error) {
23 | stages := yaml.StageSlice{}
24 |
25 | // create new clone stage
26 | init := &yaml.Stage{
27 | Name: initStageName,
28 | Steps: yaml.StepSlice{
29 | &yaml.Step{
30 | Detach: false,
31 | Image: initImage,
32 | Name: initStepName,
33 | Privileged: false,
34 | Pull: constants.PullNotPresent,
35 | },
36 | },
37 | }
38 |
39 | // add init stage as first stage
40 | stages = append(stages, init)
41 |
42 | // add existing stages after init stage
43 | stages = append(stages, p.Stages...)
44 |
45 | // overwrite existing stages
46 | p.Stages = stages
47 |
48 | return p, nil
49 | }
50 |
51 | // InitStep injects the init step process into a yaml configuration.
52 | func (c *client) InitStep(p *yaml.Build) (*yaml.Build, error) {
53 | steps := yaml.StepSlice{}
54 |
55 | // create new init step
56 | init := &yaml.Step{
57 | Detach: false,
58 | Image: initImage,
59 | Name: initStepName,
60 | Privileged: false,
61 | Pull: constants.PullNotPresent,
62 | }
63 |
64 | // add init step as first step
65 | steps = append(steps, init)
66 |
67 | // add existing steps after init step
68 | steps = append(steps, p.Steps...)
69 |
70 | // overwrite existing steps
71 | p.Steps = steps
72 |
73 | return p, nil
74 | }
75 |
--------------------------------------------------------------------------------
/compiler/native/initialize_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "flag"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/go-vela/types/yaml"
13 | "github.com/urfave/cli/v2"
14 | )
15 |
16 | func TestNative_InitStage(t *testing.T) {
17 | // setup types
18 | set := flag.NewFlagSet("test", 0)
19 | c := cli.NewContext(nil, set, nil)
20 |
21 | str := "foo"
22 | p := &yaml.Build{
23 | Version: "v1",
24 | Stages: yaml.StageSlice{
25 | &yaml.Stage{
26 | Name: str,
27 | Steps: yaml.StepSlice{
28 | &yaml.Step{
29 | Image: "alpine",
30 | Name: str,
31 | Pull: "not_present",
32 | },
33 | },
34 | },
35 | },
36 | }
37 |
38 | want := &yaml.Build{
39 | Version: "v1",
40 | Stages: yaml.StageSlice{
41 | &yaml.Stage{
42 | Name: "init",
43 | Steps: yaml.StepSlice{
44 | &yaml.Step{
45 | Image: "#init",
46 | Name: "init",
47 | Pull: "not_present",
48 | },
49 | },
50 | },
51 | &yaml.Stage{
52 | Name: str,
53 | Steps: yaml.StepSlice{
54 | &yaml.Step{
55 | Image: "alpine",
56 | Name: str,
57 | Pull: "not_present",
58 | },
59 | },
60 | },
61 | },
62 | }
63 |
64 | // run test
65 | compiler, err := New(c)
66 | if err != nil {
67 | t.Errorf("Unable to create new compiler: %v", err)
68 | }
69 |
70 | got, err := compiler.InitStage(p)
71 | if err != nil {
72 | t.Errorf("InitStage returned err: %v", err)
73 | }
74 |
75 | if !reflect.DeepEqual(got, want) {
76 | t.Errorf("InitStage is %v, want %v", got, want)
77 | }
78 | }
79 |
80 | func TestNative_InitStep(t *testing.T) {
81 | // setup types
82 | set := flag.NewFlagSet("test", 0)
83 | c := cli.NewContext(nil, set, nil)
84 |
85 | str := "foo"
86 | p := &yaml.Build{
87 | Version: "v1",
88 | Steps: yaml.StepSlice{
89 | &yaml.Step{
90 | Image: "alpine",
91 | Name: str,
92 | Pull: "not_present",
93 | },
94 | },
95 | }
96 |
97 | want := &yaml.Build{
98 | Version: "v1",
99 | Steps: yaml.StepSlice{
100 | &yaml.Step{
101 | Image: "#init",
102 | Name: "init",
103 | Pull: "not_present",
104 | },
105 | &yaml.Step{
106 | Image: "alpine",
107 | Name: str,
108 | Pull: "not_present",
109 | },
110 | },
111 | }
112 | // run test
113 | compiler, err := New(c)
114 | if err != nil {
115 | t.Errorf("Unable to create new compiler: %v", err)
116 | }
117 |
118 | got, err := compiler.InitStep(p)
119 | if err != nil {
120 | t.Errorf("InitStep returned err: %v", err)
121 | }
122 |
123 | if !reflect.DeepEqual(got, want) {
124 | t.Errorf("InitStep is %v, want %v", got, want)
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/compiler/native/native.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "time"
9 |
10 | "github.com/go-vela/compiler/compiler"
11 |
12 | "github.com/go-vela/compiler/registry"
13 | "github.com/go-vela/compiler/registry/github"
14 |
15 | "github.com/go-vela/types"
16 | "github.com/go-vela/types/library"
17 |
18 | "github.com/sirupsen/logrus"
19 | "github.com/urfave/cli/v2"
20 | )
21 |
22 | type ModificationConfig struct {
23 | Timeout time.Duration
24 | Retries int
25 | Endpoint string
26 | Secret string
27 | }
28 |
29 | type client struct {
30 | Github registry.Service
31 | PrivateGithub registry.Service
32 | UsePrivateGithub bool
33 | ModificationService ModificationConfig
34 |
35 | build *library.Build
36 | comment string
37 | files []string
38 | local bool
39 | metadata *types.Metadata
40 | repo *library.Repo
41 | user *library.User
42 | }
43 |
44 | // New returns a Pipeline implementation that integrates with the supported registries.
45 | //
46 | // nolint: revive // ignore returning unexported client
47 | func New(ctx *cli.Context) (*client, error) {
48 | logrus.Debug("Creating registry clients from CLI configuration")
49 | c := new(client)
50 |
51 | if ctx.String("modification-addr") != "" {
52 | c.ModificationService = ModificationConfig{
53 | Timeout: ctx.Duration("modification-timeout"),
54 | Endpoint: ctx.String("modification-addr"),
55 | Secret: ctx.String("modification-secret"),
56 | Retries: ctx.Int("modification-retries"),
57 | }
58 | }
59 |
60 | // setup github template service
61 | github, err := setupGithub()
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | c.Github = github
67 |
68 | if ctx.Bool("github-driver") {
69 | logrus.Tracef("setting up Private GitHub Client for %s", ctx.String("github-url"))
70 | // setup private github service
71 | privGithub, err := setupPrivateGithub(ctx.String("github-url"), ctx.String("github-token"))
72 | if err != nil {
73 | return nil, err
74 | }
75 |
76 | c.PrivateGithub = privGithub
77 | c.UsePrivateGithub = true
78 | }
79 |
80 | return c, nil
81 | }
82 |
83 | // setupGithub is a helper function to setup the
84 | // Github registry service from the CLI arguments.
85 | func setupGithub() (registry.Service, error) {
86 | logrus.Tracef("Creating %s registry client from CLI configuration", "github")
87 | return github.New("", "")
88 | }
89 |
90 | // setupPrivateGithub is a helper function to setup the
91 | // Github registry service from the CLI arguments.
92 | func setupPrivateGithub(addr, token string) (registry.Service, error) {
93 | logrus.Tracef("Creating private %s registry client from CLI configuration", "github")
94 | return github.New(addr, token)
95 | }
96 |
97 | // Duplicate creates a clone of the Engine.
98 | func (c *client) Duplicate() compiler.Engine {
99 | cc := new(client)
100 |
101 | // copy the essential fields from the existing client
102 | cc.Github = c.Github
103 | cc.PrivateGithub = c.PrivateGithub
104 | cc.UsePrivateGithub = c.UsePrivateGithub
105 | cc.ModificationService = c.ModificationService
106 |
107 | return cc
108 | }
109 |
110 | // WithBuild sets the library build type in the Engine.
111 | func (c *client) WithBuild(b *library.Build) compiler.Engine {
112 | if b != nil {
113 | c.build = b
114 | }
115 |
116 | return c
117 | }
118 |
119 | // WithComment sets the comment in the Engine.
120 | func (c *client) WithComment(cmt string) compiler.Engine {
121 | if cmt != "" {
122 | c.comment = cmt
123 | }
124 |
125 | return c
126 | }
127 |
128 | // WithFiles sets the changeset files in the Engine.
129 | func (c *client) WithFiles(f []string) compiler.Engine {
130 | if f != nil {
131 | c.files = f
132 | }
133 |
134 | return c
135 | }
136 |
137 | // WithLocal sets the compiler metadata type in the Engine.
138 | func (c *client) WithLocal(local bool) compiler.Engine {
139 | c.local = local
140 |
141 | return c
142 | }
143 |
144 | // WithMetadata sets the compiler metadata type in the Engine.
145 | func (c *client) WithMetadata(m *types.Metadata) compiler.Engine {
146 | if m != nil {
147 | c.metadata = m
148 | }
149 |
150 | return c
151 | }
152 |
153 | // WithPrivateGitHub sets the private github client in the Engine.
154 | func (c *client) WithPrivateGitHub(url, token string) compiler.Engine {
155 | if len(url) != 0 && len(token) != 0 {
156 | privGithub, _ := setupPrivateGithub(url, token)
157 |
158 | c.PrivateGithub = privGithub
159 | }
160 |
161 | return c
162 | }
163 |
164 | // WithRepo sets the library repo type in the Engine.
165 | func (c *client) WithRepo(r *library.Repo) compiler.Engine {
166 | if r != nil {
167 | c.repo = r
168 | }
169 |
170 | return c
171 | }
172 |
173 | // WithUser sets the library user type in the Engine.
174 | func (c *client) WithUser(u *library.User) compiler.Engine {
175 | if u != nil {
176 | c.user = u
177 | }
178 |
179 | return c
180 | }
181 |
--------------------------------------------------------------------------------
/compiler/native/parse.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "fmt"
9 | "io"
10 | "io/ioutil"
11 | "os"
12 |
13 | "github.com/go-vela/compiler/template/native"
14 | "github.com/go-vela/compiler/template/starlark"
15 | "github.com/go-vela/types/constants"
16 | types "github.com/go-vela/types/yaml"
17 |
18 | "github.com/buildkite/yaml"
19 | )
20 |
21 | // ParseRaw converts an object to a string.
22 | func (c *client) ParseRaw(v interface{}) (string, error) {
23 | switch v := v.(type) {
24 | case []byte:
25 | return string(v), nil
26 | case *os.File:
27 | return ParseFileRaw(v)
28 | case io.Reader:
29 | return ParseReaderRaw(v)
30 | case string:
31 | // check if string is path to file
32 | _, err := os.Stat(v)
33 | if err == nil {
34 | // parse string as path to yaml configuration
35 | return ParsePathRaw(v)
36 | }
37 |
38 | // parse string as yaml configuration
39 | return v, nil
40 | default:
41 | return "", fmt.Errorf("unable to parse yaml: unrecognized type %T", v)
42 | }
43 | }
44 |
45 | // Parse converts an object to a yaml configuration.
46 | func (c *client) Parse(v interface{}) (*types.Build, error) {
47 | var p *types.Build
48 |
49 | switch c.repo.GetPipelineType() {
50 | case constants.PipelineTypeGo:
51 | // expand the base configuration
52 | parsedRaw, err := c.ParseRaw(v)
53 | if err != nil {
54 | return nil, err
55 | }
56 | p, err = native.RenderBuild(parsedRaw, c.EnvironmentBuild())
57 | if err != nil {
58 | return nil, err
59 | }
60 | case constants.PipelineTypeStarlark:
61 | // expand the base configuration
62 | parsedRaw, err := c.ParseRaw(v)
63 | if err != nil {
64 | return nil, err
65 | }
66 | p, err = starlark.RenderBuild(parsedRaw, c.EnvironmentBuild())
67 | if err != nil {
68 | return nil, err
69 | }
70 | case constants.PipelineTypeYAML, "":
71 | switch v := v.(type) {
72 | case []byte:
73 | return ParseBytes(v)
74 | case *os.File:
75 | return ParseFile(v)
76 | case io.Reader:
77 | return ParseReader(v)
78 | case string:
79 | // check if string is path to file
80 | _, err := os.Stat(v)
81 | if err == nil {
82 | // parse string as path to yaml configuration
83 | return ParsePath(v)
84 | }
85 |
86 | // parse string as yaml configuration
87 | return ParseString(v)
88 | default:
89 | return nil, fmt.Errorf("unable to parse yaml: unrecognized type %T", v)
90 | }
91 | default:
92 | // nolint:lll // detailed error message
93 | return nil, fmt.Errorf("unable to parse config: unrecognized pipeline_type of %s", c.repo.GetPipelineType())
94 | }
95 |
96 | return p, nil
97 | }
98 |
99 | // ParseBytes converts a byte slice to a yaml configuration.
100 | func ParseBytes(b []byte) (*types.Build, error) {
101 | config := new(types.Build)
102 |
103 | // unmarshal the bytes into the yaml configuration
104 | err := yaml.Unmarshal(b, config)
105 | if err != nil {
106 | return nil, fmt.Errorf("unable to unmarshal yaml: %v", err)
107 | }
108 |
109 | return config, nil
110 | }
111 |
112 | // ParseFile converts an os.File into a yaml configuration.
113 | func ParseFile(f *os.File) (*types.Build, error) {
114 | return ParseReader(f)
115 | }
116 |
117 | // ParseFileRaw converts an os.File into a string.
118 | func ParseFileRaw(f *os.File) (string, error) {
119 | return ParseReaderRaw(f)
120 | }
121 |
122 | // ParsePath converts a file path into a yaml configuration.
123 | func ParsePath(p string) (*types.Build, error) {
124 | // open the file for reading
125 | f, err := os.Open(p)
126 | if err != nil {
127 | return nil, fmt.Errorf("unable to open yaml file %s: %v", p, err)
128 | }
129 |
130 | defer f.Close()
131 |
132 | return ParseReader(f)
133 | }
134 |
135 | // ParsePathRaw converts a file path into a yaml configuration.
136 | func ParsePathRaw(p string) (string, error) {
137 | // open the file for reading
138 | f, err := os.Open(p)
139 | if err != nil {
140 | return "", fmt.Errorf("unable to open yaml file %s: %v", p, err)
141 | }
142 |
143 | defer f.Close()
144 |
145 | return ParseReaderRaw(f)
146 | }
147 |
148 | // ParseReader converts an io.Reader into a yaml configuration.
149 | func ParseReader(r io.Reader) (*types.Build, error) {
150 | // read all the bytes from the reader
151 | b, err := ioutil.ReadAll(r)
152 | if err != nil {
153 | return nil, fmt.Errorf("unable to read bytes for yaml: %v", err)
154 | }
155 |
156 | return ParseBytes(b)
157 | }
158 |
159 | // ParseReaderRaw converts an io.Reader into a yaml configuration.
160 | func ParseReaderRaw(r io.Reader) (string, error) {
161 | // read all the bytes from the reader
162 | b, err := ioutil.ReadAll(r)
163 | if err != nil {
164 | return "", fmt.Errorf("unable to read bytes for yaml: %v", err)
165 | }
166 |
167 | return string(b), nil
168 | }
169 |
170 | // ParseString converts a string into a yaml configuration.
171 | func ParseString(s string) (*types.Build, error) {
172 | return ParseBytes([]byte(s))
173 | }
174 |
--------------------------------------------------------------------------------
/compiler/native/script.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "bytes"
9 | "encoding/base64"
10 | "fmt"
11 | "strings"
12 |
13 | "github.com/go-vela/types/yaml"
14 | )
15 |
16 | // ScriptStages injects the script for each step in every stage in a yaml configuration.
17 | func (c *client) ScriptStages(s yaml.StageSlice) (yaml.StageSlice, error) {
18 | // iterate through all stages
19 | for _, stage := range s {
20 | // inject the scripts into the steps for the stage
21 | steps, err := c.ScriptSteps(stage.Steps)
22 | if err != nil {
23 | return nil, err
24 | }
25 |
26 | stage.Steps = steps
27 | }
28 |
29 | return s, nil
30 | }
31 |
32 | // ScriptSteps injects the script for each step in a yaml configuration.
33 | func (c *client) ScriptSteps(s yaml.StepSlice) (yaml.StepSlice, error) {
34 | // iterate through all steps
35 | for _, step := range s {
36 | // skip if no commands block for the step
37 | if len(step.Commands) == 0 {
38 | continue
39 | }
40 |
41 | // set the default home
42 | // nolint: goconst // ignore making this a constant for now
43 | home := "/root"
44 | // override the home value if user is defined
45 | // TODO:
46 | // - add ability to override user home directory
47 | if step.User != "" {
48 | home = fmt.Sprintf("/home/%s", step.User)
49 | }
50 |
51 | // generate script from commands
52 | script := generateScriptPosix(step.Commands)
53 |
54 | // set the entrypoint for the step
55 | step.Entrypoint = []string{"/bin/sh", "-c"}
56 |
57 | // set the commands for the step
58 | step.Commands = []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"}
59 |
60 | // set the environment variables for the step
61 | step.Environment["VELA_BUILD_SCRIPT"] = script
62 | step.Environment["HOME"] = home
63 | // nolint: goconst // ignore making this a constant for now
64 | step.Environment["SHELL"] = "/bin/sh"
65 | }
66 |
67 | return s, nil
68 | }
69 |
70 | // generateScriptPosix is a helper function that generates a build script
71 | // for a linux container using the given commands.
72 | func generateScriptPosix(commands []string) string {
73 | var buf bytes.Buffer
74 |
75 | // iterate through each command provided
76 | for _, command := range commands {
77 | // safely escape entire command
78 | escaped := fmt.Sprintf("%q", command)
79 |
80 | // safely escape trace character
81 | escaped = strings.Replace(escaped, "$", `\$`, -1)
82 |
83 | // write escaped lines to buffer
84 | buf.WriteString(fmt.Sprintf(
85 | traceScript,
86 | escaped,
87 | command,
88 | ))
89 | }
90 |
91 | // create build script with netrc and buffer information
92 | script := fmt.Sprintf(
93 | setupScript,
94 | buf.String(),
95 | )
96 |
97 | return base64.StdEncoding.EncodeToString([]byte(script))
98 | }
99 |
100 | // setupScript is a helper script this is added to the build to ensure
101 | // a minimum set of environment variables are set correctly.
102 | const setupScript = `
103 | cat < $HOME/.netrc
104 | machine $VELA_NETRC_MACHINE
105 | login $VELA_NETRC_USERNAME
106 | password $VELA_NETRC_PASSWORD
107 | EOF
108 | chmod 0600 $HOME/.netrc
109 | unset VELA_NETRC_MACHINE
110 | unset VELA_NETRC_USERNAME
111 | unset VELA_NETRC_PASSWORD
112 | unset VELA_BUILD_SCRIPT
113 | %s
114 | `
115 |
116 | // traceScript is a helper script that is added to the build script
117 | // to trace a command.
118 | const traceScript = `
119 | echo $ %s
120 | %s
121 | `
122 |
--------------------------------------------------------------------------------
/compiler/native/script_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "flag"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/go-vela/types/yaml"
13 |
14 | "github.com/urfave/cli/v2"
15 | )
16 |
17 | func TestNative_ScriptStages(t *testing.T) {
18 | // setup types
19 | set := flag.NewFlagSet("test", 0)
20 | c := cli.NewContext(nil, set, nil)
21 |
22 | baseEnv := environment(nil, nil, nil, nil)
23 |
24 | s := yaml.StageSlice{
25 | &yaml.Stage{
26 | Name: "install",
27 | Steps: yaml.StepSlice{
28 | &yaml.Step{
29 | Commands: []string{"./gradlew downloadDependencies"},
30 | Environment: baseEnv,
31 | Image: "openjdk:latest",
32 | Name: "install",
33 | Pull: "always",
34 | },
35 | },
36 | },
37 | &yaml.Stage{
38 | Name: "test",
39 | Needs: []string{"install"},
40 | Steps: yaml.StepSlice{
41 | &yaml.Step{
42 | Commands: []string{"./gradlew check"},
43 | Environment: baseEnv,
44 | Image: "openjdk:latest",
45 | Name: "test",
46 | Pull: "always",
47 | },
48 | },
49 | },
50 | }
51 |
52 | baseEnv["HOME"] = "/root"
53 | baseEnv["SHELL"] = "/bin/sh"
54 |
55 | installEnv := baseEnv
56 | installEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew downloadDependencies"})
57 | testEnv := baseEnv
58 | testEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew check"})
59 |
60 | want := yaml.StageSlice{
61 | &yaml.Stage{
62 | Name: "install",
63 | Steps: yaml.StepSlice{
64 | &yaml.Step{
65 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
66 | Entrypoint: []string{"/bin/sh", "-c"},
67 | Environment: installEnv,
68 | Image: "openjdk:latest",
69 | Name: "install",
70 | Pull: "always",
71 | },
72 | },
73 | },
74 | &yaml.Stage{
75 | Name: "test",
76 | Needs: []string{"install"},
77 | Steps: yaml.StepSlice{
78 | &yaml.Step{
79 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
80 | Entrypoint: []string{"/bin/sh", "-c"},
81 | Environment: testEnv,
82 | Image: "openjdk:latest",
83 | Name: "test",
84 | Pull: "always",
85 | },
86 | },
87 | },
88 | }
89 |
90 | // run test
91 | compiler, err := New(c)
92 | if err != nil {
93 | t.Errorf("Creating compiler returned err: %v", err)
94 | }
95 |
96 | got, err := compiler.ScriptStages(s)
97 | if err != nil {
98 | t.Errorf("ScriptStages returned err: %v", err)
99 | }
100 |
101 | if !reflect.DeepEqual(got, want) {
102 | t.Errorf("ScriptStages is %v, want %v", got, want)
103 | }
104 | }
105 |
106 | func TestNative_ScriptSteps(t *testing.T) {
107 | // setup types
108 | set := flag.NewFlagSet("test", 0)
109 | c := cli.NewContext(nil, set, nil)
110 |
111 | baseEnv := environment(nil, nil, nil, nil)
112 | baseEnv["HOME"] = "/root"
113 | baseEnv["SHELL"] = "/bin/sh"
114 |
115 | installEnv := baseEnv
116 | installEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew downloadDependencies"})
117 | testEnv := baseEnv
118 | testEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew check"})
119 |
120 | type args struct {
121 | s yaml.StepSlice
122 | }
123 | tests := []struct {
124 | name string
125 | args args
126 | want yaml.StepSlice
127 | wantErr bool
128 | }{
129 | {"no user defined", args{s: yaml.StepSlice{
130 | &yaml.Step{
131 | Commands: []string{"./gradlew downloadDependencies"},
132 | Environment: baseEnv,
133 | Image: "openjdk:latest",
134 | Name: "install",
135 | Pull: "always",
136 | },
137 | &yaml.Step{
138 | Commands: []string{"./gradlew check"},
139 | Environment: baseEnv,
140 | Image: "openjdk:latest",
141 | Name: "test",
142 | Pull: "always",
143 | },
144 | }}, yaml.StepSlice{
145 | &yaml.Step{
146 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
147 | Entrypoint: []string{"/bin/sh", "-c"},
148 | Environment: installEnv,
149 | Image: "openjdk:latest",
150 | Name: "install",
151 | Pull: "always",
152 | },
153 | &yaml.Step{
154 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
155 | Entrypoint: []string{"/bin/sh", "-c"},
156 | Environment: testEnv,
157 | Image: "openjdk:latest",
158 | Name: "test",
159 | Pull: "always",
160 | },
161 | }, false},
162 | {"root user defined", args{s: yaml.StepSlice{
163 | &yaml.Step{
164 | Commands: []string{"./gradlew downloadDependencies"},
165 | Environment: baseEnv,
166 | Image: "openjdk:latest",
167 | Name: "install",
168 | User: "root",
169 | Pull: "always",
170 | },
171 | &yaml.Step{
172 | Commands: []string{"./gradlew check"},
173 | Environment: baseEnv,
174 | Image: "openjdk:latest",
175 | Name: "test",
176 | User: "root",
177 | Pull: "always",
178 | },
179 | }}, yaml.StepSlice{
180 | &yaml.Step{
181 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
182 | Entrypoint: []string{"/bin/sh", "-c"},
183 | Environment: installEnv,
184 | Image: "openjdk:latest",
185 | Name: "install",
186 | User: "root",
187 | Pull: "always",
188 | },
189 | &yaml.Step{
190 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
191 | Entrypoint: []string{"/bin/sh", "-c"},
192 | Environment: testEnv,
193 | Image: "openjdk:latest",
194 | Name: "test",
195 | User: "root",
196 | Pull: "always",
197 | },
198 | }, false},
199 | {"foo user defined", args{s: yaml.StepSlice{
200 | &yaml.Step{
201 | Commands: []string{"./gradlew downloadDependencies"},
202 | Environment: baseEnv,
203 | Image: "openjdk:latest",
204 | Name: "install",
205 | User: "foo",
206 | Pull: "always",
207 | },
208 | &yaml.Step{
209 | Commands: []string{"./gradlew check"},
210 | Environment: baseEnv,
211 | Image: "openjdk:latest",
212 | Name: "test",
213 | User: "foo",
214 | Pull: "always",
215 | },
216 | }}, yaml.StepSlice{
217 | &yaml.Step{
218 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
219 | Entrypoint: []string{"/bin/sh", "-c"},
220 | Environment: installEnv,
221 | Image: "openjdk:latest",
222 | Name: "install",
223 | User: "foo",
224 | Pull: "always",
225 | },
226 | &yaml.Step{
227 | Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"},
228 | Entrypoint: []string{"/bin/sh", "-c"},
229 | Environment: testEnv,
230 | Image: "openjdk:latest",
231 | Name: "test",
232 | User: "foo",
233 | Pull: "always",
234 | },
235 | }, false},
236 | }
237 | for _, tt := range tests {
238 | t.Run(tt.name, func(t *testing.T) {
239 | compiler, err := New(c)
240 | if err != nil {
241 | t.Errorf("Creating compiler returned err: %v", err)
242 | }
243 | got, err := compiler.ScriptSteps(tt.args.s)
244 | if (err != nil) != tt.wantErr {
245 | t.Errorf("ScriptSteps() error = %v, wantErr %v", err, tt.wantErr)
246 | return
247 | }
248 | if !reflect.DeepEqual(got, tt.want) {
249 | t.Errorf("ScriptSteps() got = %v, want %v", got, tt.want)
250 | }
251 | })
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/compiler/native/substitute.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "fmt"
9 | "strings"
10 |
11 | "github.com/buildkite/yaml"
12 |
13 | "github.com/drone/envsubst"
14 |
15 | types "github.com/go-vela/types/yaml"
16 | )
17 |
18 | // SubstituteStages replaces every declared environment
19 | // variable with it's corresponding value for each step
20 | // in every stage in a yaml configuration.
21 | func (c *client) SubstituteStages(s types.StageSlice) (types.StageSlice, error) {
22 | // iterate through all stages
23 | for _, stage := range s {
24 | // inject the scripts into the steps for the stage
25 | steps, err := c.SubstituteSteps(stage.Steps)
26 | if err != nil {
27 | return nil, err
28 | }
29 |
30 | stage.Steps = steps
31 | }
32 |
33 | return s, nil
34 | }
35 |
36 | // SubstituteSteps replaces every declared environment
37 | // variable with it's corresponding value for each step
38 | // in a yaml configuration.
39 | func (c *client) SubstituteSteps(s types.StepSlice) (types.StepSlice, error) {
40 | // iterate through all steps
41 | for _, step := range s {
42 | // marshal step configuration
43 | body, err := yaml.Marshal(step)
44 | if err != nil {
45 | return nil, fmt.Errorf("unable to marshal configuration: %v", err)
46 | }
47 |
48 | // create substitute function
49 | subFunc := func(name string) string {
50 | // check for the environment variable
51 | env, ok := step.Environment[name]
52 | if !ok {
53 | // return the original declaration if
54 | // the environment variable isn't found
55 | return fmt.Sprintf("${%s}", name)
56 | }
57 |
58 | // check for a new line
59 | if strings.Contains(env, "\n") {
60 | // escape the environment variable
61 | env = fmt.Sprintf("%q", env)
62 | }
63 |
64 | return env
65 | }
66 |
67 | // substitute the environment variables
68 | subStep, err := envsubst.Eval(string(body), subFunc)
69 | if err != nil {
70 | return nil, fmt.Errorf("unable to substitute environment variables: %v", err)
71 | }
72 |
73 | // unmarshal step configuration
74 | err = yaml.Unmarshal([]byte(subStep), step)
75 | if err != nil {
76 | return nil, fmt.Errorf("unable to unmarshal configuration: %v", err)
77 | }
78 | }
79 |
80 | return s, nil
81 | }
82 |
--------------------------------------------------------------------------------
/compiler/native/substitute_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "flag"
9 | "reflect"
10 | "testing"
11 |
12 | "github.com/go-vela/types/yaml"
13 |
14 | "github.com/urfave/cli/v2"
15 | )
16 |
17 | func TestNative_SubstituteStages(t *testing.T) {
18 | // setup types
19 | set := flag.NewFlagSet("test", 0)
20 | c := cli.NewContext(nil, set, nil)
21 |
22 | s := yaml.StageSlice{
23 | {
24 | Name: "simple",
25 | Steps: yaml.StepSlice{
26 | {
27 | Commands: []string{"echo ${FOO}", "echo $${BAR}"},
28 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
29 | Image: "alpine:latest",
30 | Name: "simple",
31 | Pull: "always",
32 | },
33 | },
34 | },
35 | {
36 | Name: "advanced",
37 | Steps: yaml.StepSlice{
38 | {
39 | Commands: []string{"echo ${COMPLEX}"},
40 | Environment: map[string]string{"COMPLEX": "{\"hello\":\n \"world\"}"},
41 | Image: "alpine:latest",
42 | Name: "advanced",
43 | Pull: "always",
44 | },
45 | },
46 | },
47 | {
48 | Name: "not_found",
49 | Steps: yaml.StepSlice{
50 | {
51 | Commands: []string{"echo $NOT_FOUND", "echo ${NOT_FOUND}", "echo $${NOT_FOUND}"},
52 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
53 | Image: "alpine:latest",
54 | Name: "not_found",
55 | Pull: "always",
56 | },
57 | },
58 | },
59 | }
60 |
61 | want := yaml.StageSlice{
62 | {
63 | Name: "simple",
64 | Steps: yaml.StepSlice{
65 | {
66 | Commands: []string{"echo baz", "echo ${BAR}"},
67 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
68 | Image: "alpine:latest",
69 | Name: "simple",
70 | Pull: "always",
71 | },
72 | },
73 | },
74 | {
75 | Name: "advanced",
76 | Steps: yaml.StepSlice{
77 | {
78 | Commands: []string{"echo \"{\\\"hello\\\":\\n \\\"world\\\"}\""},
79 | Environment: map[string]string{"COMPLEX": "{\"hello\":\n \"world\"}"},
80 | Image: "alpine:latest",
81 | Name: "advanced",
82 | Pull: "always",
83 | },
84 | },
85 | },
86 | {
87 | Name: "not_found",
88 | Steps: yaml.StepSlice{
89 | {
90 | Commands: []string{"echo $NOT_FOUND", "echo ${NOT_FOUND}", "echo ${NOT_FOUND}"},
91 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
92 | Image: "alpine:latest",
93 | Name: "not_found",
94 | Pull: "always",
95 | },
96 | },
97 | },
98 | }
99 |
100 | // run test
101 | compiler, err := New(c)
102 | if err != nil {
103 | t.Errorf("Creating compiler returned err: %v", err)
104 | }
105 |
106 | got, err := compiler.SubstituteStages(s)
107 | if err != nil {
108 | t.Errorf("SubstituteStages returned err: %v", err)
109 | }
110 |
111 | if !reflect.DeepEqual(got, want) {
112 | t.Errorf("SubstituteStages is %v, want %v", got, want)
113 | }
114 | }
115 |
116 | func TestNative_SubstituteSteps(t *testing.T) {
117 | // setup types
118 | set := flag.NewFlagSet("test", 0)
119 | c := cli.NewContext(nil, set, nil)
120 |
121 | p := yaml.StepSlice{
122 | {
123 | Commands: []string{"echo ${FOO}", "echo $${BAR}"},
124 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
125 | Image: "alpine:latest",
126 | Name: "simple",
127 | Pull: "always",
128 | },
129 | {
130 | Commands: []string{"echo ${COMPLEX}"},
131 | Environment: map[string]string{"COMPLEX": "{\"hello\":\n \"world\"}"},
132 | Image: "alpine:latest",
133 | Name: "advanced",
134 | Pull: "always",
135 | },
136 | {
137 | Commands: []string{"echo $NOT_FOUND", "echo ${NOT_FOUND}", "echo $${NOT_FOUND}"},
138 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
139 | Image: "alpine:latest",
140 | Name: "not_found",
141 | Pull: "always",
142 | },
143 | }
144 |
145 | want := yaml.StepSlice{
146 | {
147 | Commands: []string{"echo baz", "echo ${BAR}"},
148 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
149 | Image: "alpine:latest",
150 | Name: "simple",
151 | Pull: "always",
152 | },
153 | {
154 | Commands: []string{"echo \"{\\\"hello\\\":\\n \\\"world\\\"}\""},
155 | Environment: map[string]string{"COMPLEX": "{\"hello\":\n \"world\"}"},
156 | Image: "alpine:latest",
157 | Name: "advanced",
158 | Pull: "always",
159 | },
160 | {
161 | Commands: []string{"echo $NOT_FOUND", "echo ${NOT_FOUND}", "echo ${NOT_FOUND}"},
162 | Environment: map[string]string{"FOO": "baz", "BAR": "baz"},
163 | Image: "alpine:latest",
164 | Name: "not_found",
165 | Pull: "always",
166 | },
167 | }
168 |
169 | // run test
170 | compiler, err := New(c)
171 | if err != nil {
172 | t.Errorf("Creating compiler returned err: %v", err)
173 | }
174 |
175 | got, err := compiler.SubstituteSteps(p)
176 | if err != nil {
177 | t.Errorf("SubstituteSteps returned err: %v", err)
178 | }
179 |
180 | if !reflect.DeepEqual(got, want) {
181 | t.Errorf("SubstituteSteps is %v, want %v", got, want)
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/compiler/native/testdata/clone_false.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 | metadata:
4 | clone: false
5 |
6 | steps:
7 | - name: foo
8 | parameters:
9 | registry: foo
10 | image: alpine
11 | pull: true
12 |
--------------------------------------------------------------------------------
/compiler/native/testdata/clone_replace.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 | metadata:
4 | clone: false
5 |
6 | steps:
7 | - name: clone
8 | image: target/vela-git:v0.4.0
9 | parameters:
10 | depth: 5
11 | pull: always
12 |
13 | - name: foo
14 | parameters:
15 | registry: foo
16 | image: alpine
17 | pull: true
18 |
--------------------------------------------------------------------------------
/compiler/native/testdata/clone_true.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 | metadata:
4 | clone: true
5 |
6 | steps:
7 | - name: foo
8 | parameters:
9 | registry: foo
10 | image: alpine
11 | pull: true
12 |
--------------------------------------------------------------------------------
/compiler/native/testdata/invalid.yml:
--------------------------------------------------------------------------------
1 | ---
2 | !@#$%^&*()
--------------------------------------------------------------------------------
/compiler/native/testdata/invalid_type.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | metadata:
4 | os: linux
5 |
6 | templates:
7 | - name: gradle
8 | source: github.example.com/foo/bar/template.yml
9 | type: github
10 | - name: gradle
11 | source: github.example.com/foo/bar/template.yml
12 | type: bla
13 |
14 | steps:
15 | # would execute the following steps:
16 | # 1. sample_get_dependencies
17 | # 2. sample_test
18 | # 3. sample_build
19 | - name: sample
20 | template:
21 | name: gradle
22 | vars:
23 | image: openjdk:latest
24 | environment: "{ GRADLE_USER_HOME: .gradle, GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false }"
25 | pull_policy: "pull: true"
26 |
27 | - name: docker
28 | image: plugins/docker:18.09
29 | parameters:
30 | registry: index.docker.io
31 | repo: github/octocat
32 | tags:
33 | - latest
34 | - dev
35 | pull: true
36 | secrets:
37 | - source: docker_username
38 | target: registry_username
39 | - source: docker_password
40 | target: registry_password
41 |
42 | secrets:
43 | # Repo secrets
44 | - name: docker_username
45 | key: org/repo/docker/username
46 | engine: native
47 | type: repo
48 |
49 | - name: docker_password
50 | key: org/repo/docker/password
51 | engine: vault
52 | type: repo
53 |
--------------------------------------------------------------------------------
/compiler/native/testdata/metadata.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 |
4 | metadata:
5 | template: false
6 |
--------------------------------------------------------------------------------
/compiler/native/testdata/parameters.yml:
--------------------------------------------------------------------------------
1 | ---
2 | steps:
3 | - name: docker
4 | image: plugins/docker:18.09
5 | parameters:
6 | registry: index.docker.io
7 | repo: github/octocat
8 | tags:
9 | - latest
10 | - dev
11 | pull: true
12 | secrets: [ docker_username, docker_password ]
13 |
--------------------------------------------------------------------------------
/compiler/native/testdata/pipeline_type.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | image = "alpine"
3 |
4 | return {
5 | 'version': '1',
6 | 'steps': [
7 | {
8 | "name": "foo",
9 | "image": image,
10 | "parameters": {
11 | "registry": "foo"
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/compiler/native/testdata/pipeline_type_default.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 |
4 | steps:
5 | - name: foo
6 | parameters:
7 | registry: foo
8 | image: alpine
9 |
--------------------------------------------------------------------------------
/compiler/native/testdata/pipeline_type_go.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | {{$image := "alpine"}}
4 |
5 | steps:
6 | - name: foo
7 | parameters:
8 | registry: foo
9 | image: {{ $image }}
10 |
--------------------------------------------------------------------------------
/compiler/native/testdata/secrets.yml:
--------------------------------------------------------------------------------
1 | ---
2 | secrets:
3 | # Repo secrets
4 | - name: docker_username
5 | key: org/repo/docker/username
6 | engine: native
7 | type: repo
8 |
9 | - name: docker_password
10 | key: org/repo/docker/password
11 | engine: vault
12 | type: repo
13 |
14 | # Org secrets
15 | - name: docker_username
16 | key: org/docker/username
17 | engine: native
18 | type: org
19 |
20 | - name: docker_password
21 | key: org/docker/password
22 | engine: vault
23 | type: org
24 |
25 | # Shared secrets
26 | - name: docker_username
27 | key: org/team/docker/username
28 | engine: native
29 | type: shared
30 |
31 | - name: docker_password
32 | key: org/team/docker/password
33 | engine: vault
34 | type: shared
35 |
--------------------------------------------------------------------------------
/compiler/native/testdata/stages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | stages:
3 |
4 | install:
5 | steps:
6 | - name: install
7 | commands:
8 | - ./gradlew downloadDependencies
9 | environment:
10 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
11 | GRADLE_USER_HOME: .gradle
12 | image: openjdk:latest
13 | pull: true
14 |
15 | test:
16 | needs: [ install ]
17 | steps:
18 | - name: test
19 | commands:
20 | - ./gradlew check
21 | environment:
22 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
23 | GRADLE_USER_HOME: .gradle
24 | image: openjdk:latest
25 | pull: true
26 |
27 | build:
28 | needs: [ install ]
29 | steps:
30 | - name: build
31 | commands:
32 | - ./gradlew build
33 | environment:
34 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
35 | - GRADLE_USER_HOME=.gradle
36 | image: openjdk:latest
37 | pull: true
38 |
--------------------------------------------------------------------------------
/compiler/native/testdata/stages_pipeline.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 |
4 | metadata:
5 | template: false
6 |
7 | environment:
8 | HELLO: "Hello, Global Environment"
9 |
10 | stages:
11 |
12 | install:
13 | steps:
14 | - name: install
15 | commands:
16 | - ./gradlew downloadDependencies
17 | environment:
18 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
19 | GRADLE_USER_HOME: .gradle
20 | image: openjdk:latest
21 | pull: true
22 |
23 | test:
24 | needs: [ install ]
25 | steps:
26 | - name: test
27 | commands:
28 | - ./gradlew check
29 | environment:
30 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
31 | GRADLE_USER_HOME: .gradle
32 | image: openjdk:latest
33 | pull: true
34 |
35 | build:
36 | needs: [ install ]
37 | steps:
38 | - name: build
39 | commands:
40 | - ./gradlew build
41 | environment:
42 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
43 | - GRADLE_USER_HOME=.gradle
44 | image: openjdk:latest
45 | pull: true
46 |
47 | publish:
48 | needs: [ build ]
49 | steps:
50 | - name: publish
51 | image: plugins/docker:18.09
52 | parameters:
53 | registry: index.docker.io
54 | repo: github/octocat
55 | tags:
56 | - latest
57 | - dev
58 | pull: true
59 | secrets:
60 | - source: docker_username
61 | target: registry_username
62 | - source: docker_password
63 | target: registry_password
64 |
65 | secrets:
66 | # Repo secrets
67 | - name: docker_username
68 | key: org/repo/docker/username
69 | engine: native
70 | type: repo
71 |
72 | - name: docker_password
73 | key: org/repo/docker/password
74 | engine: vault
75 | type: repo
76 |
--------------------------------------------------------------------------------
/compiler/native/testdata/stages_pipeline_template.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 |
4 | metadata:
5 | template: false
6 |
7 | templates:
8 | - name: gradle
9 | source: github.example.com/github/octocat/template.yml
10 | type: github
11 |
12 | stages:
13 |
14 | gradle:
15 | steps:
16 | # would execute the following steps:
17 | # 1. sample_get_dependencies
18 | # 2. sample_test
19 | # 3. sample_build
20 | - name: sample
21 | template:
22 | name: gradle
23 | vars:
24 | image: openjdk:latest
25 | environment: "{ GRADLE_USER_HOME: .gradle, GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false }"
26 | pull_policy: "pull: true"
27 |
28 | publish:
29 | needs: [ gradle ]
30 | steps:
31 | - name: publish
32 | image: plugins/docker:18.09
33 | parameters:
34 | registry: index.docker.io
35 | repo: github/octocat
36 | tags:
37 | - latest
38 | - dev
39 | pull: true
40 | secrets:
41 | - source: docker_username
42 | target: registry_username
43 | - source: docker_password
44 | target: registry_password
45 |
46 | secrets:
47 | # Repo secrets
48 | - name: docker_username
49 | key: org/repo/docker/username
50 | engine: native
51 | type: repo
52 |
53 | - name: docker_password
54 | key: org/repo/docker/password
55 | engine: vault
56 | type: repo
57 |
--------------------------------------------------------------------------------
/compiler/native/testdata/steps.yml:
--------------------------------------------------------------------------------
1 | ---
2 | steps:
3 | - name: install
4 | commands:
5 | - ./gradlew downloadDependencies
6 | environment:
7 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
8 | GRADLE_USER_HOME: .gradle
9 | image: openjdk:latest
10 | pull: true
11 |
12 | - name: test
13 | commands:
14 | - ./gradlew check
15 | environment:
16 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
17 | GRADLE_USER_HOME: .gradle
18 | image: openjdk:latest
19 | pull: true
20 |
21 | - name: build
22 | commands:
23 | - ./gradlew build
24 | environment:
25 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
26 | - GRADLE_USER_HOME=.gradle
27 | image: openjdk:latest
28 | pull: true
29 |
--------------------------------------------------------------------------------
/compiler/native/testdata/steps_and_stages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | stages:
3 |
4 | dependencies:
5 | steps:
6 | - name: install
7 | commands:
8 | - ./gradlew downloadDependencies
9 | environment:
10 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
11 | GRADLE_USER_HOME: .gradle
12 | image: openjdk:latest
13 | pull: true
14 |
15 | steps:
16 |
17 | - name: test
18 | commands:
19 | - ./gradlew check
20 | environment:
21 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
22 | GRADLE_USER_HOME: .gradle
23 | image: openjdk:latest
24 | pull: true
25 |
26 | - name: build
27 | commands:
28 | - ./gradlew build
29 | environment:
30 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
31 | - GRADLE_USER_HOME=.gradle
32 | image: openjdk:latest
33 | pull: true
34 |
--------------------------------------------------------------------------------
/compiler/native/testdata/steps_pipeline.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: "1"
3 |
4 | metadata:
5 | template: false
6 |
7 | environment:
8 | HELLO: "Hello, Global Environment"
9 |
10 | steps:
11 | - name: install
12 | commands:
13 | - ./gradlew downloadDependencies
14 | environment:
15 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
16 | GRADLE_USER_HOME: .gradle
17 | image: openjdk:latest
18 | pull: true
19 |
20 | - name: test
21 | commands:
22 | - ./gradlew check
23 | environment:
24 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
25 | - GRADLE_USER_HOME=.gradle
26 | image: openjdk:latest
27 | pull: true
28 |
29 | - name: build
30 | commands:
31 | - ./gradlew build
32 | environment:
33 | - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false
34 | - GRADLE_USER_HOME=.gradle
35 | image: openjdk:latest
36 | pull: true
37 |
38 | - name: publish
39 | image: plugins/docker:18.09
40 | parameters:
41 | registry: index.docker.io
42 | repo: github/octocat
43 | tags:
44 | - latest
45 | - dev
46 | pull: true
47 | secrets:
48 | - source: docker_username
49 | target: registry_username
50 | - source: docker_password
51 | target: registry_password
52 |
53 | secrets:
54 | # Repo secrets
55 | - name: docker_username
56 | key: org/repo/docker/username
57 | engine: native
58 | type: repo
59 |
60 | - name: docker_password
61 | key: org/repo/docker/password
62 | engine: vault
63 | type: repo
64 |
--------------------------------------------------------------------------------
/compiler/native/testdata/steps_pipeline_template.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | metadata:
4 | os: linux
5 |
6 | templates:
7 | - name: gradle
8 | source: github.example.com/foo/bar/template.yml
9 | type: github
10 |
11 | steps:
12 | # would execute the following steps:
13 | # 1. sample_get_dependencies
14 | # 2. sample_test
15 | # 3. sample_build
16 | - name: sample
17 | template:
18 | name: gradle
19 | vars:
20 | image: openjdk:latest
21 | environment: "{ GRADLE_USER_HOME: .gradle, GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false }"
22 | pull_policy: "pull: true"
23 |
24 | - name: docker
25 | image: plugins/docker:18.09
26 | parameters:
27 | registry: index.docker.io
28 | repo: github/octocat
29 | tags:
30 | - latest
31 | - dev
32 | pull: true
33 | secrets:
34 | - source: docker_username
35 | target: registry_username
36 | - source: docker_password
37 | target: registry_password
38 |
39 | secrets:
40 | # Repo secrets
41 | - name: docker_username
42 | key: org/repo/docker/username
43 | engine: native
44 | type: repo
45 |
46 | - name: docker_password
47 | key: org/repo/docker/password
48 | engine: vault
49 | type: repo
50 |
--------------------------------------------------------------------------------
/compiler/native/testdata/template-gradle.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "file",
3 | "encoding": "base64",
4 | "size": 5362,
5 | "name": "gradle.yml",
6 | "path": "gradle.yml",
7 | "content": "bWV0YWRhdGE6CiAgdGVtcGxhdGU6IHRydWUKCnN0ZXBzOgogIC0gbmFtZTogaW5zdGFsbAogICAgY29tbWFuZHM6CiAgICAgIC0gLi9ncmFkbGV3IGRvd25sb2FkRGVwZW5kZW5jaWVzCiAgICBlbnZpcm9ubWVudDoge3sgLmVudmlyb25tZW50IH19CiAgICBpbWFnZToge3sgLmltYWdlIH19CiAgICB7eyAucHVsbF9wb2xpY3kgfX0KCiAgLSBuYW1lOiB0ZXN0CiAgICBjb21tYW5kczoKICAgICAgLSAuL2dyYWRsZXcgY2hlY2sKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKICAtIG5hbWU6IGJ1aWxkCiAgICBjb21tYW5kczoKICAgICAgLSAuL2dyYWRsZXcgYnVpbGQKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKc2VjcmV0czoKICAtIG5hbWU6IGRvY2tlcl91c2VybmFtZQogICAga2V5OiBvcmcvcmVwby9mb28vYmFyCiAgICBlbmdpbmU6IG5hdGl2ZQogICAgdHlwZTogcmVwbwoKICAtIG5hbWU6IGZvb19wYXNzd29yZAogICAga2V5OiBvcmcvcmVwby9mb28vcGFzc3dvcmQKICAgIGVuZ2luZTogdmF1bHQKICAgIHR5cGU6IHJlcG8KCiAgLSBuYW1lOiB2YXVsdF90b2tlbgoKICAtIG9yaWdpbjoKICAgICAgbmFtZTogcHJpdmF0ZSB2YXVsdAogICAgICBpbWFnZTogdGFyZ2V0L3NlY3JldC12YXVsdDpsYXRlc3QKICAgICAgcHVsbDogYWx3YXlzCiAgICAgIHNlY3JldHM6IFsgdmF1bHRfdG9rZW4gXQogICAgICBwYXJhbWV0ZXJzOgogICAgICAgIGFkZHI6IHZhdWx0LmV4YW1wbGUuY29tCiAgICAgICAgYXV0aF9tZXRob2Q6IHRva2VuCiAgICAgICAgdXNlcm5hbWU6IG9jdG9jYXQKICAgICAgICBpdGVtczoKICAgICAgICAgIC0gc291cmNlOiBzZWNyZXQvZG9ja2VyCiAgICAgICAgICAgIHBhdGg6IGRvY2tlcgoKe3sgaWYgLnNlY3JldCB9fQoKICAtIG5hbWU6IGJhcl9wYXNzd29yZAogICAga2V5OiBvcmcvcmVwby9iYXIvcGFzc3dvcmQKICAgIGVuZ2luZTogdmF1bHQKICAgIHR5cGU6IHJlcG8KCnt7IGVuZCB9fQoKc2VydmljZXM6CiAgIC0gbmFtZTogcG9zdGdyZXMKICAgICBpbWFnZTogcG9zdGdyZXM6MTIKCiB7eyBpZiAuc2VydmljZSB9fQoKICAgLSBuYW1lOiByZWRpcwogICAgIGtleTogcmVkaXM6NgoKIHt7IGVuZCB9fQo=",
8 | "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
9 | "url": "https://api.github.com/repos/octokit/octokit.rb/contents/gradle.yml",
10 | "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
11 | "html_url": "https://github.com/octokit/octokit.rb/blob/master/gradle.yml",
12 | "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/gradle.yml",
13 | "_links": {
14 | "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
15 | "self": "https://api.github.com/repos/octokit/octokit.rb/contents/gradle.yml",
16 | "html": "https://github.com/octokit/octokit.rb/blob/master/gradle.yml"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/compiler/native/testdata/template-maven.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "file",
3 | "encoding": "base64",
4 | "size": 5362,
5 | "name": "maven.yml",
6 | "path": "maven.yml",
7 | "content": "bWV0YWRhdGE6CiAgdGVtcGxhdGU6IHRydWUKCnN0ZXBzOgogIC0gbmFtZTogaW5zdGFsbAogICAgY29tbWFuZHM6CiAgICAgIC0gbXZuIGRvd25sb2FkRGVwZW5kZW5jaWVzCiAgICBlbnZpcm9ubWVudDoge3sgLmVudmlyb25tZW50IH19CiAgICBpbWFnZToge3sgLmltYWdlIH19CiAgICB7eyAucHVsbF9wb2xpY3kgfX0KCiAgLSBuYW1lOiB0ZXN0CiAgICBjb21tYW5kczoKICAgICAgLSBtdm4gY2hlY2sKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKICAtIG5hbWU6IGJ1aWxkCiAgICBjb21tYW5kczoKICAgICAgLSBtdm4gYnVpbGQKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKc2VjcmV0czoKICAtIG5hbWU6IGRvY2tlcl91c2VybmFtZQogICAga2V5OiBvcmcvcmVwby9mb28vYmFyCiAgICBlbmdpbmU6IG5hdGl2ZQogICAgdHlwZTogcmVwbwoKICAtIG5hbWU6IGZvb19wYXNzd29yZAogICAga2V5OiBvcmcvcmVwby9mb28vcGFzc3dvcmQKICAgIGVuZ2luZTogdmF1bHQKICAgIHR5cGU6IHJlcG8KCiAgLSBuYW1lOiB2YXVsdF90b2tlbgoKICAtIG9yaWdpbjoKICAgICAgbmFtZTogcHJpdmF0ZSB2YXVsdAogICAgICBpbWFnZTogdGFyZ2V0L3NlY3JldC12YXVsdDpsYXRlc3QKICAgICAgcHVsbDogYWx3YXlzCiAgICAgIHNlY3JldHM6IFsgdmF1bHRfdG9rZW4gXQogICAgICBwYXJhbWV0ZXJzOgogICAgICAgIGFkZHI6IHZhdWx0LmV4YW1wbGUuY29tCiAgICAgICAgYXV0aF9tZXRob2Q6IHRva2VuCiAgICAgICAgdXNlcm5hbWU6IG9jdG9jYXQKICAgICAgICBpdGVtczoKICAgICAgICAgIC0gc291cmNlOiBzZWNyZXQvZG9ja2VyCiAgICAgICAgICAgIHBhdGg6IGRvY2tlcgoKe3sgaWYgLnNlY3JldCB9fQoKICAtIG5hbWU6IGJhcl9wYXNzd29yZAogICAga2V5OiBvcmcvcmVwby9iYXIvcGFzc3dvcmQKICAgIGVuZ2luZTogdmF1bHQKICAgIHR5cGU6IHJlcG8KCnt7IGVuZCB9fQoKc2VydmljZXM6CiAgIC0gbmFtZTogcG9zdGdyZXMKICAgICBpbWFnZTogcG9zdGdyZXM6MTIKCiB7eyBpZiAuc2VydmljZSB9fQoKICAgLSBuYW1lOiByZWRpcwogICAgIGtleTogcmVkaXM6NgoKIHt7IGVuZCB9fQo=",
8 | "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
9 | "url": "https://api.github.com/repos/octokit/octokit.rb/contents/maven.yml",
10 | "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
11 | "html_url": "https://github.com/octokit/octokit.rb/blob/master/maven.yml",
12 | "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/maven.yml",
13 | "_links": {
14 | "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
15 | "self": "https://api.github.com/repos/octokit/octokit.rb/contents/maven.yml",
16 | "html": "https://github.com/octokit/octokit.rb/blob/master/maven.yml"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/compiler/native/testdata/template-starlark.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "file",
3 | "encoding": "base64",
4 | "size": 5362,
5 | "name": "template.star",
6 | "path": "template.star",
7 | "content": "ZGVmIG1haW4oY3R4KToKICByZXR1cm4gewogICAgJ3ZlcnNpb24nOiAnMScsCiAgICAnZW52aXJvbm1lbnQnOiB7CiAgICAgICdzdGFyJzogJ3Rlc3QzJywKICAgICAgJ2Jhcic6ICd0ZXN0NCcsCiAgICB9LAogICAgJ3N0ZXBzJzogWwogICAgICB7CiAgICAgICAgJ25hbWUnOiAnYnVpbGQnLAogICAgICAgICdpbWFnZSc6ICdnb2xhbmc6bGF0ZXN0JywKICAgICAgICAnY29tbWFuZHMnOiBbCiAgICAgICAgICAnZ28gYnVpbGQnLAogICAgICAgICAgJ2dvIHRlc3QnLAogICAgICAgIF0KICAgICAgfSwKICAgIF0sCn0K\n",
8 | "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
9 | "url": "https://api.github.com/repos/octokit/octokit.rb/contents/template.star",
10 | "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
11 | "html_url": "https://github.com/octokit/octokit.rb/blob/master/template.star",
12 | "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/template.star",
13 | "_links": {
14 | "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
15 | "self": "https://api.github.com/repos/octokit/octokit.rb/contents/template.star",
16 | "html": "https://github.com/octokit/octokit.rb/blob/master/template.star"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/compiler/native/testdata/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "file",
3 | "encoding": "base64",
4 | "size": 5362,
5 | "name": "template.yml",
6 | "path": "template.yml",
7 | "content": "ZW52aXJvbm1lbnQ6CiAgc3RhcjogInRlc3QzIgogIGJhcjogInRlc3Q0IgoKbWV0YWRhdGE6CiAgdGVtcGxhdGU6IHRydWUKCnN0ZXBzOgogIC0gbmFtZTogaW5zdGFsbAogICAgY29tbWFuZHM6CiAgICAgIC0gLi9ncmFkbGV3IGRvd25sb2FkRGVwZW5kZW5jaWVzCiAgICBlbnZpcm9ubWVudDoge3sgLmVudmlyb25tZW50IH19CiAgICBpbWFnZToge3sgLmltYWdlIH19CiAgICB7eyAucHVsbF9wb2xpY3kgfX0KCiAgLSBuYW1lOiB0ZXN0CiAgICBjb21tYW5kczoKICAgICAgLSAuL2dyYWRsZXcgY2hlY2sKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKICAtIG5hbWU6IGJ1aWxkCiAgICBjb21tYW5kczoKICAgICAgLSAuL2dyYWRsZXcgYnVpbGQKICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGltYWdlOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQoKc2VjcmV0czoKICAtIG5hbWU6IGRvY2tlcl91c2VybmFtZQogICAga2V5OiBvcmcvcmVwby9mb28vYmFyCiAgICBlbmdpbmU6IG5hdGl2ZQogICAgdHlwZTogcmVwbwoKICAtIG5hbWU6IGZvb19wYXNzd29yZAogICAga2V5OiBvcmcvcmVwby9mb28vcGFzc3dvcmQKICAgIGVuZ2luZTogdmF1bHQKICAgIHR5cGU6IHJlcG8KCnt7IGlmIC5zZWNyZXQgfX0KCiAgLSBuYW1lOiBiYXJfcGFzc3dvcmQKICAgIGtleTogb3JnL3JlcG8vYmFyL3Bhc3N3b3JkCiAgICBlbmdpbmU6IHZhdWx0CiAgICB0eXBlOiByZXBvCgp7eyBlbmQgfX0KCnNlcnZpY2VzOgogICAtIG5hbWU6IHBvc3RncmVzCiAgICAgaW1hZ2U6IHBvc3RncmVzOjEyCgoge3sgaWYgLnNlcnZpY2UgfX0KCiAgIC0gbmFtZTogcmVkaXMKICAgICBrZXk6IHJlZGlzOjYKCiB7eyBlbmQgfX0K",
8 | "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
9 | "url": "https://api.github.com/repos/octokit/octokit.rb/contents/template.yml",
10 | "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
11 | "html_url": "https://github.com/octokit/octokit.rb/blob/master/template.yml",
12 | "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/template.yml",
13 | "_links": {
14 | "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
15 | "self": "https://api.github.com/repos/octokit/octokit.rb/contents/template.yml",
16 | "html": "https://github.com/octokit/octokit.rb/blob/master/template.yml"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/compiler/native/testdata/template.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | return {
3 | 'version': '1',
4 | 'steps': [
5 | {
6 | 'name': 'build',
7 | 'image': 'golang:latest',
8 | 'commands': [
9 | 'go build',
10 | 'go test',
11 | ]
12 | },
13 | ],
14 | }
--------------------------------------------------------------------------------
/compiler/native/testdata/template.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: install
6 | commands:
7 | - ./gradlew downloadDependencies
8 | environment: {{ .environment }}
9 | image: {{ .image }}
10 | {{ .pull_policy }}
11 |
12 | - name: test
13 | commands:
14 | - ./gradlew check
15 | environment: {{ .environment }}
16 | image: {{ .image }}
17 | {{ .pull_policy }}
18 |
19 | - name: build
20 | commands:
21 | - ./gradlew build
22 | environment: {{ .environment }}
23 | image: {{ .image }}
24 | {{ .pull_policy }}
25 |
--------------------------------------------------------------------------------
/compiler/native/transform.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "fmt"
9 |
10 | "github.com/go-vela/types/pipeline"
11 | "github.com/go-vela/types/yaml"
12 | )
13 |
14 | const (
15 | // default org for pipeline.
16 | localOrg = "localOrg"
17 | // default repo for pipeline.
18 | localRepo = "localRepo"
19 | // default build number for pipeline.
20 | localBuild = 1
21 | // default ID for pipeline.
22 | // format: `__`
23 | pipelineID = "%s_%s_%d"
24 | // default ID for steps in a stage in a pipeline.
25 | // format: `____`
26 | stageID = "%s_%s_%d_%s_%s"
27 | // default ID for steps in a pipeline.
28 | // format: `step____`
29 | stepID = "step_%s_%s_%d_%s"
30 | // default ID for services in a pipeline.
31 | // format: `service____`
32 | serviceID = "service_%s_%s_%d_%s"
33 | // default ID for secrets in a pipeline.
34 | // format: `secret____`
35 | //
36 | // nolint: gosec // ignore gosec keying off of secret as no credentials are hardcoded
37 | secretID = "secret_%s_%s_%d_%s"
38 | )
39 |
40 | // TransformStages converts a yaml configuration with stages into an executable pipeline.
41 | func (c *client) TransformStages(r *pipeline.RuleData, p *yaml.Build) (*pipeline.Build, error) {
42 | // capture variables for setting the unique ID fields
43 | org := c.repo.GetOrg()
44 | name := c.repo.GetName()
45 | number := c.build.GetNumber()
46 |
47 | // check if the compiler is setup for a local pipeline
48 | if c.local {
49 | // check if the org provided is empty
50 | if len(org) == 0 {
51 | // set a default for the org
52 | org = localOrg
53 | }
54 |
55 | // check if the repo provided is empty
56 | if len(name) == 0 {
57 | // set a default for the repo
58 | name = localRepo
59 | }
60 |
61 | // check if the number provided is empty
62 | if number == 0 {
63 | // set a default for the number
64 | number = localBuild
65 | }
66 | }
67 |
68 | // create new executable pipeline
69 | pipeline := &pipeline.Build{
70 | Version: p.Version,
71 | Metadata: *p.Metadata.ToPipeline(),
72 | Stages: *p.Stages.ToPipeline(),
73 | Secrets: *p.Secrets.ToPipeline(),
74 | Services: *p.Services.ToPipeline(),
75 | Worker: *p.Worker.ToPipeline(),
76 | }
77 |
78 | // set the unique ID for the executable pipeline
79 | pipeline.ID = fmt.Sprintf(pipelineID, org, name, number)
80 |
81 | // set the unique ID for each step in each stage of the executable pipeline
82 | for _, stage := range pipeline.Stages {
83 | for _, step := range stage.Steps {
84 | // create pattern for steps
85 | pattern := fmt.Sprintf(stageID, org, name, number, stage.Name, step.Name)
86 |
87 | // set id to the pattern
88 | step.ID = pattern
89 |
90 | // set the workspace directory
91 | step.Directory = step.Environment["VELA_WORKSPACE"]
92 | }
93 | }
94 |
95 | // set the unique ID for each service in the executable pipeline
96 | for _, service := range pipeline.Services {
97 | // create pattern for services
98 | pattern := fmt.Sprintf(serviceID, org, name, number, service.Name)
99 |
100 | // set id to the pattern
101 | service.ID = pattern
102 | }
103 |
104 | // set the unique ID for each secret in the executable pipeline
105 | for _, secret := range pipeline.Secrets {
106 | // skip non plugin secrets
107 | if secret.Origin.Empty() {
108 | continue
109 | }
110 |
111 | // create pattern for secrets
112 | pattern := fmt.Sprintf(secretID, org, name, number, secret.Origin.Name)
113 |
114 | // set id to the pattern
115 | secret.Origin.ID = pattern
116 | }
117 |
118 | return pipeline.Purge(r), nil
119 | }
120 |
121 | // TransformSteps converts a yaml configuration with steps into an executable pipeline.
122 | func (c *client) TransformSteps(r *pipeline.RuleData, p *yaml.Build) (*pipeline.Build, error) {
123 | // capture variables for setting the unique ID fields
124 | org := c.repo.GetOrg()
125 | name := c.repo.GetName()
126 | number := c.build.GetNumber()
127 |
128 | // check if the compiler is setup for a local pipeline
129 | if c.local {
130 | // check if the org provided is empty
131 | if len(org) == 0 {
132 | // set a default for the org
133 | org = localOrg
134 | }
135 |
136 | // check if the repo provided is empty
137 | if len(name) == 0 {
138 | // set a default for the repo
139 | name = localRepo
140 | }
141 |
142 | // check if the number provided is empty
143 | if number == 0 {
144 | // set a default for the number
145 | number = localBuild
146 | }
147 | }
148 |
149 | // create new executable pipeline
150 | pipeline := &pipeline.Build{
151 | Version: p.Version,
152 | Metadata: *p.Metadata.ToPipeline(),
153 | Steps: *p.Steps.ToPipeline(),
154 | Secrets: *p.Secrets.ToPipeline(),
155 | Services: *p.Services.ToPipeline(),
156 | Worker: *p.Worker.ToPipeline(),
157 | }
158 |
159 | // set the unique ID for the executable pipeline
160 | pipeline.ID = fmt.Sprintf(pipelineID, org, name, number)
161 |
162 | // set the unique ID for each step in the executable pipeline
163 | for _, step := range pipeline.Steps {
164 | // create pattern for steps
165 | pattern := fmt.Sprintf(stepID, org, name, number, step.Name)
166 |
167 | // set id to the pattern
168 | step.ID = pattern
169 |
170 | // set the workspace directory
171 | step.Directory = step.Environment["VELA_WORKSPACE"]
172 | }
173 |
174 | // set the unique ID for each service in the executable pipeline
175 | for _, service := range pipeline.Services {
176 | // create pattern for services
177 | pattern := fmt.Sprintf(serviceID, org, name, number, service.Name)
178 |
179 | // set id to the pattern
180 | service.ID = pattern
181 | }
182 |
183 | // set the unique ID for each secret in the executable pipeline
184 | for _, secret := range pipeline.Secrets {
185 | // skip non plugin secrets
186 | if secret.Origin.Empty() {
187 | continue
188 | }
189 |
190 | // create pattern for secrets
191 | pattern := fmt.Sprintf(secretID, org, name, number, secret.Origin.Name)
192 |
193 | // set id to the pattern
194 | secret.Origin.ID = pattern
195 | }
196 |
197 | return pipeline.Purge(r), nil
198 | }
199 |
--------------------------------------------------------------------------------
/compiler/native/validate.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "fmt"
9 |
10 | "github.com/go-vela/types/yaml"
11 | )
12 |
13 | // Validate verifies the the yaml configuration is valid.
14 | func (c *client) Validate(p *yaml.Build) error {
15 | // check a version is provided
16 | if len(p.Version) == 0 {
17 | return fmt.Errorf("no version provided")
18 | }
19 |
20 | // check that stages or steps are provided
21 | if len(p.Stages) == 0 && len(p.Steps) == 0 {
22 | return fmt.Errorf("no stages or steps provided")
23 | }
24 |
25 | // check that stages and steps aren't provided
26 | if len(p.Stages) > 0 && len(p.Steps) > 0 {
27 | return fmt.Errorf("stages and steps provided")
28 | }
29 |
30 | // validate the services block provided
31 | err := validateServices(p.Services)
32 | if err != nil {
33 | return err
34 | }
35 |
36 | // validate the stages block provided
37 | err = validateStages(p.Stages)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | // validate the steps block provided
43 | err = validateSteps(p.Steps)
44 | if err != nil {
45 | return err
46 | }
47 |
48 | return nil
49 | }
50 |
51 | // validateServices is a helper function that verifies the
52 | // services block in the yaml configuration is valid.
53 | func validateServices(s yaml.ServiceSlice) error {
54 | for _, service := range s {
55 | if len(service.Name) == 0 {
56 | return fmt.Errorf("no name provided for service")
57 | }
58 |
59 | if len(service.Image) == 0 {
60 | return fmt.Errorf("no image provided for service %s", service.Name)
61 | }
62 | }
63 |
64 | return nil
65 | }
66 |
67 | // validateStages is a helper function that verifies the
68 | // stages block in the yaml configuration is valid.
69 | func validateStages(s yaml.StageSlice) error {
70 | for _, stage := range s {
71 | if len(stage.Name) == 0 {
72 | return fmt.Errorf("no name provided for stage")
73 | }
74 |
75 | // validate that a stage is not referencing itself in needs
76 | for _, need := range stage.Needs {
77 | if stage.Name == need {
78 | return fmt.Errorf("stage %s references itself in 'needs' declaration", stage.Name)
79 | }
80 | }
81 |
82 | for _, step := range stage.Steps {
83 | if len(step.Name) == 0 {
84 | return fmt.Errorf("no name provided for step for stage %s", stage.Name)
85 | }
86 |
87 | // nolint: lll // ignore simplification here
88 | if len(step.Image) == 0 && len(step.Template.Name) == 0 {
89 | return fmt.Errorf("no image or template provided for step %s for stage %s", step.Name, stage.Name)
90 | }
91 |
92 | if step.Name == "clone" || step.Name == "init" {
93 | continue
94 | }
95 |
96 | // nolint: lll // ignore simplification here
97 | if len(step.Commands) == 0 && len(step.Environment) == 0 &&
98 | len(step.Parameters) == 0 && len(step.Secrets) == 0 &&
99 | len(step.Template.Name) == 0 && !step.Detach {
100 | return fmt.Errorf("no commands, environment, parameters, secrets or template provided for step %s for stage %s", step.Name, stage.Name)
101 | }
102 | }
103 | }
104 |
105 | return nil
106 | }
107 |
108 | // validateSteps is a helper function that verifies the
109 | // steps block in the yaml configuration is valid.
110 | func validateSteps(s yaml.StepSlice) error {
111 | for _, step := range s {
112 | if len(step.Name) == 0 {
113 | return fmt.Errorf("no name provided for step")
114 | }
115 |
116 | if len(step.Image) == 0 && len(step.Template.Name) == 0 {
117 | return fmt.Errorf("no image or template provided for step %s", step.Name)
118 | }
119 |
120 | if step.Name == "clone" || step.Name == "init" {
121 | continue
122 | }
123 |
124 | // nolint: lll // ignore simplification here
125 | if len(step.Commands) == 0 && len(step.Environment) == 0 &&
126 | len(step.Parameters) == 0 && len(step.Secrets) == 0 &&
127 | len(step.Template.Name) == 0 && !step.Detach {
128 | return fmt.Errorf("no commands, environment, parameters, secrets or template provided for step %s", step.Name)
129 | }
130 | }
131 |
132 | return nil
133 | }
134 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/go-vela/compiler
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/Masterminds/sprig/v3 v3.2.2
7 | github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3
8 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
9 | github.com/drone/envsubst v1.0.3
10 | github.com/gin-gonic/gin v1.7.4
11 | github.com/go-vela/types v0.10.0
12 | github.com/google/go-cmp v0.5.6
13 | github.com/google/go-github/v39 v39.2.0
14 | github.com/google/uuid v1.1.4 // indirect
15 | github.com/goware/urlx v0.3.1
16 | github.com/hashicorp/go-cleanhttp v0.5.2
17 | github.com/hashicorp/go-retryablehttp v0.7.0
18 | github.com/huandu/xstrings v1.3.2 // indirect
19 | github.com/mitchellh/reflectwalk v1.0.1 // indirect
20 | github.com/sirupsen/logrus v1.8.1
21 | github.com/spf13/afero v1.6.0
22 | github.com/ugorji/go v1.1.11 // indirect
23 | github.com/urfave/cli/v2 v2.3.0
24 | go.starlark.net v0.0.0-20211013185944-b0039bd2cfe3
25 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
26 | k8s.io/apimachinery v0.22.2
27 | )
28 |
--------------------------------------------------------------------------------
/registry/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package registry provides the ability for Vela to
6 | // integrate with different supported Template registries.
7 | //
8 | // Usage:
9 | //
10 | // import "github.com/go-vela/compiler/registry"
11 | package registry
12 |
--------------------------------------------------------------------------------
/registry/github/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package github provides the ability for Vela to
6 | // integrate with GitHub or GitHub Enterprise as a
7 | // template registry.
8 | //
9 | // Usage:
10 | //
11 | // import "github.com/go-vela/compiler/registry/github"
12 | package github
13 |
--------------------------------------------------------------------------------
/registry/github/github.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "context"
9 | "net/url"
10 | "strings"
11 |
12 | "github.com/google/go-github/v39/github"
13 | "golang.org/x/oauth2"
14 | )
15 |
16 | const (
17 | defaultURL = "https://github.com/" // Default GitHub URL
18 | defaultAPI = "https://api.github.com/" // Default GitHub API URL
19 | )
20 |
21 | type client struct {
22 | Github *github.Client
23 | URL string
24 | API string
25 | }
26 |
27 | // New returns a Registry implementation that integrates
28 | // with GitHub or a GitHub Enterprise instance.
29 | //
30 | // nolint: revive // ignore returning unexported client
31 | func New(address, token string) (*client, error) {
32 | // create the client object
33 | c := &client{
34 | URL: defaultURL,
35 | API: defaultAPI,
36 | }
37 |
38 | // ensure we have the URL and API set
39 | if len(address) > 0 {
40 | if !strings.EqualFold(c.URL, address) {
41 | c.URL = strings.Trim(address, "/")
42 | if !strings.Contains(c.URL, "https://github.com") {
43 | c.API = c.URL + "/api/v3/"
44 | }
45 | }
46 | }
47 |
48 | // create the GitHub client
49 | gitClient := github.NewClient(nil)
50 | // ensure the proper URL is set
51 | gitClient.BaseURL, _ = url.Parse(c.API)
52 |
53 | if len(token) > 0 {
54 | // create GitHub OAuth client with user's token
55 | gitClient = c.newClientToken(token)
56 | }
57 |
58 | // overwrite the github client
59 | c.Github = gitClient
60 |
61 | return c, nil
62 | }
63 |
64 | // newClientToken is a helper function to return the GitHub oauth2 client.
65 | func (c *client) newClientToken(token string) *github.Client {
66 | // create the token object for the client
67 | ts := oauth2.StaticTokenSource(
68 | &oauth2.Token{AccessToken: token},
69 | )
70 |
71 | // create the OAuth client
72 | tc := oauth2.NewClient(context.Background(), ts)
73 | // if c.SkipVerify {
74 | // tc.Transport.(*oauth2.Transport).Base = &http.Transport{
75 | // Proxy: http.ProxyFromEnvironment,
76 | // TLSClientConfig: &tls.Config{
77 | // InsecureSkipVerify: true,
78 | // },
79 | // }
80 | // }
81 |
82 | // create the GitHub client from the OAuth client
83 | github := github.NewClient(tc)
84 |
85 | // ensure the proper URL is set
86 | github.BaseURL, _ = url.Parse(c.API)
87 | return github
88 | }
89 |
--------------------------------------------------------------------------------
/registry/github/github_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "context"
9 | "net/http"
10 | "net/http/httptest"
11 | "net/url"
12 | "reflect"
13 | "testing"
14 |
15 | "github.com/google/go-github/v39/github"
16 | "golang.org/x/oauth2"
17 | )
18 |
19 | func TestGithub_New(t *testing.T) {
20 | // setup router
21 | s := httptest.NewServer(http.NotFoundHandler())
22 | defer s.Close()
23 |
24 | gitClient := github.NewClient(nil)
25 | gitClient.BaseURL, _ = url.Parse(s.URL + "/api/v3/")
26 |
27 | want := &client{
28 | Github: gitClient,
29 | URL: s.URL,
30 | API: s.URL + "/api/v3/",
31 | }
32 |
33 | // run test
34 | got, err := New(s.URL, "")
35 |
36 | if err != nil {
37 | t.Errorf("New returned err: %v", err)
38 | }
39 |
40 | if !reflect.DeepEqual(got, want) {
41 | t.Errorf("New is %v, want %v", got, want)
42 | }
43 | }
44 |
45 | func TestGithub_NewToken(t *testing.T) {
46 | // setup router
47 | s := httptest.NewServer(http.NotFoundHandler())
48 | defer s.Close()
49 |
50 | token := "foobar"
51 | ts := oauth2.StaticTokenSource(
52 | &oauth2.Token{AccessToken: token},
53 | )
54 | tc := oauth2.NewClient(context.Background(), ts)
55 | gitClient := github.NewClient(tc)
56 | gitClient.BaseURL, _ = url.Parse(s.URL + "/api/v3/")
57 |
58 | want := &client{
59 | Github: gitClient,
60 | URL: s.URL,
61 | API: s.URL + "/api/v3/",
62 | }
63 |
64 | // run test
65 | got, err := New(s.URL, token)
66 |
67 | if err != nil {
68 | t.Errorf("New returned err: %v", err)
69 | }
70 |
71 | if !reflect.DeepEqual(got, want) {
72 | t.Errorf("New is %+v, want %+v", got.Github, want.Github)
73 | }
74 | }
75 |
76 | func TestGithub_NewURL(t *testing.T) {
77 | // setup tests
78 | tests := []struct {
79 | address string
80 | want client
81 | }{
82 | {
83 | // address matches default, so no change to default URL or API.
84 | address: "https://github.com/",
85 | want: client{
86 | URL: "https://github.com/",
87 | API: "https://api.github.com/",
88 | },
89 | },
90 | {
91 | // not the default address, but has github.com, so keep default API.
92 | address: "https://github.com",
93 | want: client{
94 | URL: "https://github.com",
95 | API: "https://api.github.com/",
96 | },
97 | },
98 | {
99 | // github-enterprise install with /
100 | address: "https://git.example.com/",
101 | want: client{
102 | URL: "https://git.example.com",
103 | API: "https://git.example.com/api/v3/",
104 | },
105 | },
106 | {
107 | // github-enterprise install without /
108 | address: "https://git.example.com",
109 | want: client{
110 | URL: "https://git.example.com",
111 | API: "https://git.example.com/api/v3/",
112 | },
113 | },
114 | }
115 |
116 | // run tests
117 | for _, test := range tests {
118 | // run test
119 | got, err := New(test.address, "foobar")
120 |
121 | if err != nil {
122 | t.Errorf("New returned err: %v", err)
123 | }
124 |
125 | if got.URL != test.want.URL {
126 | t.Errorf("New URL is %v, want %v", got.URL, test.want.URL)
127 | }
128 | if got.API != test.want.API {
129 | t.Errorf("New API is %v, want %v", got.API, test.want.API)
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/registry/github/parse.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "fmt"
9 | "strings"
10 |
11 | "github.com/go-vela/compiler/registry"
12 |
13 | "github.com/goware/urlx"
14 | )
15 |
16 | // Parse creates the registry source object from
17 | // a template path and default branch.
18 | func (c *client) Parse(path string) (*registry.Source, error) {
19 | // ref will hold the reference identifier,
20 | // eg. //@
21 | ref := ""
22 |
23 | // parse the path provided
24 | //
25 | // goware/urlx is used over net/url because it is more consistent for parsing
26 | // the template paths we use (similar to go imports)
27 | u, err := urlx.Parse(path)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | u.Path = strings.TrimPrefix(u.Path, "/")
33 |
34 | // this will handle multiple cases for the path:
35 | // * //
36 | // * ////
37 | // nolint: gomnd // ignore magic number
38 | parts := strings.SplitN(u.Path, "/", 3)
39 |
40 | // ensure org, repo and filename parts exist
41 | // nolint: gomnd // ignore magic number
42 | if len(parts) < 3 {
43 | // nolint: lll // ignore long line length due to error message
44 | return ®istry.Source{}, fmt.Errorf("invalid template source %s, must contain org/repo/path_to_template", path)
45 | }
46 |
47 | // check for reference provided in filename:
48 | // * //@
49 | // * ////@
50 | if strings.Contains(parts[2], "@") {
51 | // capture the filename and reference
52 | refParts := strings.Split(parts[2], "@")
53 | // set the filename
54 | parts[2] = refParts[0]
55 | // set the reference
56 | ref = refParts[1]
57 | }
58 |
59 | return ®istry.Source{
60 | Host: u.Host,
61 | Org: parts[0],
62 | Repo: parts[1],
63 | Name: parts[2],
64 | Ref: ref,
65 | }, nil
66 | }
67 |
--------------------------------------------------------------------------------
/registry/github/parse_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "fmt"
9 | "net/http"
10 | "net/http/httptest"
11 | "net/url"
12 | "reflect"
13 | "testing"
14 |
15 | "github.com/go-vela/compiler/registry"
16 | )
17 |
18 | func TestGithub_Parse(t *testing.T) {
19 | // setup mock server
20 | s := httptest.NewServer(http.NotFoundHandler())
21 | defer s.Close()
22 |
23 | // setup types
24 | want := ®istry.Source{
25 | Host: "github.example.com",
26 | Org: "github",
27 | Repo: "octocat",
28 | Name: "template.yml",
29 | Ref: "",
30 | }
31 |
32 | // run test
33 | c, err := New(s.URL, "")
34 | if err != nil {
35 | t.Errorf("Creating client returned err: %v", err)
36 | }
37 |
38 | path := "github.example.com/github/octocat/template.yml"
39 |
40 | got, err := c.Parse(path)
41 | if err != nil {
42 | t.Errorf("Parse returned err: %v", err)
43 | }
44 |
45 | if !reflect.DeepEqual(got, want) {
46 | t.Errorf("Parse is %v, want %v", got, want)
47 | }
48 | }
49 |
50 | func TestGithub_ParseWithBranch(t *testing.T) {
51 | // setup mock server
52 | s := httptest.NewServer(http.NotFoundHandler())
53 | defer s.Close()
54 |
55 | // setup types
56 | want := ®istry.Source{
57 | Host: "github.example.com",
58 | Org: "github",
59 | Repo: "octocat",
60 | Name: "template.yml",
61 | Ref: "dev",
62 | }
63 |
64 | // run test
65 | c, err := New(s.URL, "")
66 | if err != nil {
67 | t.Errorf("Creating client returned err: %v", err)
68 | }
69 |
70 | path := "github.example.com/github/octocat/template.yml@dev"
71 |
72 | got, err := c.Parse(path)
73 | if err != nil {
74 | t.Errorf("Parse returned err: %v", err)
75 | }
76 |
77 | if !reflect.DeepEqual(got, want) {
78 | t.Errorf("Parse is %v, want %v", got, want)
79 | }
80 | }
81 | func TestGithub_Parse_Custom(t *testing.T) {
82 | // setup mock server
83 | s := httptest.NewServer(http.NotFoundHandler())
84 | defer s.Close()
85 |
86 | // setup types
87 | want := ®istry.Source{
88 | Host: "github.example.com",
89 | Org: "github",
90 | Repo: "octocat",
91 | Name: "path/to/template.yml",
92 | Ref: "test",
93 | }
94 |
95 | // run test
96 | c, err := New(s.URL, "")
97 | if err != nil {
98 | t.Errorf("Creating client returned err: %v", err)
99 | }
100 |
101 | path := "github.example.com/github/octocat/path/to/template.yml@test"
102 |
103 | got, err := c.Parse(path)
104 | if err != nil {
105 | t.Errorf("Parse returned err: %v", err)
106 | }
107 |
108 | if !reflect.DeepEqual(got, want) {
109 | t.Errorf("Parse is %v, want %v", got, want)
110 | }
111 | }
112 |
113 | func TestGithub_Parse_Full(t *testing.T) {
114 | // setup mock server
115 | s := httptest.NewServer(http.NotFoundHandler())
116 | defer s.Close()
117 |
118 | // setup types
119 | u, err := url.Parse(s.URL)
120 | if err != nil {
121 | t.Errorf("Parsing url returned err: %v", err)
122 | }
123 |
124 | want := ®istry.Source{
125 | Host: u.Host,
126 | Org: "github",
127 | Repo: "octocat",
128 | Name: "template.yml",
129 | Ref: "test",
130 | }
131 |
132 | // run test
133 | c, err := New(s.URL, "")
134 | if err != nil {
135 | t.Errorf("Creating client returned err: %v", err)
136 | }
137 |
138 | path := fmt.Sprintf("%s/%s", s.URL, "github/octocat/template.yml@test")
139 |
140 | got, err := c.Parse(path)
141 | if err != nil {
142 | t.Errorf("Parse returned err: %v", err)
143 | }
144 |
145 | if !reflect.DeepEqual(got, want) {
146 | t.Errorf("Parse is %v, want %v", got, want)
147 | }
148 | }
149 |
150 | func TestGithub_Parse_Invalid(t *testing.T) {
151 | // setup mock server
152 | s := httptest.NewServer(http.NotFoundHandler())
153 | defer s.Close()
154 |
155 | // run test
156 | c, err := New(s.URL, "")
157 | if err != nil {
158 | t.Errorf("Creating client returned err: %v", err)
159 | }
160 |
161 | got, err := c.Parse("!@#$%^&*()")
162 | if err == nil {
163 | t.Errorf("Parse should have returned err")
164 | }
165 |
166 | if got != nil {
167 | t.Errorf("Parse is %v, want nil", got)
168 | }
169 | }
170 |
171 | func TestGithub_Parse_Hostname(t *testing.T) {
172 | // setup mock server
173 | s := httptest.NewServer(http.NotFoundHandler())
174 | defer s.Close()
175 |
176 | // setup types
177 | u, err := url.Parse(s.URL)
178 | if err != nil {
179 | t.Errorf("Parsing url returned err: %v", err)
180 | }
181 |
182 | want := ®istry.Source{
183 | Host: u.Hostname(),
184 | Org: "github",
185 | Repo: "octocat",
186 | Name: "template.yml",
187 | Ref: "",
188 | }
189 |
190 | // run test
191 | c, err := New(s.URL, "")
192 | if err != nil {
193 | t.Errorf("Creating client returned err: %v", err)
194 | }
195 |
196 | path := fmt.Sprintf("%s/%s", u.Hostname(), "github/octocat/template.yml")
197 |
198 | got, err := c.Parse(path)
199 | if err != nil {
200 | t.Errorf("Parse returned err: %v", err)
201 | }
202 |
203 | if !reflect.DeepEqual(got, want) {
204 | t.Errorf("Parse is %v, want %v", got, want)
205 | }
206 | }
207 |
208 | func TestGithub_Parse_Path(t *testing.T) {
209 | // setup mock server
210 | s := httptest.NewServer(http.NotFoundHandler())
211 | defer s.Close()
212 |
213 | // setup types
214 | u, err := url.Parse(s.URL)
215 | if err != nil {
216 | t.Errorf("Parsing url returned err: %v", err)
217 | }
218 |
219 | want := ®istry.Source{
220 | Host: u.Host,
221 | Org: "github",
222 | Repo: "octocat",
223 | Name: "path/to/template.yml",
224 | Ref: "",
225 | }
226 |
227 | // run test
228 | c, err := New(s.URL, "")
229 | if err != nil {
230 | t.Errorf("Creating client returned err: %v", err)
231 | }
232 |
233 | path := fmt.Sprintf("%s/%s", s.URL, "github/octocat/path/to/template.yml")
234 |
235 | got, err := c.Parse(path)
236 | if err != nil {
237 | t.Errorf("Parse returned err: %v", err)
238 | }
239 |
240 | if !reflect.DeepEqual(got, want) {
241 | t.Errorf("Parse is %v, want %v", got, want)
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/registry/github/template.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "context"
9 | "fmt"
10 | "net/http"
11 |
12 | "github.com/go-vela/compiler/registry"
13 |
14 | "github.com/go-vela/types/library"
15 |
16 | "github.com/google/go-github/v39/github"
17 | )
18 |
19 | // Template captures the templated pipeline configuration from the GitHub repo.
20 | func (c *client) Template(u *library.User, s *registry.Source) ([]byte, error) {
21 | // use default GitHub OAuth client we provide
22 | cli := c.Github
23 | if u != nil {
24 | // create GitHub OAuth client with user's token
25 | cli = c.newClientToken(u.GetToken())
26 | }
27 |
28 | // create the options to pass
29 | opts := &github.RepositoryContentGetOptions{}
30 |
31 | // set the reference for the options to capture the templated pipeline
32 | // configuration. if no ref is set, it will pull from the default
33 | // branch on the targeted repo, see:
34 | // https://docs.github.com/en/rest/reference/repos#get-repository-content--parameters
35 | if len(s.Ref) > 0 {
36 | opts.Ref = s.Ref
37 | }
38 |
39 | // send API call to capture the templated pipeline configuration
40 | //
41 | // nolint: lll // ignore long line length due to variable names
42 | data, _, resp, err := cli.Repositories.GetContents(context.Background(), s.Org, s.Repo, s.Name, opts)
43 | if err != nil {
44 | if resp != nil && resp.StatusCode != http.StatusNotFound {
45 | // return different error message depending on if a branch was provided
46 | if len(s.Ref) == 0 {
47 | errString := "unexpected error fetching template %s/%s/%s: %v"
48 | return nil, fmt.Errorf(errString, s.Org, s.Repo, s.Name, err)
49 | }
50 | errString := "unexpected error fetching template %s/%s/%s@%s: %v"
51 | return nil, fmt.Errorf(errString, s.Org, s.Repo, s.Name, s.Ref, err)
52 | }
53 |
54 | // return different error message depending on if a branch was provided
55 | if len(s.Ref) == 0 {
56 | return nil, fmt.Errorf("no Vela template found at %s/%s/%s", s.Org, s.Repo, s.Name)
57 | }
58 | return nil, fmt.Errorf("no Vela template found at %s/%s/%s@%s", s.Org, s.Repo, s.Name, s.Ref)
59 | }
60 |
61 | // data is not nil if template exists
62 | if data != nil {
63 | strData, err := data.GetContent()
64 | if err != nil {
65 | return nil, err
66 | }
67 |
68 | return []byte(strData), nil
69 | }
70 |
71 | // return different error message depending on if a branch was provided
72 | if len(s.Ref) == 0 {
73 | return nil, fmt.Errorf("no Vela template found at %s/%s/%s", s.Org, s.Repo, s.Name)
74 | }
75 | return nil, fmt.Errorf("no Vela template found at %s/%s/%s@%s", s.Org, s.Repo, s.Name, s.Ref)
76 | }
77 |
--------------------------------------------------------------------------------
/registry/github/template_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package github
6 |
7 | import (
8 | "io/ioutil"
9 | "net/http"
10 | "net/http/httptest"
11 | "reflect"
12 | "testing"
13 |
14 | "github.com/go-vela/compiler/registry"
15 |
16 | "github.com/go-vela/types/library"
17 |
18 | "github.com/gin-gonic/gin"
19 | )
20 |
21 | func TestGithub_Template(t *testing.T) {
22 | // setup context
23 | gin.SetMode(gin.TestMode)
24 | resp := httptest.NewRecorder()
25 | _, engine := gin.CreateTestContext(resp)
26 |
27 | // setup mock server
28 | engine.GET("/api/v3/repos/:owner/:name/contents/:path", func(c *gin.Context) {
29 | c.Header("Content-Type", "application/json")
30 | c.Status(http.StatusOK)
31 | c.File("testdata/template.json")
32 | })
33 | s := httptest.NewServer(engine)
34 | defer s.Close()
35 |
36 | // setup types
37 | str := "foo"
38 | u := &library.User{
39 | Name: &str,
40 | Token: &str,
41 | }
42 |
43 | src := ®istry.Source{
44 | Org: "github",
45 | Repo: "octocat",
46 | Name: "template.yml",
47 | }
48 |
49 | want, err := ioutil.ReadFile("testdata/template.yml")
50 | if err != nil {
51 | t.Errorf("Reading file returned err: %v", err)
52 | }
53 |
54 | // run test
55 | c, err := New(s.URL, "")
56 | if err != nil {
57 | t.Errorf("Creating client returned err: %v", err)
58 | }
59 |
60 | got, err := c.Template(u, src)
61 |
62 | if resp.Code != http.StatusOK {
63 | t.Errorf("Template returned %v, want %v", resp.Code, http.StatusOK)
64 | }
65 |
66 | if err != nil {
67 | t.Errorf("Template returned err: %v", err)
68 | }
69 |
70 | if !reflect.DeepEqual(got, want) {
71 | t.Errorf("Template is %v, want %v", got, want)
72 | }
73 | }
74 |
75 | func TestGithub_TemplateSourceRef(t *testing.T) {
76 | // setup context
77 | gin.SetMode(gin.TestMode)
78 | resp := httptest.NewRecorder()
79 | _, engine := gin.CreateTestContext(resp)
80 |
81 | // store the ref
82 | gotRef := ""
83 |
84 | // setup mock server
85 | engine.GET("/api/v3/repos/:owner/:name/contents/:path", func(c *gin.Context) {
86 | gotRef = c.Request.FormValue("ref")
87 | c.Header("Content-Type", "application/json")
88 | c.Status(http.StatusOK)
89 | c.File("testdata/template.json")
90 | })
91 | s := httptest.NewServer(engine)
92 | defer s.Close()
93 |
94 | // setup types
95 | str := "foo"
96 | u := &library.User{
97 | Name: &str,
98 | Token: &str,
99 | }
100 |
101 | src := ®istry.Source{
102 | Org: "github",
103 | Repo: "octocat",
104 | Name: "template.yml",
105 | Ref: "main",
106 | }
107 |
108 | want, err := ioutil.ReadFile("testdata/template.yml")
109 | if err != nil {
110 | t.Errorf("Reading file returned err: %v", err)
111 | }
112 |
113 | // run test
114 | c, err := New(s.URL, "")
115 | if err != nil {
116 | t.Errorf("Creating client returned err: %v", err)
117 | }
118 |
119 | got, err := c.Template(u, src)
120 |
121 | if resp.Code != http.StatusOK {
122 | t.Errorf("Template returned %v, want %v", resp.Code, http.StatusOK)
123 | }
124 |
125 | if err != nil {
126 | t.Errorf("Template returned err: %v", err)
127 | }
128 |
129 | if gotRef != src.Ref {
130 | t.Errorf("Ref returned %v, want %v", gotRef, src.Ref)
131 | }
132 |
133 | if !reflect.DeepEqual(got, want) {
134 | t.Errorf("Template is %v, want %v", got, want)
135 | }
136 | }
137 |
138 | func TestGithub_TemplateEmptySourceRef(t *testing.T) {
139 | // setup context
140 | gin.SetMode(gin.TestMode)
141 | resp := httptest.NewRecorder()
142 | _, engine := gin.CreateTestContext(resp)
143 |
144 | // store the ref
145 | gotRef := ""
146 |
147 | // setup mock server
148 | engine.GET("/api/v3/repos/:owner/:name/contents/:path", func(c *gin.Context) {
149 | gotRef = c.Request.FormValue("ref")
150 | c.Header("Content-Type", "application/json")
151 | c.Status(http.StatusOK)
152 | c.File("testdata/template.json")
153 | })
154 | s := httptest.NewServer(engine)
155 | defer s.Close()
156 |
157 | // setup types
158 | str := "foo"
159 | u := &library.User{
160 | Name: &str,
161 | Token: &str,
162 | }
163 |
164 | src := ®istry.Source{
165 | Org: "github",
166 | Repo: "octocat",
167 | Name: "template.yml",
168 | }
169 |
170 | want, err := ioutil.ReadFile("testdata/template.yml")
171 | if err != nil {
172 | t.Errorf("Reading file returned err: %v", err)
173 | }
174 |
175 | // run test
176 | c, err := New(s.URL, "")
177 | if err != nil {
178 | t.Errorf("Creating client returned err: %v", err)
179 | }
180 |
181 | got, err := c.Template(u, src)
182 |
183 | if resp.Code != http.StatusOK {
184 | t.Errorf("Template returned %v, want %v", resp.Code, http.StatusOK)
185 | }
186 |
187 | if err != nil {
188 | t.Errorf("Template returned err: %v", err)
189 | }
190 |
191 | if gotRef != "" {
192 | t.Errorf("Ref returned %v, want empty string", gotRef)
193 | }
194 |
195 | if !reflect.DeepEqual(got, want) {
196 | t.Errorf("Template is %v, want %v", got, want)
197 | }
198 | }
199 |
200 | func TestGithub_Template_BadRequest(t *testing.T) {
201 | // setup context
202 | gin.SetMode(gin.TestMode)
203 | resp := httptest.NewRecorder()
204 | _, engine := gin.CreateTestContext(resp)
205 |
206 | // setup mock server
207 | engine.GET("/api/v3/repos/foo/bar/contents/:path", func(c *gin.Context) {
208 | c.Status(http.StatusBadRequest)
209 | })
210 | s := httptest.NewServer(engine)
211 | defer s.Close()
212 |
213 | // setup types
214 | str := "foo"
215 | u := &library.User{
216 | Name: &str,
217 | Token: &str,
218 | }
219 |
220 | src := ®istry.Source{
221 | Org: "github",
222 | Repo: "octocat",
223 | Name: "template.yml",
224 | }
225 |
226 | // run test
227 | c, err := New(s.URL, "")
228 | if err != nil {
229 | t.Errorf("Creating client returned err: %v", err)
230 | }
231 |
232 | got, err := c.Template(u, src)
233 |
234 | if resp.Code != http.StatusOK {
235 | t.Errorf("Template returned %v, want %v", resp.Code, http.StatusOK)
236 | }
237 |
238 | if err == nil {
239 | t.Error("Template should have returned err")
240 | }
241 |
242 | if got != nil {
243 | t.Errorf("Template is %v, want nil", got)
244 | }
245 | }
246 |
247 | func TestGithub_Template_NotFound(t *testing.T) {
248 | // setup context
249 | gin.SetMode(gin.TestMode)
250 | resp := httptest.NewRecorder()
251 | _, engine := gin.CreateTestContext(resp)
252 |
253 | // setup mock server
254 | engine.GET("/api/v3/repos/foo/bar/contents/:path", func(c *gin.Context) {
255 | c.Status(http.StatusNotFound)
256 | })
257 | s := httptest.NewServer(engine)
258 | defer s.Close()
259 |
260 | // setup types
261 | str := "foo"
262 | u := &library.User{
263 | Name: &str,
264 | Token: &str,
265 | }
266 |
267 | src := ®istry.Source{
268 | Org: "github",
269 | Repo: "octocat",
270 | Name: "template.yml",
271 | }
272 |
273 | // run test
274 | c, err := New(s.URL, "")
275 | if err != nil {
276 | t.Errorf("Creating client returned err: %v", err)
277 | }
278 |
279 | got, err := c.Template(u, src)
280 |
281 | if resp.Code != http.StatusOK {
282 | t.Errorf("Template returned %v, want %v", resp.Code, http.StatusOK)
283 | }
284 |
285 | if err == nil {
286 | t.Error("Template should have returned err")
287 | }
288 |
289 | if got != nil {
290 | t.Errorf("Template is %v, want nil", got)
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/registry/github/testdata/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "file",
3 | "encoding": "base64",
4 | "size": 5362,
5 | "name": "template.yml",
6 | "path": "template.yml",
7 | "content": "bWV0YWRhdGE6CiAgdGVtcGxhdGU6IHRydWUKCnN0ZXBzOgogIC0gbmFtZTogZ2V0X2RlcGVuZGVuY2llcwogICAgcGx1Z2luOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQogICAgZW52aXJvbm1lbnQ6IHt7IC5lbnZpcm9ubWVudCB9fQogICAgY29tbWFuZHM6CiAgICAgIC0gLi9ncmFkbGV3IGRvd25sb2FkRGVwZW5kZW5jaWVzCgogIC0gbmFtZTogdGVzdAogICAgcGx1Z2luOiB7eyAuaW1hZ2UgfX0KICAgIHt7IC5wdWxsX3BvbGljeSB9fQogICAgZW52aXJvbm1lbnQ6IHt7IC5lbnZpcm9ubWVudCB9fQogICAgY29tbWFuZHM6CiAgICAgIC0gLi9ncmFkbGV3IGNoZWNrCgogIC0gbmFtZTogYnVpbGQKICAgIHBsdWdpbjoge3sgLmltYWdlIH19CiAgICB7eyAucHVsbF9wb2xpY3kgfX0KICAgIGVudmlyb25tZW50OiB7eyAuZW52aXJvbm1lbnQgfX0KICAgIGNvbW1hbmRzOgogICAgICAtIC4vZ3JhZGxldyBidWlsZCBkaXN0VGFyCg==",
8 | "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
9 | "url": "https://api.github.com/repos/octokit/octokit.rb/contents/template.yml",
10 | "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
11 | "html_url": "https://github.com/octokit/octokit.rb/blob/master/template.yml",
12 | "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/template.yml",
13 | "_links": {
14 | "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
15 | "self": "https://api.github.com/repos/octokit/octokit.rb/contents/template.yml",
16 | "html": "https://github.com/octokit/octokit.rb/blob/master/template.yml"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/registry/github/testdata/template.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: get_dependencies
6 | plugin: {{ .image }}
7 | {{ .pull_policy }}
8 | environment: {{ .environment }}
9 | commands:
10 | - ./gradlew downloadDependencies
11 |
12 | - name: test
13 | plugin: {{ .image }}
14 | {{ .pull_policy }}
15 | environment: {{ .environment }}
16 | commands:
17 | - ./gradlew check
18 |
19 | - name: build
20 | plugin: {{ .image }}
21 | {{ .pull_policy }}
22 | environment: {{ .environment }}
23 | commands:
24 | - ./gradlew build distTar
25 |
--------------------------------------------------------------------------------
/registry/registry.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package registry
6 |
7 | import "github.com/go-vela/types/library"
8 |
9 | // Service represents the interface for Vela integrating
10 | // with the different supported template registries.
11 | type Service interface {
12 | // Parse defines a function that creates the
13 | // registry source object from a template path.
14 | Parse(string) (*Source, error)
15 |
16 | // Template defines a function that captures the
17 | // templated pipeline configuration from a repo.
18 | Template(*library.User, *Source) ([]byte, error)
19 | }
20 |
--------------------------------------------------------------------------------
/registry/source.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package registry
6 |
7 | // Source represents a registry object
8 | // for retrieving templates.
9 | type Source struct {
10 | Host string
11 | Org string
12 | Repo string
13 | Name string
14 | Ref string
15 | }
16 |
--------------------------------------------------------------------------------
/template/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package template provides the ability for Vela to
6 | // render a templated yaml configuration into an
7 | // executable pipeline.
8 | //
9 | // Usage:
10 | //
11 | // import "github.com/go-vela/compiler/template"
12 | package template
13 |
--------------------------------------------------------------------------------
/template/native/convert.go:
--------------------------------------------------------------------------------
1 | package native
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/go-vela/types/raw"
7 | )
8 |
9 | // convertPlatformVars takes the platform injected variables
10 | // within the step environment block and modifies them to be returned
11 | // within the template.
12 | func convertPlatformVars(slice raw.StringSliceMap, name string) raw.StringSliceMap {
13 | envs := make(map[string]string)
14 | for key, value := range slice {
15 | key = strings.ToLower(key)
16 | if strings.HasPrefix(key, "vela_") {
17 | envs[strings.TrimPrefix(key, "vela_")] = value
18 | }
19 | }
20 |
21 | envs["template_name"] = name
22 |
23 | return envs
24 | }
25 |
26 | type funcHandler struct {
27 | envs raw.StringSliceMap
28 | }
29 |
30 | // returnPlatformVar returns the value of the platform
31 | // variable if it exists within the environment map.
32 | func (h funcHandler) returnPlatformVar(input string) string {
33 | input = strings.ToLower(input)
34 | input = strings.TrimPrefix(input, "vela_")
35 | // check if key exists within map
36 | if _, ok := h.envs[input]; ok {
37 | // return value if exists
38 | return h.envs[input]
39 | }
40 | // return empty string if not exists
41 | return ""
42 | }
43 |
--------------------------------------------------------------------------------
/template/native/convert_test.go:
--------------------------------------------------------------------------------
1 | package native
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 |
7 | "github.com/go-vela/types/raw"
8 | )
9 |
10 | func Test_convertPlatformVars(t *testing.T) {
11 | tests := []struct {
12 | name string
13 | slice raw.StringSliceMap
14 | templateName string
15 | want raw.StringSliceMap
16 | }{
17 | {
18 | name: "with all vela prefixed vars",
19 | slice: raw.StringSliceMap{
20 | "VELA_BUILD_AUTHOR": "octocat",
21 | "VELA_REPO_FULL_NAME": "go-vela/hello-world",
22 | "VELA_USER_ADMIN": "true",
23 | "VELA_WORKSPACE": "/vela/src/github.com/go-vela/hello-world",
24 | },
25 | templateName: "foo",
26 | want: raw.StringSliceMap{"build_author": "octocat", "repo_full_name": "go-vela/hello-world", "user_admin": "true", "workspace": "/vela/src/github.com/go-vela/hello-world", "template_name": "foo"},
27 | },
28 | {
29 | name: "with combination of vela and user vars",
30 | slice: raw.StringSliceMap{
31 | "VELA_BUILD_AUTHOR": "octocat",
32 | "VELA_REPO_FULL_NAME": "go-vela/hello-world",
33 | "FOO_VAR1": "test1",
34 | "BAR_VAR1": "test2",
35 | },
36 | templateName: "foo",
37 | want: raw.StringSliceMap{"build_author": "octocat", "repo_full_name": "go-vela/hello-world", "template_name": "foo"},
38 | },
39 | }
40 | for _, tt := range tests {
41 | t.Run(tt.name, func(t *testing.T) {
42 | if got := convertPlatformVars(tt.slice, tt.templateName); !reflect.DeepEqual(got, tt.want) {
43 | t.Errorf("convertPlatformVars() = %v, want %v", got, tt.want)
44 | }
45 | })
46 | }
47 | }
48 |
49 | func Test_funcHandler_returnPlatformVar(t *testing.T) {
50 | type fields struct {
51 | envs raw.StringSliceMap
52 | }
53 | type args struct {
54 | input string
55 | }
56 | tests := []struct {
57 | name string
58 | fields fields
59 | args args
60 | want string
61 | }{
62 | {
63 | name: "existing platform var without prefix (lowercase)",
64 | fields: fields{
65 | envs: raw.StringSliceMap{
66 | "build_author": "octocat",
67 | },
68 | },
69 | args: args{input: "build_author"},
70 | want: "octocat",
71 | },
72 | {
73 | name: "existing platform var without prefix (uppercase)",
74 | fields: fields{
75 | envs: raw.StringSliceMap{
76 | "build_author": "octocat",
77 | },
78 | },
79 | args: args{input: "BUILD_AUTHOR"},
80 | want: "octocat",
81 | },
82 | {
83 | name: "existing platform var with prefix (lowercase)",
84 | fields: fields{
85 | envs: raw.StringSliceMap{
86 | "build_author": "octocat",
87 | },
88 | },
89 | args: args{input: "vela_build_author"},
90 | want: "octocat",
91 | },
92 | {
93 | name: "existing platform var with prefix (uppercase)",
94 | fields: fields{
95 | envs: raw.StringSliceMap{
96 | "build_author": "octocat",
97 | },
98 | },
99 | args: args{input: "VELA_BUILD_AUTHOR"},
100 | want: "octocat",
101 | },
102 | {
103 | name: "non existent var",
104 | fields: fields{
105 | envs: raw.StringSliceMap{
106 | "build_author": "octocat",
107 | },
108 | },
109 | args: args{input: "foo"},
110 | want: "",
111 | },
112 | }
113 | for _, tt := range tests {
114 | t.Run(tt.name, func(t *testing.T) {
115 | h := funcHandler{
116 | envs: tt.fields.envs,
117 | }
118 | if got := h.returnPlatformVar(tt.args.input); got != tt.want {
119 | t.Errorf("returnPlatformVar() = %v, want %v", got, tt.want)
120 | }
121 | })
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/template/native/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | // Package native provides the ability for Vela to
6 | // render a templated yaml configuration into an
7 | // executable pipeline.
8 | //
9 | // Usage:
10 | //
11 | // import "github.com/go-vela/compiler/template/native"
12 | package native
13 |
--------------------------------------------------------------------------------
/template/native/render.go:
--------------------------------------------------------------------------------
1 | package native
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "text/template"
7 |
8 | "github.com/go-vela/types/raw"
9 |
10 | types "github.com/go-vela/types/yaml"
11 |
12 | "github.com/Masterminds/sprig/v3"
13 |
14 | yaml "github.com/buildkite/yaml"
15 | )
16 |
17 | // RenderStep combines the template with the step in the yaml pipeline.
18 | // nolint: lll // ignore long line length due to return args
19 | func RenderStep(tmpl string, s *types.Step) (types.StepSlice, types.SecretSlice, types.ServiceSlice, raw.StringSliceMap, error) {
20 | buffer := new(bytes.Buffer)
21 | config := new(types.Build)
22 |
23 | velaFuncs := funcHandler{envs: convertPlatformVars(s.Environment, s.Name)}
24 | templateFuncMap := map[string]interface{}{
25 | "vela": velaFuncs.returnPlatformVar,
26 | }
27 | // modify Masterminds/sprig functions
28 | // to remove OS functions
29 | //
30 | // https://masterminds.github.io/sprig/os.html
31 | sf := sprig.TxtFuncMap()
32 | delete(sf, "env")
33 | delete(sf, "expandenv")
34 |
35 | // parse the template with Masterminds/sprig functions
36 | //
37 | // https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap
38 | t, err := template.New(s.Name).Funcs(sf).Funcs(templateFuncMap).Parse(tmpl)
39 | if err != nil {
40 | // nolint: lll // ignore long line length due to return arguments
41 | return types.StepSlice{}, types.SecretSlice{}, types.ServiceSlice{}, raw.StringSliceMap{}, fmt.Errorf("unable to parse template %s: %v", s.Template.Name, err)
42 | }
43 |
44 | // apply the variables to the parsed template
45 | err = t.Execute(buffer, s.Template.Variables)
46 | if err != nil {
47 | // nolint: lll // ignore long line length due to return arguments
48 | return types.StepSlice{}, types.SecretSlice{}, types.ServiceSlice{}, raw.StringSliceMap{}, fmt.Errorf("unable to execute template %s: %v", s.Template.Name, err)
49 | }
50 |
51 | // unmarshal the template to the pipeline
52 | err = yaml.Unmarshal(buffer.Bytes(), config)
53 | if err != nil {
54 | // nolint: lll // ignore long line length due to return args
55 | return types.StepSlice{}, types.SecretSlice{}, types.ServiceSlice{}, raw.StringSliceMap{}, fmt.Errorf("unable to unmarshal yaml: %v", err)
56 | }
57 |
58 | // ensure all templated steps have template prefix
59 | for index, newStep := range config.Steps {
60 | config.Steps[index].Name = fmt.Sprintf("%s_%s", s.Name, newStep.Name)
61 | }
62 |
63 | return config.Steps, config.Secrets, config.Services, config.Environment, nil
64 | }
65 |
66 | // RenderBuild renders the templated build.
67 | func RenderBuild(b string, envs map[string]string) (*types.Build, error) {
68 | buffer := new(bytes.Buffer)
69 | config := new(types.Build)
70 |
71 | velaFuncs := funcHandler{envs: convertPlatformVars(envs, "")}
72 | templateFuncMap := map[string]interface{}{
73 | "vela": velaFuncs.returnPlatformVar,
74 | }
75 | // modify Masterminds/sprig functions
76 | // to remove OS functions
77 | //
78 | // https://masterminds.github.io/sprig/os.html
79 | sf := sprig.TxtFuncMap()
80 | delete(sf, "env")
81 | delete(sf, "expandenv")
82 |
83 | // parse the template with Masterminds/sprig functions
84 | //
85 | // https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap
86 | t, err := template.New("build").Funcs(sf).Funcs(templateFuncMap).Parse(b)
87 | if err != nil {
88 | return nil, err
89 | }
90 |
91 | // execute the template
92 | err = t.Execute(buffer, "")
93 | if err != nil {
94 | return nil, fmt.Errorf("unable to execute template: %w", err)
95 | }
96 |
97 | // unmarshal the template to the pipeline
98 | err = yaml.Unmarshal(buffer.Bytes(), config)
99 | if err != nil {
100 | return nil, fmt.Errorf("unable to unmarshal yaml: %w", err)
101 | }
102 |
103 | return config, nil
104 | }
105 |
--------------------------------------------------------------------------------
/template/native/render_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package native
6 |
7 | import (
8 | "io/ioutil"
9 | "testing"
10 |
11 | goyaml "github.com/buildkite/yaml"
12 | "github.com/google/go-cmp/cmp"
13 |
14 | "github.com/go-vela/types/raw"
15 | "github.com/go-vela/types/yaml"
16 | )
17 |
18 | func TestNative_RenderStep(t *testing.T) {
19 | type args struct {
20 | velaFile string
21 | templateFile string
22 | }
23 | tests := []struct {
24 | name string
25 | args args
26 | wantFile string
27 | wantErr bool
28 | }{
29 | {"basic", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/basic/tmpl.yml"}, "testdata/step/basic/want.yml", false},
30 | {"multiline", args{velaFile: "testdata/step/multiline/step.yml", templateFile: "testdata/step/multiline/tmpl.yml"}, "testdata/step/multiline/want.yml", false},
31 | {"conditional match", args{velaFile: "testdata/step/conditional/step.yml", templateFile: "testdata/step/conditional/tmpl.yml"}, "testdata/step/conditional/want.yml", false},
32 | {"loop map", args{velaFile: "testdata/step/loop_map/step.yml", templateFile: "testdata/step/loop_map/tmpl.yml"}, "testdata/step/loop_map/want.yml", false},
33 | {"loop slice", args{velaFile: "testdata/step/loop_slice/step.yml", templateFile: "testdata/step/loop_slice/tmpl.yml"}, "testdata/step/loop_slice/want.yml", false},
34 | {"platform vars", args{velaFile: "testdata/step/with_vars_plat/step.yml", templateFile: "testdata/step/with_vars_plat/tmpl.yml"}, "testdata/step/with_vars_plat/want.yml", false},
35 | {"invalid template", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/invalid_template.yml"}, "", true},
36 | {"invalid variable", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/invalid_variables.yml"}, "", true},
37 | {"invalid yml", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/invalid.yml"}, "", true},
38 | {"disallowed env func", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/disallowed/tmpl_env.yml"}, "", true},
39 | {"disallowed expandenv func", args{velaFile: "testdata/step/basic/step.yml", templateFile: "testdata/step/disallowed/tmpl_expandenv.yml"}, "", true},
40 | }
41 | for _, tt := range tests {
42 | t.Run(tt.name, func(t *testing.T) {
43 | sFile, err := ioutil.ReadFile(tt.args.velaFile)
44 | if err != nil {
45 | t.Error(err)
46 | }
47 | b := &yaml.Build{}
48 | err = goyaml.Unmarshal(sFile, b)
49 | if err != nil {
50 | t.Error(err)
51 | }
52 | b.Steps[0].Environment = raw.StringSliceMap{
53 | "VELA_REPO_FULL_NAME": "octocat/hello-world",
54 | }
55 |
56 | tmpl, err := ioutil.ReadFile(tt.args.templateFile)
57 | if err != nil {
58 | t.Error(err)
59 | }
60 |
61 | steps, secrets, services, environment, err := RenderStep(string(tmpl), b.Steps[0])
62 | if (err != nil) != tt.wantErr {
63 | t.Errorf("RenderStep() error = %v, wantErr %v", err, tt.wantErr)
64 | return
65 | }
66 |
67 | if tt.wantErr != true {
68 | wFile, err := ioutil.ReadFile(tt.wantFile)
69 | if err != nil {
70 | t.Error(err)
71 | }
72 | w := &yaml.Build{}
73 | err = goyaml.Unmarshal(wFile, w)
74 | if err != nil {
75 | t.Error(err)
76 | }
77 | wantSteps := w.Steps
78 | wantSecrets := w.Secrets
79 | wantServices := w.Services
80 | wantEnvironment := w.Environment
81 |
82 | if diff := cmp.Diff(wantSteps, steps); diff != "" {
83 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
84 | }
85 | if diff := cmp.Diff(wantSecrets, secrets); diff != "" {
86 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
87 | }
88 | if diff := cmp.Diff(wantServices, services); diff != "" {
89 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
90 | }
91 | if diff := cmp.Diff(wantEnvironment, environment); diff != "" {
92 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
93 | }
94 | }
95 | })
96 | }
97 | }
98 |
99 | func TestNative_RenderBuild(t *testing.T) {
100 | type args struct {
101 | velaFile string
102 | }
103 | tests := []struct {
104 | name string
105 | args args
106 | wantFile string
107 | wantErr bool
108 | }{
109 | {"steps", args{velaFile: "testdata/build/basic/build.yml"}, "testdata/build/basic/want.yml", false},
110 | {"stages", args{velaFile: "testdata/build/basic_stages/build.yml"}, "testdata/build/basic_stages/want.yml", false},
111 | {"conditional match", args{velaFile: "testdata/build/conditional/build.yml"}, "testdata/build/conditional/want.yml", false},
112 | }
113 | for _, tt := range tests {
114 | t.Run(tt.name, func(t *testing.T) {
115 | sFile, err := ioutil.ReadFile(tt.args.velaFile)
116 | if err != nil {
117 | t.Error(err)
118 | }
119 |
120 | got, err := RenderBuild(string(sFile), map[string]string{
121 | "VELA_REPO_FULL_NAME": "octocat/hello-world",
122 | "VELA_BUILD_BRANCH": "master",
123 | })
124 | if (err != nil) != tt.wantErr {
125 | t.Errorf("RenderBuild() error = %v, wantErr %v", err, tt.wantErr)
126 | return
127 | }
128 |
129 | if tt.wantErr != true {
130 | wFile, err := ioutil.ReadFile(tt.wantFile)
131 | if err != nil {
132 | t.Error(err)
133 | }
134 | want := &yaml.Build{}
135 | err = goyaml.Unmarshal(wFile, want)
136 | if err != nil {
137 | t.Error(err)
138 | }
139 |
140 | if diff := cmp.Diff(want, got); diff != "" {
141 | t.Errorf("RenderBuild() mismatch (-want +got):\n%s", diff)
142 | }
143 | }
144 | })
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/template/native/testdata/build/basic/build.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | {{$steplist := list "foo" "bar" "star"}}
4 |
5 | steps:
6 | {{range $step := $steplist}}
7 | - name: {{ $step }}
8 | image: alpine
9 | commands:
10 | - echo hello from {{ $step }}
11 | {{ end }}
--------------------------------------------------------------------------------
/template/native/testdata/build/basic/want.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | steps:
4 | - name: foo
5 | image: alpine
6 | pull: not_present
7 | commands:
8 | - echo hello from foo
9 | - name: bar
10 | image: alpine
11 | pull: not_present
12 | commands:
13 | - echo hello from bar
14 | - name: star
15 | image: alpine
16 | pull: not_present
17 | commands:
18 | - echo hello from star
--------------------------------------------------------------------------------
/template/native/testdata/build/basic_stages/build.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | {{$stageList := list "foo" "bar" "star" -}}
4 |
5 | stages:
6 | {{range $stage := $stageList -}}
7 | {{ $stage }}:
8 | steps:
9 | - name: {{ $stage }}
10 | image: alpine
11 | commands:
12 | - echo hello from {{ $stage }}
13 | {{ end }}
14 |
--------------------------------------------------------------------------------
/template/native/testdata/build/basic_stages/want.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | stages:
4 | foo:
5 | steps:
6 | - name: foo
7 | image: alpine
8 | commands:
9 | - echo hello from foo
10 | needs: [ "clone" ]
11 | pull: not_present
12 |
13 | bar:
14 | steps:
15 | - name: bar
16 | image: alpine
17 | commands:
18 | - echo hello from bar
19 | needs: [ "clone" ]
20 | pull: not_present
21 |
22 | star:
23 | steps:
24 | - name: star
25 | image: alpine
26 | commands:
27 | - echo hello from star
28 | needs: [ "clone" ]
29 | pull: not_present
30 |
31 |
--------------------------------------------------------------------------------
/template/native/testdata/build/conditional/build.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | {{$br := vela "vela_build_branch"}}
4 | {{$image := "golang:latest"}}
5 |
6 | steps:
7 |
8 | {{ if (eq $br "master") }}
9 |
10 | - name: install
11 | commands:
12 | - go get ./...
13 | image: {{ $image }}
14 | ruleset:
15 | event: [ push, pull_request ]
16 |
17 | {{ end }}
18 |
19 | - name: test
20 | commands:
21 | - go test ./...
22 | image: {{ $image }}
23 | ruleset:
24 | event: [ push, pull_request ]
25 |
26 | - name: build
27 | commands:
28 | - go build
29 | environment:
30 | CGO_ENABLED: '0'
31 | GOOS: linux
32 | image: {{ $image }}
33 | ruleset:
34 | event: [ push, pull_request ]
35 |
--------------------------------------------------------------------------------
/template/native/testdata/build/conditional/want.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 | steps:
3 | - name: install
4 | commands:
5 | - go get ./...
6 | image: golang:latest
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: test
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | ruleset:
15 | event: [ push, pull_request ]
16 |
17 | - name: build
18 | commands:
19 | - go build
20 | environment:
21 | CGO_ENABLED: '0'
22 | GOOS: linux
23 | image: golang:latest
24 | ruleset:
25 | event: [ push, pull_request ]
26 |
--------------------------------------------------------------------------------
/template/native/testdata/step/basic/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | image: golang:latest
7 | pull_policy: "pull: true"
8 |
9 |
--------------------------------------------------------------------------------
/template/native/testdata/step/basic/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: install
6 | commands:
7 | - go get ./...
8 | image: {{ .image }}
9 | {{ .pull_policy }}
10 | ruleset:
11 | event: [ push, pull_request ]
12 |
13 | - name: test
14 | commands:
15 | - go test ./...
16 | image: {{ .image }}
17 | {{ .pull_policy }}
18 | ruleset:
19 | event: [ push, pull_request ]
20 |
21 | - name: build
22 | commands:
23 | - go build
24 | environment:
25 | CGO_ENABLED: '0'
26 | GOOS: linux
27 | image: {{ .image }}
28 | {{ .pull_policy }}
29 | ruleset:
30 | event: [ push, pull_request ]
31 |
--------------------------------------------------------------------------------
/template/native/testdata/step/basic/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_install
3 | commands:
4 | - go get ./...
5 | image: golang:latest
6 | pull: true
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: sample_test
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | pull: true
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | - name: sample_build
19 | commands:
20 | - go build
21 | environment:
22 | CGO_ENABLED: '0'
23 | GOOS: linux
24 | image: golang:latest
25 | pull: true
26 | ruleset:
27 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/conditional/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | image: golang:latest
7 | pull_policy: "pull: true"
8 | branch: master
9 |
10 |
--------------------------------------------------------------------------------
/template/native/testdata/step/conditional/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | {{$br := .branch}}
5 |
6 | steps:
7 |
8 | {{ if (eq $br "master") }}
9 |
10 | - name: install
11 | commands:
12 | - go get ./...
13 | image: {{ .image }}
14 | {{ .pull_policy }}
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | {{ end }}
19 |
20 | - name: test
21 | commands:
22 | - go test ./...
23 | image: {{ .image }}
24 | {{ .pull_policy }}
25 | ruleset:
26 | event: [ push, pull_request ]
27 |
28 | - name: build
29 | commands:
30 | - go build
31 | environment:
32 | CGO_ENABLED: '0'
33 | GOOS: linux
34 | image: {{ .image }}
35 | {{ .pull_policy }}
36 | ruleset:
37 | event: [ push, pull_request ]
38 |
--------------------------------------------------------------------------------
/template/native/testdata/step/conditional/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_install
3 | commands:
4 | - go get ./...
5 | image: golang:latest
6 | pull: true
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: sample_test
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | pull: true
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | - name: sample_build
19 | commands:
20 | - go build
21 | environment:
22 | CGO_ENABLED: '0'
23 | GOOS: linux
24 | image: golang:latest
25 | pull: true
26 | ruleset:
27 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/disallowed/tmpl_env.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: echo
6 | commands:
7 | - echo {{ env "VELA_SOURCE_CLIENT" }}
8 | image: alpine:latest
--------------------------------------------------------------------------------
/template/native/testdata/step/disallowed/tmpl_expandenv.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: echo
6 | commands:
7 | - echo {{ expandenv "Your client id is set to $VELA_SOURCE_CLIENT" }}
8 | image: alpine:latest
--------------------------------------------------------------------------------
/template/native/testdata/step/invalid.yml:
--------------------------------------------------------------------------------
1 | ---
2 | !@#$%^&*()
--------------------------------------------------------------------------------
/template/native/testdata/step/invalid_template.yml:
--------------------------------------------------------------------------------
1 | ---
2 | {{ foobar }}
3 |
--------------------------------------------------------------------------------
/template/native/testdata/step/invalid_variables.yml:
--------------------------------------------------------------------------------
1 | ---
2 | {{template "foobar"}}
3 |
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_map/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | pull_policy: "pull: true"
7 | # loop over map of images
8 | images:
9 | latest: golang:latest
10 |
11 |
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_map/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | ## template variables
5 | {{ $pull := .pull_policy }}
6 |
7 | steps:
8 |
9 | - name: install
10 | commands:
11 | - go get ./...
12 | image: golang:latest
13 | {{ $pull }}
14 | ruleset:
15 | event: [ push, pull_request ]
16 |
17 | {{ range $key, $value := .images }}
18 |
19 | - name: test_{{ $key }}
20 | commands:
21 | - go test ./...
22 | image: {{ $value }}
23 | {{ $pull }}
24 | ruleset:
25 | event: [ push, pull_request ]
26 |
27 | {{ end }}
28 |
29 | - name: build
30 | commands:
31 | - go build
32 | environment:
33 | CGO_ENABLED: '0'
34 | GOOS: linux
35 | image: golang:latest
36 | {{ $pull }}
37 | ruleset:
38 | event: [ push, pull_request ]
39 |
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_map/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_install
3 | commands:
4 | - go get ./...
5 | image: golang:latest
6 | pull: true
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: sample_test_latest
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | pull: true
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | - name: sample_build
19 | commands:
20 | - go build
21 | environment:
22 | CGO_ENABLED: '0'
23 | GOOS: linux
24 | image: golang:latest
25 | pull: true
26 | ruleset:
27 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_slice/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | pull_policy: "pull: true"
7 | # loop over slice of images
8 | images: [ golang:latest, golang:1.12, golang:1.13 ]
9 |
10 |
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_slice/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | ## template variables
5 | {{ $pull := .pull_policy }}
6 |
7 | steps:
8 |
9 | - name: install
10 | commands:
11 | - go get ./...
12 | image: golang:latest
13 | {{ $pull }}
14 | ruleset:
15 | event: [ push, pull_request ]
16 |
17 | {{ range $value := .images }}
18 |
19 | - name: test_{{ $value }}
20 | commands:
21 | - go test ./...
22 | image: {{ $value }}
23 | {{ $pull }}
24 | ruleset:
25 | event: [ push, pull_request ]
26 |
27 | {{ end }}
28 |
29 | - name: build
30 | commands:
31 | - go build
32 | environment:
33 | CGO_ENABLED: '0'
34 | GOOS: linux
35 | image: golang:latest
36 | {{ $pull }}
37 | ruleset:
38 | event: [ push, pull_request ]
39 |
--------------------------------------------------------------------------------
/template/native/testdata/step/loop_slice/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_install
3 | commands:
4 | - go get ./...
5 | image: golang:latest
6 | pull: true
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: sample_test_golang:latest
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | pull: true
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | - name: sample_test_golang:1.12
19 | commands:
20 | - go test ./...
21 | image: golang:1.12
22 | pull: true
23 | ruleset:
24 | event: [ push, pull_request ]
25 |
26 | - name: sample_test_golang:1.13
27 | commands:
28 | - go test ./...
29 | image: golang:1.13
30 | pull: true
31 | ruleset:
32 | event: [ push, pull_request ]
33 |
34 | - name: sample_build
35 | commands:
36 | - go build
37 | environment:
38 | CGO_ENABLED: '0'
39 | GOOS: linux
40 | image: golang:latest
41 | pull: true
42 | ruleset:
43 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/multiline/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | test: |
7 | - name: test
8 | commands:
9 | - go test ./...
10 | image: golang:latest
11 | pull: true
12 | ruleset:
13 | event: [ push, pull_request ]
14 |
15 |
--------------------------------------------------------------------------------
/template/native/testdata/step/multiline/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: install
6 | commands:
7 | - go get ./...
8 | image: golang:latest
9 | pull: true
10 | ruleset:
11 | event: [ push, pull_request ]
12 |
13 | {{ .test }}
14 |
15 | - name: build
16 | commands:
17 | - go build
18 | environment:
19 | CGO_ENABLED: '0'
20 | GOOS: linux
21 | image: golang:latest
22 | pull: true
23 | ruleset:
24 | event: [ push, pull_request ]
25 |
--------------------------------------------------------------------------------
/template/native/testdata/step/multiline/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_install
3 | commands:
4 | - go get ./...
5 | image: golang:latest
6 | pull: true
7 | ruleset:
8 | event: [ push, pull_request ]
9 |
10 | - name: sample_test
11 | commands:
12 | - go test ./...
13 | image: golang:latest
14 | pull: true
15 | ruleset:
16 | event: [ push, pull_request ]
17 |
18 | - name: sample_build
19 | commands:
20 | - go build
21 | environment:
22 | CGO_ENABLED: '0'
23 | GOOS: linux
24 | image: golang:latest
25 | pull: true
26 | ruleset:
27 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/with_vars_plat/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
--------------------------------------------------------------------------------
/template/native/testdata/step/with_vars_plat/tmpl.yml:
--------------------------------------------------------------------------------
1 | metadata:
2 | template: true
3 |
4 | steps:
5 | - name: test
6 | commands:
7 | - echo {{ vela "repo_full_name" }}
8 | - echo {{ vela "REPO_FULL_NAME" }}
9 | - echo {{ vela "vela_repo_full_name" }}
10 | - echo {{ vela "VELA_REPO_FULL_NAME" }}
11 | - echo {{ vela "non_existent" }}
12 | image: alpine
13 | pull: true
14 | ruleset:
15 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/native/testdata/step/with_vars_plat/want.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample_test
3 | commands:
4 | - echo octocat/hello-world
5 | - echo octocat/hello-world
6 | - echo octocat/hello-world
7 | - echo octocat/hello-world
8 | - echo
9 | image: alpine
10 | pull: true
11 | ruleset:
12 | event: [ push, pull_request ]
--------------------------------------------------------------------------------
/template/starlark/convert.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package starlark
6 |
7 | import (
8 | "strings"
9 |
10 | "github.com/go-vela/types/raw"
11 | "go.starlark.net/starlark"
12 | )
13 |
14 | // convertTemplateVars takes template variables and converts
15 | // them to a starlark string dictionary for template reference.
16 | //
17 | // Example Usage within template: ctx["vars"]["message"] = "Hello, World!"
18 | //
19 | // Explanation of type "starlark.StringDict":
20 | // https://pkg.go.dev/go.starlark.net/starlark#StringDict
21 | func convertTemplateVars(m map[string]interface{}) (*starlark.Dict, error) {
22 | dict := starlark.NewDict(0)
23 |
24 | // loop through user vars converting provided types to starlark primitives
25 | for key, value := range m {
26 | val, err := toStarlark(value)
27 | if err != nil {
28 | return nil, err
29 | }
30 |
31 | err = dict.SetKey(starlark.String(key), val)
32 | if err != nil {
33 | return nil, err
34 | }
35 | }
36 |
37 | return dict, nil
38 | }
39 |
40 | // convertPlatformVars takes the platform injected variables
41 | // within the step environment block and converts them to a
42 | // starlark string dictionary.
43 | //
44 | // Example Usage within template: ctx["vela"]["build"]["number"] = 1
45 | //
46 | // Explanation of type "starlark.StringDict":
47 | // https://pkg.go.dev/go.starlark.net/starlark#StringDict
48 | func convertPlatformVars(slice raw.StringSliceMap, name string) (*starlark.Dict, error) {
49 | build := starlark.NewDict(0)
50 | repo := starlark.NewDict(0)
51 | user := starlark.NewDict(0)
52 | system := starlark.NewDict(0)
53 | dict := starlark.NewDict(0)
54 |
55 | err := dict.SetKey(starlark.String("build"), build)
56 | if err != nil {
57 | return nil, err
58 | }
59 | err = dict.SetKey(starlark.String("repo"), repo)
60 | if err != nil {
61 | return nil, err
62 | }
63 | err = dict.SetKey(starlark.String("user"), user)
64 | if err != nil {
65 | return nil, err
66 | }
67 | err = dict.SetKey(starlark.String("system"), system)
68 | if err != nil {
69 | return nil, err
70 | }
71 |
72 | err = system.SetKey(starlark.String("template_name"), starlark.String(name))
73 | if err != nil {
74 | return nil, err
75 | }
76 |
77 | for key, value := range slice {
78 | key = strings.ToLower(key)
79 | if strings.HasPrefix(key, "vela_") {
80 | key = strings.TrimPrefix(key, "vela_")
81 |
82 | switch {
83 | case strings.HasPrefix(key, "build_"):
84 | err := build.SetKey(starlark.String(strings.TrimPrefix(key, "build_")), starlark.String(value))
85 | if err != nil {
86 | return nil, err
87 | }
88 | case strings.HasPrefix(key, "repo_"):
89 | err := repo.SetKey(starlark.String(strings.TrimPrefix(key, "repo_")), starlark.String(value))
90 | if err != nil {
91 | return nil, err
92 | }
93 | case strings.HasPrefix(key, "user_"):
94 | err := user.SetKey(starlark.String(strings.TrimPrefix(key, "user_")), starlark.String(value))
95 | if err != nil {
96 | return nil, err
97 | }
98 | default:
99 | err := system.SetKey(starlark.String(key), starlark.String(value))
100 | if err != nil {
101 | return nil, err
102 | }
103 | }
104 | }
105 | }
106 |
107 | return dict, nil
108 | }
109 |
--------------------------------------------------------------------------------
/template/starlark/convert_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package starlark
6 |
7 | import (
8 | "reflect"
9 | "testing"
10 |
11 | "github.com/go-vela/types/raw"
12 | "go.starlark.net/starlark"
13 | )
14 |
15 | func TestStarlark_Render_convertTemplateVars(t *testing.T) {
16 | // setup types
17 | tags := starlark.Tuple(nil)
18 | tags = append(tags, starlark.String("latest"))
19 | tags = append(tags, starlark.String("1.14"))
20 | tags = append(tags, starlark.String("1.15"))
21 |
22 | commands := starlark.NewDict(16)
23 | err := commands.SetKey(starlark.String("test"), starlark.String("go test ./..."))
24 | if err != nil {
25 | t.Error(err)
26 | }
27 |
28 | strWant := starlark.NewDict(0)
29 | err = strWant.SetKey(starlark.String("pull"), starlark.String("always"))
30 | if err != nil {
31 | t.Error(err)
32 | }
33 |
34 | arrayWant := starlark.NewDict(0)
35 | err = arrayWant.SetKey(starlark.String("tags"), tags)
36 | if err != nil {
37 | t.Error(err)
38 | }
39 |
40 | mapWant := starlark.NewDict(0)
41 | err = mapWant.SetKey(starlark.String("commands"), commands)
42 | if err != nil {
43 | t.Error(err)
44 | }
45 |
46 | tests := []struct {
47 | name string
48 | args map[string]interface{}
49 | want *starlark.Dict
50 | }{
51 | {
52 | name: "test for a user passed string",
53 | args: map[string]interface{}{"pull": "always"},
54 | want: strWant,
55 | },
56 | {
57 | name: "test for a user passed array",
58 | args: map[string]interface{}{"tags": []string{"latest", "1.14", "1.15"}},
59 | want: arrayWant,
60 | },
61 | {
62 | name: "test for a user passed map",
63 | args: map[string]interface{}{"commands": map[string]string{"test": "go test ./..."}},
64 | want: mapWant,
65 | }}
66 |
67 | for _, tt := range tests {
68 | t.Run(tt.name, func(t *testing.T) {
69 | got, err := convertTemplateVars(tt.args)
70 | if err != nil {
71 | t.Error(err)
72 | }
73 |
74 | if !reflect.DeepEqual(got, tt.want) {
75 | t.Errorf("convertTemplateVars() = %v, want %v", got, tt.want)
76 | }
77 | })
78 | }
79 | }
80 |
81 | func TestStarlark_Render_velaEnvironmentData(t *testing.T) {
82 | // setup types
83 | build := starlark.NewDict(1)
84 | err := build.SetKey(starlark.String("author"), starlark.String("octocat"))
85 | if err != nil {
86 | t.Error(err)
87 | }
88 |
89 | repo := starlark.NewDict(1)
90 | err = repo.SetKey(starlark.String("full_name"), starlark.String("go-vela/hello-world"))
91 | if err != nil {
92 | t.Error(err)
93 | }
94 |
95 | user := starlark.NewDict(1)
96 | err = user.SetKey(starlark.String("admin"), starlark.String("true"))
97 | if err != nil {
98 | t.Error(err)
99 | }
100 |
101 | system := starlark.NewDict(2)
102 | err = system.SetKey(starlark.String("template_name"), starlark.String("foo"))
103 | if err != nil {
104 | t.Error(err)
105 | }
106 | err = system.SetKey(starlark.String("workspace"), starlark.String("/vela/src/github.com/go-vela/hello-world"))
107 | if err != nil {
108 | t.Error(err)
109 | }
110 |
111 | withAllPre := starlark.NewDict(0)
112 | err = withAllPre.SetKey(starlark.String("build"), build)
113 | if err != nil {
114 | t.Error(err)
115 | }
116 | err = withAllPre.SetKey(starlark.String("repo"), repo)
117 | if err != nil {
118 | t.Error(err)
119 | }
120 | err = withAllPre.SetKey(starlark.String("user"), user)
121 | if err != nil {
122 | t.Error(err)
123 | }
124 | err = withAllPre.SetKey(starlark.String("system"), system)
125 | if err != nil {
126 | t.Error(err)
127 | }
128 |
129 | tests := []struct {
130 | name string
131 | slice raw.StringSliceMap
132 | templateName string
133 | want *starlark.Dict
134 | wantErr bool
135 | }{
136 | {
137 | name: "with all vela prefixed var",
138 | slice: raw.StringSliceMap{
139 | "VELA_BUILD_AUTHOR": "octocat",
140 | "VELA_REPO_FULL_NAME": "go-vela/hello-world",
141 | "VELA_USER_ADMIN": "true",
142 | "VELA_WORKSPACE": "/vela/src/github.com/go-vela/hello-world",
143 | },
144 | templateName: "foo",
145 | want: withAllPre,
146 | },
147 | }
148 | for _, tt := range tests {
149 | t.Run(tt.name, func(t *testing.T) {
150 | got, err := convertPlatformVars(tt.slice, tt.templateName)
151 | if (err != nil) != tt.wantErr {
152 | t.Errorf("convertPlatformVars() error = %v, wantErr %v", err, tt.wantErr)
153 | return
154 | }
155 |
156 | if !reflect.DeepEqual(got, tt.want) {
157 | t.Errorf("convertPlatformVars() = %v, want %v", got, tt.want)
158 | }
159 | })
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/template/starlark/render.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package starlark
6 |
7 | import (
8 | "bytes"
9 | "errors"
10 | "fmt"
11 |
12 | "github.com/go-vela/types/raw"
13 |
14 | yaml "github.com/buildkite/yaml"
15 | types "github.com/go-vela/types/yaml"
16 | "go.starlark.net/starlark"
17 | )
18 |
19 | var (
20 | // ErrMissingMainFunc defines the error type when the
21 | // main function does not exist in the provided template.
22 | ErrMissingMainFunc = errors.New("unable to find main function in template")
23 |
24 | // ErrInvalidMainFunc defines the error type when the
25 | // main function is invalid within the provided template.
26 | ErrInvalidMainFunc = errors.New("invalid main function (main must be a function) in template")
27 |
28 | // ErrInvalidPipelineReturn defines the error type when the
29 | // return type is not a pipeline within the provided template.
30 | ErrInvalidPipelineReturn = errors.New("invalid pipeline return in template")
31 | )
32 |
33 | // RenderStep combines the template with the step in the yaml pipeline.
34 | //
35 | // nolint: funlen,lll // ignore function length due to comments
36 | func RenderStep(tmpl string, s *types.Step) (types.StepSlice, types.SecretSlice, types.ServiceSlice, raw.StringSliceMap, error) {
37 | config := new(types.Build)
38 |
39 | thread := &starlark.Thread{Name: s.Name}
40 | // arbitrarily limiting the steps of the thread to 5000 to help prevent infinite loops
41 | // may need to further investigate spawning a separate POSIX process if user input is problematic
42 | // see https://github.com/google/starlark-go/issues/160#issuecomment-466794230 for further details
43 | //
44 | // nolint: gomnd // ignore magic number
45 | thread.SetMaxExecutionSteps(5000)
46 | globals, err := starlark.ExecFile(thread, s.Template.Name, tmpl, nil)
47 | if err != nil {
48 | return nil, nil, nil, nil, err
49 | }
50 |
51 | // check the provided template has a main function
52 | mainVal, ok := globals["main"]
53 | if !ok {
54 | return nil, nil, nil, nil, fmt.Errorf("%s: %s", ErrMissingMainFunc, s.Template.Name)
55 | }
56 |
57 | // check the provided main is a function
58 | main, ok := mainVal.(starlark.Callable)
59 | if !ok {
60 | return nil, nil, nil, nil, fmt.Errorf("%s: %s", ErrInvalidMainFunc, s.Template.Name)
61 | }
62 |
63 | // load the user provided vars into a starlark type
64 | userVars, err := convertTemplateVars(s.Template.Variables)
65 | if err != nil {
66 | return nil, nil, nil, nil, err
67 | }
68 |
69 | // load the platform provided vars into a starlark type
70 | velaVars, err := convertPlatformVars(s.Environment, s.Name)
71 | if err != nil {
72 | return nil, nil, nil, nil, err
73 | }
74 |
75 | // add the user and platform vars to a context to be used
76 | // within the template caller i.e. ctx["vela"] or ctx["vars"]
77 | context := starlark.NewDict(0)
78 | err = context.SetKey(starlark.String("vela"), velaVars)
79 | if err != nil {
80 | return nil, nil, nil, nil, err
81 | }
82 | err = context.SetKey(starlark.String("vars"), userVars)
83 | if err != nil {
84 | return nil, nil, nil, nil, err
85 | }
86 |
87 | args := starlark.Tuple([]starlark.Value{context})
88 |
89 | // execute Starlark program from Go.
90 | mainVal, err = starlark.Call(thread, main, args, nil)
91 | if err != nil {
92 | return nil, nil, nil, nil, err
93 | }
94 |
95 | buf := new(bytes.Buffer)
96 |
97 | // extract the pipeline from the starlark program
98 | switch v := mainVal.(type) {
99 | case *starlark.List:
100 | for i := 0; i < v.Len(); i++ {
101 | item := v.Index(i)
102 | buf.WriteString("---\n")
103 | err = writeJSON(buf, item)
104 | if err != nil {
105 | return nil, nil, nil, nil, err
106 | }
107 | buf.WriteString("\n")
108 | }
109 | case *starlark.Dict:
110 | buf.WriteString("---\n")
111 | err = writeJSON(buf, v)
112 | if err != nil {
113 | return nil, nil, nil, nil, err
114 | }
115 | default:
116 | return nil, nil, nil, nil, fmt.Errorf("%s: %s", ErrInvalidPipelineReturn, mainVal.Type())
117 | }
118 |
119 | // unmarshal the template to the pipeline
120 | err = yaml.Unmarshal(buf.Bytes(), config)
121 | if err != nil {
122 | // nolint: lll // ignore long line length due to return args
123 | return types.StepSlice{}, types.SecretSlice{}, types.ServiceSlice{}, raw.StringSliceMap{}, fmt.Errorf("unable to unmarshal yaml: %v", err)
124 | }
125 |
126 | // ensure all templated steps have template prefix
127 | for index, newStep := range config.Steps {
128 | config.Steps[index].Name = fmt.Sprintf("%s_%s", s.Name, newStep.Name)
129 | }
130 |
131 | return config.Steps, config.Secrets, config.Services, config.Environment, nil
132 | }
133 |
134 | // RenderBuild renders the templated build.
135 | func RenderBuild(b string, envs map[string]string) (*types.Build, error) {
136 | config := new(types.Build)
137 |
138 | thread := &starlark.Thread{Name: "templated-base"}
139 | // arbitrarily limiting the steps of the thread to 5000 to help prevent infinite loops
140 | // may need to further investigate spawning a separate POSIX process if user input is problematic
141 | // see https://github.com/google/starlark-go/issues/160#issuecomment-466794230 for further details
142 | //
143 | // nolint: gomnd // ignore magic number
144 | thread.SetMaxExecutionSteps(5000)
145 | globals, err := starlark.ExecFile(thread, "templated-base", b, nil)
146 | if err != nil {
147 | return nil, err
148 | }
149 |
150 | // check the provided template has a main function
151 | mainVal, ok := globals["main"]
152 | if !ok {
153 | return nil, fmt.Errorf("%s: %s", ErrMissingMainFunc, "templated-base")
154 | }
155 |
156 | // check the provided main is a function
157 | main, ok := mainVal.(starlark.Callable)
158 | if !ok {
159 | return nil, fmt.Errorf("%s: %s", ErrInvalidMainFunc, "templated-base")
160 | }
161 |
162 | // load the platform provided vars into a starlark type
163 | velaVars, err := convertPlatformVars(envs, "")
164 | if err != nil {
165 | return nil, err
166 | }
167 |
168 | // add the user and platform vars to a context to be used
169 | // within the template caller i.e. ctx["vela"] or ctx["vars"]
170 | context := starlark.NewDict(0)
171 | err = context.SetKey(starlark.String("vela"), velaVars)
172 | if err != nil {
173 | return nil, err
174 | }
175 |
176 | args := starlark.Tuple([]starlark.Value{context})
177 |
178 | // execute Starlark program from Go.
179 | mainVal, err = starlark.Call(thread, main, args, nil)
180 | if err != nil {
181 | return nil, err
182 | }
183 |
184 | buf := new(bytes.Buffer)
185 |
186 | // extract the pipeline from the starlark program
187 | switch v := mainVal.(type) {
188 | case *starlark.List:
189 | for i := 0; i < v.Len(); i++ {
190 | item := v.Index(i)
191 | buf.WriteString("---\n")
192 | err = writeJSON(buf, item)
193 | if err != nil {
194 | return nil, err
195 | }
196 | buf.WriteString("\n")
197 | }
198 | case *starlark.Dict:
199 | buf.WriteString("---\n")
200 | err = writeJSON(buf, v)
201 | if err != nil {
202 | return nil, err
203 | }
204 | default:
205 | return nil, fmt.Errorf("%s: %s", ErrInvalidPipelineReturn, mainVal.Type())
206 | }
207 |
208 | // unmarshal the template to the pipeline
209 | err = yaml.Unmarshal(buf.Bytes(), config)
210 | if err != nil {
211 | return nil, fmt.Errorf("unable to unmarshal yaml: %v", err)
212 | }
213 |
214 | return config, nil
215 | }
216 |
--------------------------------------------------------------------------------
/template/starlark/render_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package starlark
6 |
7 | import (
8 | "io/ioutil"
9 | "testing"
10 |
11 | goyaml "github.com/buildkite/yaml"
12 | "github.com/go-vela/types/raw"
13 | "github.com/go-vela/types/yaml"
14 | "github.com/google/go-cmp/cmp"
15 | )
16 |
17 | func TestStarlark_RenderStep(t *testing.T) {
18 | type args struct {
19 | velaFile string
20 | starlarkFile string
21 | }
22 | tests := []struct {
23 | name string
24 | args args
25 | wantFile string
26 | wantErr bool
27 | }{
28 | {"basic", args{velaFile: "testdata/step/basic/step.yml", starlarkFile: "testdata/step/basic/template.py"}, "testdata/step/basic/want.yml", false},
29 | {"with method", args{velaFile: "testdata/step/with_method/step.yml", starlarkFile: "testdata/step/with_method/template.star"}, "testdata/step/with_method/want.yml", false},
30 | {"user vars", args{velaFile: "testdata/step/with_vars/step.yml", starlarkFile: "testdata/step/with_vars/template.star"}, "testdata/step/with_vars/want.yml", false},
31 | {"platform vars", args{velaFile: "testdata/step/with_vars_plat/step.yml", starlarkFile: "testdata/step/with_vars_plat/template.star"}, "testdata/step/with_vars_plat/want.yml", false},
32 | {"cancel due to complexity", args{velaFile: "testdata/step/cancel/step.yml", starlarkFile: "testdata/step/cancel/template.star"}, "", true},
33 | }
34 | for _, tt := range tests {
35 | t.Run(tt.name, func(t *testing.T) {
36 | sFile, err := ioutil.ReadFile(tt.args.velaFile)
37 | if err != nil {
38 | t.Error(err)
39 | }
40 | b := &yaml.Build{}
41 | err = goyaml.Unmarshal(sFile, b)
42 | if err != nil {
43 | t.Error(err)
44 | }
45 | b.Steps[0].Environment = raw.StringSliceMap{
46 | "VELA_REPO_FULL_NAME": "octocat/hello-world",
47 | }
48 |
49 | tmpl, err := ioutil.ReadFile(tt.args.starlarkFile)
50 | if err != nil {
51 | t.Error(err)
52 | }
53 |
54 | steps, secrets, services, environment, err := RenderStep(string(tmpl), b.Steps[0])
55 | if (err != nil) != tt.wantErr {
56 | t.Errorf("RenderStep() error = %v, wantErr %v", err, tt.wantErr)
57 | return
58 | }
59 |
60 | if tt.wantErr != true {
61 | wFile, err := ioutil.ReadFile(tt.wantFile)
62 | if err != nil {
63 | t.Error(err)
64 | }
65 | w := &yaml.Build{}
66 | err = goyaml.Unmarshal(wFile, w)
67 | if err != nil {
68 | t.Error(err)
69 | }
70 | wantSteps := w.Steps
71 | wantSecrets := w.Secrets
72 | wantServices := w.Services
73 | wantEnvironment := w.Environment
74 |
75 | if diff := cmp.Diff(wantSteps, steps); diff != "" {
76 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
77 | }
78 | if diff := cmp.Diff(wantSecrets, secrets); diff != "" {
79 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
80 | }
81 | if diff := cmp.Diff(wantServices, services); diff != "" {
82 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
83 | }
84 | if diff := cmp.Diff(wantEnvironment, environment); diff != "" {
85 | t.Errorf("RenderStep() mismatch (-want +got):\n%s", diff)
86 | }
87 | }
88 | })
89 | }
90 | }
91 |
92 | func TestNative_RenderBuild(t *testing.T) {
93 | type args struct {
94 | velaFile string
95 | }
96 | tests := []struct {
97 | name string
98 | args args
99 | wantFile string
100 | wantErr bool
101 | }{
102 | {"steps", args{velaFile: "testdata/build/basic/build.star"}, "testdata/build/basic/want.yml", false},
103 | {"stages", args{velaFile: "testdata/build/basic_stages/build.star"}, "testdata/build/basic_stages/want.yml", false},
104 | {"conditional match", args{velaFile: "testdata/build/conditional/build.star"}, "testdata/build/conditional/want.yml", false},
105 | }
106 | for _, tt := range tests {
107 | t.Run(tt.name, func(t *testing.T) {
108 | sFile, err := ioutil.ReadFile(tt.args.velaFile)
109 | if err != nil {
110 | t.Error(err)
111 | }
112 |
113 | got, err := RenderBuild(string(sFile), map[string]string{
114 | "VELA_REPO_FULL_NAME": "octocat/hello-world",
115 | "VELA_BUILD_BRANCH": "master",
116 | })
117 | if (err != nil) != tt.wantErr {
118 | t.Errorf("RenderBuild() error = %v, wantErr %v", err, tt.wantErr)
119 | return
120 | }
121 |
122 | if tt.wantErr != true {
123 | wFile, err := ioutil.ReadFile(tt.wantFile)
124 | if err != nil {
125 | t.Error(err)
126 | }
127 | want := &yaml.Build{}
128 | err = goyaml.Unmarshal(wFile, want)
129 | if err != nil {
130 | t.Error(err)
131 | }
132 |
133 | if diff := cmp.Diff(want, got); diff != "" {
134 | t.Errorf("RenderBuild() mismatch (-want +got):\n%s", diff)
135 | }
136 | }
137 | })
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/template/starlark/starlark.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package starlark
6 |
7 | import (
8 | "bytes"
9 | "encoding/json"
10 | "errors"
11 | "fmt"
12 | "reflect"
13 |
14 | "github.com/sirupsen/logrus"
15 | "go.starlark.net/starlark"
16 | "k8s.io/apimachinery/pkg/util/intstr"
17 | )
18 |
19 | var (
20 | // ErrUnableToConvertStarlark defines the error type when the
21 | // toStarlark cannot convert the provided value.
22 | ErrUnableToConvertStarlark = errors.New("unable to convert to starlark type")
23 |
24 | // ErrUnableToConvertJSON defines the error type when the
25 | // writeJSON cannot convert the provided value.
26 | ErrUnableToConvertJSON = errors.New("unable to convert to json")
27 | )
28 |
29 | // toStarlark takes an value as an interface an
30 | // will return the comparable Starlark type.
31 | //
32 | // This code is under copyright (full attribution in NOTICE) and is from:
33 | //
34 | // https://github.com/wonderix/shalm/blob/899b8f7787883d40619eefcc39bd12f42a09b5e7/pkg/shalm/convert.go#L14-L85
35 | //
36 | // nolint: gocyclo,funlen,lll // ignore above line length and function length due to comments
37 | func toStarlark(value interface{}) (starlark.Value, error) {
38 | logrus.Tracef("converting %v to starlark type", value)
39 |
40 | if value == nil {
41 | return starlark.None, nil
42 | }
43 |
44 | switch v := reflect.ValueOf(value); v.Kind() {
45 | case reflect.String:
46 | return starlark.String(v.String()), nil
47 | case reflect.Bool:
48 | return starlark.Bool(v.Bool()), nil
49 | case reflect.Int:
50 | fallthrough
51 | case reflect.Int32:
52 | fallthrough
53 | case reflect.Int64:
54 | fallthrough
55 | case reflect.Int16:
56 | return starlark.MakeInt64(v.Int()), nil
57 | case reflect.Uint:
58 | fallthrough
59 | case reflect.Uint32:
60 | fallthrough
61 | case reflect.Uint64:
62 | fallthrough
63 | case reflect.Uint16:
64 | return starlark.MakeUint64(v.Uint()), nil
65 | case reflect.Float32:
66 | return starlark.Float(v.Float()), nil
67 | case reflect.Float64:
68 | return starlark.Float(v.Float()), nil
69 | case reflect.Slice:
70 | if b, ok := value.([]byte); ok {
71 | return starlark.String(string(b)), nil
72 | }
73 |
74 | a := make([]starlark.Value, 0)
75 |
76 | for i := 0; i < v.Len(); i++ {
77 | val, err := toStarlark(v.Index(i).Interface())
78 | if err != nil {
79 | return nil, err
80 | }
81 | a = append(a, val)
82 | }
83 |
84 | return starlark.Tuple(a), nil
85 | case reflect.Ptr:
86 | val, err := toStarlark(v.Elem().Interface())
87 | if err != nil {
88 | return nil, err
89 | }
90 |
91 | return val, nil
92 | case reflect.Map:
93 | // nolint: gomnd // ignore magic number
94 | d := starlark.NewDict(16)
95 |
96 | for _, key := range v.MapKeys() {
97 | strct := v.MapIndex(key)
98 | keyValue, err := toStarlark(key.Interface())
99 | if err != nil {
100 | return nil, err
101 | }
102 |
103 | kv, err := toStarlark(strct.Interface())
104 | if err != nil {
105 | return nil, err
106 | }
107 |
108 | err = d.SetKey(keyValue, kv)
109 | if err != nil {
110 | return nil, err
111 | }
112 | }
113 |
114 | return d, nil
115 | case reflect.Struct:
116 | ios, ok := value.(intstr.IntOrString)
117 | if ok {
118 | switch ios.Type {
119 | case intstr.String:
120 | return starlark.String(ios.StrVal), nil
121 | case intstr.Int:
122 | return starlark.MakeInt(int(ios.IntVal)), nil
123 | }
124 | } else {
125 | data, err := json.Marshal(value)
126 | if err != nil {
127 | return nil, err
128 | }
129 |
130 | var m map[string]interface{}
131 | err = json.Unmarshal(data, &m)
132 | if err != nil {
133 | return nil, err
134 | }
135 |
136 | return toStarlark(m)
137 | }
138 | }
139 |
140 | return nil, fmt.Errorf("%s: %v", ErrUnableToConvertStarlark, value)
141 | }
142 |
143 | // writeJSON takes an starlark input and return the valid JSON
144 | // for the specific type.
145 | //
146 | // This code is under copyright (full attribution in NOTICE) and is from:
147 | //
148 | // https://github.com/drone/drone-cli/blob/master/drone/starlark/starlark.go#L214-L274
149 | //
150 | // Note: we are using logrus log unchecked errors that the original implementation ignored.
151 | // if/when we try to return values it breaks the recursion. Panics were swapped to error
152 | // returns from implementation.
153 | //
154 | // nolint: gocyclo,funlen // ignore cyclomatic complexity and function length
155 | func writeJSON(out *bytes.Buffer, v starlark.Value) error {
156 | logrus.Tracef("converting %v to JSON", v)
157 |
158 | if marshaler, ok := v.(json.Marshaler); ok {
159 | jsonData, err := marshaler.MarshalJSON()
160 | if err != nil {
161 | return err
162 | }
163 |
164 | _, err = out.Write(jsonData)
165 | if err != nil {
166 | logrus.Error(err)
167 | }
168 |
169 | return nil
170 | }
171 |
172 | switch v := v.(type) {
173 | case starlark.NoneType:
174 | _, err := out.WriteString("null")
175 | if err != nil {
176 | logrus.Error(err)
177 | }
178 | case starlark.Bool:
179 | _, err := fmt.Fprintf(out, "%t", v)
180 | if err != nil {
181 | logrus.Error(err)
182 | }
183 | case starlark.Int:
184 | _, err := out.WriteString(v.String())
185 | if err != nil {
186 | logrus.Error(err)
187 | }
188 | case starlark.Float:
189 | _, err := fmt.Fprintf(out, "%g", v)
190 | if err != nil {
191 | logrus.Error(err)
192 | }
193 | case starlark.String:
194 | s := string(v)
195 |
196 | if goQuoteIsSafe(s) {
197 | fmt.Fprintf(out, "%q", s)
198 | } else {
199 | // vanishingly rare for text strings
200 | data, err := json.Marshal(s)
201 | if err != nil {
202 | logrus.Error(err)
203 | }
204 |
205 | _, err = out.Write(data)
206 | if err != nil {
207 | logrus.Error(err)
208 | }
209 | }
210 | case starlark.Indexable: // Tuple, List
211 | err := out.WriteByte('[')
212 | if err != nil {
213 | logrus.Error(err)
214 | }
215 |
216 | for i, n := 0, starlark.Len(v); i < n; i++ {
217 | if i > 0 {
218 | _, err := out.WriteString(", ")
219 | if err != nil {
220 | logrus.Error(err)
221 | }
222 | }
223 |
224 | err := writeJSON(out, v.Index(i))
225 | if err != nil {
226 | return err
227 | }
228 | }
229 |
230 | err = out.WriteByte(']')
231 | if err != nil {
232 | logrus.Error(err)
233 | }
234 | case *starlark.Dict:
235 | err := out.WriteByte('{')
236 | if err != nil {
237 | logrus.Error(err)
238 | }
239 |
240 | for i, itemPair := range v.Items() {
241 | key, value := itemPair[0], itemPair[1]
242 |
243 | if i > 0 {
244 | _, err := out.WriteString(", ")
245 | if err != nil {
246 | logrus.Error(err)
247 | }
248 | }
249 |
250 | err := writeJSON(out, key)
251 | if err != nil {
252 | return err
253 | }
254 |
255 | _, err = out.WriteString(": ")
256 | if err != nil {
257 | logrus.Error(err)
258 | }
259 |
260 | err = writeJSON(out, value)
261 | if err != nil {
262 | return err
263 | }
264 | }
265 |
266 | err = out.WriteByte('}')
267 | if err != nil {
268 | logrus.Error(err)
269 | }
270 | default:
271 | return fmt.Errorf("%s: %v", ErrUnableToConvertJSON, v)
272 | }
273 |
274 | return nil
275 | }
276 |
277 | // goQuoteIsSafe takes a string and checks if is safely quoted
278 | //
279 | // This code is under copyright (full attribution in NOTICE) and is from:
280 | // https://github.com/drone/drone-cli/blob/master/drone/starlark/starlark.go#L276-L285
281 | func goQuoteIsSafe(s string) bool {
282 | for _, r := range s {
283 | // JSON doesn't like Go's \xHH escapes for ASCII control codes,
284 | // nor its \UHHHHHHHH escapes for runes >16 bits.
285 | if r < 0x20 || r >= 0x10000 {
286 | return false
287 | }
288 | }
289 | return true
290 | }
291 |
--------------------------------------------------------------------------------
/template/starlark/starlark_test.go:
--------------------------------------------------------------------------------
1 | package starlark
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 |
7 | "go.starlark.net/starlark"
8 | )
9 |
10 | func TestStarlark_toStarlark(t *testing.T) {
11 | dict := starlark.NewDict(16)
12 | err := dict.SetKey(starlark.String("foo"), starlark.String("bar"))
13 | if err != nil {
14 | t.Error(err)
15 | }
16 | a := make([]starlark.Value, 0)
17 | a = append(a, starlark.Value(starlark.String("foo")))
18 | a = append(a, starlark.Value(starlark.String("bar")))
19 | type args struct {
20 | value interface{}
21 | }
22 | tests := []struct {
23 | name string
24 | args args
25 | want starlark.Value
26 | wantErr bool
27 | }{
28 | {"string", args{value: "foo"}, starlark.String("foo"), false},
29 | {"byte array", args{value: []byte("array")}, starlark.String("array"), false},
30 | {"array", args{value: []string{"foo", "bar"}}, starlark.Tuple(a), false},
31 | {"bool", args{value: true}, starlark.Bool(true), false},
32 | {"float64", args{value: 0.1}, starlark.Float(0.1), false},
33 | {"float32", args{value: float32(0.1)}, starlark.Float(float32(0.1)), false},
34 | {"int", args{value: 1}, starlark.MakeInt(1), false},
35 | {"int32", args{value: int32(1)}, starlark.MakeInt(1), false},
36 | {"int64", args{value: int64(1)}, starlark.MakeInt(1), false},
37 | {"int16", args{value: int16(1)}, starlark.MakeInt(1), false},
38 | {"unit", args{value: uint(1)}, starlark.MakeInt(1), false},
39 | {"unit32", args{value: uint32(1)}, starlark.MakeInt(1), false},
40 | {"unit64", args{value: uint64(1)}, starlark.MakeInt(1), false},
41 | {"unit16", args{value: uint16(1)}, starlark.MakeInt(1), false},
42 | {"nil", args{value: nil}, starlark.None, false},
43 | {"map", args{value: map[string]string{"foo": "bar"}}, dict, false},
44 | }
45 | for _, tt := range tests {
46 | t.Run(tt.name, func(t *testing.T) {
47 | got, err := toStarlark(tt.args.value)
48 | if (err != nil) != tt.wantErr {
49 | t.Errorf("toStarlark() error = %v, wantErr %v", err, tt.wantErr)
50 | return
51 | }
52 | if !reflect.DeepEqual(got, tt.want) {
53 | t.Errorf("toStarlark() got = %v, want %v", got, tt.want)
54 | }
55 | })
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/basic/build.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | stepNames = ["foo", "bar", "star"]
3 |
4 | steps = []
5 |
6 | for name in stepNames:
7 | steps.append(step(name))
8 |
9 | return {
10 | 'version': '1',
11 | 'steps': steps
12 | }
13 |
14 | def step(word):
15 | return {
16 | "name": "build_%s" % word,
17 | "image": "alpine:latest",
18 | 'commands': [
19 | "echo %s" % word
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/basic/want.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | steps:
3 | - name: build_foo
4 | image: alpine:latest
5 | commands:
6 | - echo foo
7 |
8 | - name: build_bar
9 | image: alpine:latest
10 | commands:
11 | - echo bar
12 |
13 | - name: build_star
14 | image: alpine:latest
15 | commands:
16 | - echo star
17 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/basic_stages/build.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | stageNames = ["foo", "bar", "star"]
3 |
4 | stages = {}
5 |
6 | for name in stageNames:
7 | stages[name] = stage(name)
8 |
9 | return {
10 | 'version': '1',
11 | 'stages': stages
12 | }
13 |
14 | def stage(word):
15 | return {
16 | "steps": [
17 | {
18 | "name": "build_%s" % word,
19 | "image": "alpine:latest",
20 | 'commands': [
21 | "echo hello from %s" % word
22 | ]
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/basic_stages/want.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 |
3 | stages:
4 | foo:
5 | steps:
6 | - name: build_foo
7 | image: alpine:latest
8 | commands:
9 | - echo hello from foo
10 | needs: [ "clone" ]
11 | pull: not_present
12 |
13 | bar:
14 | steps:
15 | - name: build_bar
16 | image: alpine:latest
17 | commands:
18 | - echo hello from bar
19 | needs: [ "clone" ]
20 | pull: not_present
21 |
22 | star:
23 | steps:
24 | - name: build_star
25 | image: alpine:latest
26 | commands:
27 | - echo hello from star
28 | needs: [ "clone" ]
29 | pull: not_present
30 |
31 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/conditional/build.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | stepNames = ["foo", "bar", "star"]
3 |
4 | steps = []
5 |
6 | for name in stepNames:
7 | if name == "foo" and ctx["vela"]["build"]["branch"] == "master":
8 | steps.append(step(name))
9 |
10 | return {
11 | 'version': '1',
12 | 'steps': steps
13 | }
14 |
15 | def step(word):
16 | return {
17 | "name": "build_%s" % word,
18 | "image": "alpine:latest",
19 | 'commands': [
20 | "echo %s" % word
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/template/starlark/testdata/build/conditional/want.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | steps:
3 | - name: build_foo
4 | image: alpine:latest
5 | commands:
6 | - echo foo
7 |
--------------------------------------------------------------------------------
/template/starlark/testdata/step/basic/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: golang
5 | vars:
6 | image: golang:latest
7 | pull_policy: "pull: true"
--------------------------------------------------------------------------------
/template/starlark/testdata/step/basic/template.py:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | return {
3 | 'version': '1',
4 | 'steps': [
5 | {
6 | 'name': 'build',
7 | 'image': 'golang:latest',
8 | 'commands': [
9 | 'go build',
10 | 'go test',
11 | ]
12 | },
13 | ],
14 | }
--------------------------------------------------------------------------------
/template/starlark/testdata/step/basic/want.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | steps:
3 | - name: sample_build
4 | image: golang:latest
5 | commands:
6 | - go build
7 | - go test
--------------------------------------------------------------------------------
/template/starlark/testdata/step/cancel/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: echo
5 | vars:
6 | image: golang:latest
7 | pull_policy: "pull: true"
--------------------------------------------------------------------------------
/template/starlark/testdata/step/cancel/template.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | foo = '';
3 | for i in range(1, 100000):
4 | foo = foo
5 |
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_method/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: echo
5 | vars:
6 | image: golang:latest
7 | pull_policy: "pull: true"
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_method/template.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | return {
3 | 'version': '1',
4 | 'steps': [
5 | step('foo'),
6 | step('bar')
7 | ],
8 | }
9 |
10 | def step(word):
11 | return {
12 | "name": "build_%s" % word,
13 | "image": "alpine:latest",
14 | 'commands': [
15 | "echo %s" % word
16 | ]
17 | }
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_method/want.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | steps:
3 | - name: sample_build_foo
4 | image: alpine:latest
5 | commands:
6 | - echo foo
7 |
8 | - name: sample_build_bar
9 | image: alpine:latest
10 | commands:
11 | - echo bar
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: build
5 | vars:
6 | tags: [latest, "1.14", "1.15"]
7 | pull_policy: always
8 | commands:
9 | test: "go test ./..."
10 | build: "go test ./..."
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars/template.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | steps = [step(x, ctx["vars"]["pull_policy"], ctx["vars"]["commands"]) for x in ctx["vars"]["tags"]]
3 |
4 | pipeline = {
5 | 'version': '1',
6 | 'steps': steps,
7 | }
8 |
9 | return pipeline
10 |
11 | def step(tag, pull_policy, commands):
12 | return {
13 | "name": "build %s" % tag,
14 | "image": "golang:%s" % tag,
15 | "pull": pull_policy,
16 | 'commands': commands.values(),
17 | }
18 |
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars/want.yml:
--------------------------------------------------------------------------------
1 | version: "1"
2 | steps:
3 | - name: sample_build latest
4 | image: golang:latest
5 | pull: always
6 | commands:
7 | - go test ./...
8 | - go test ./...
9 |
10 | - name: sample_build 1.14
11 | image: golang:1.14
12 | pull: always
13 | commands:
14 | - go test ./...
15 | - go test ./...
16 |
17 | - name: sample_build 1.15
18 | image: golang:1.15
19 | pull: always
20 | commands:
21 | - go test ./...
22 | - go test ./...
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars_plat/step.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: sample
3 | template:
4 | name: echo
5 | vars:
6 |
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars_plat/template.star:
--------------------------------------------------------------------------------
1 | def main(ctx):
2 | return {
3 | 'version': '1',
4 | 'steps': [
5 | step(ctx["vela"]["repo"]["full_name"]),
6 | ],
7 | }
8 |
9 | def step(full_name):
10 | return {
11 | "name": "echo %s" % full_name,
12 | "image": "alpine:latest",
13 | 'commands': [
14 | "echo %s" % full_name
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/template/starlark/testdata/step/with_vars_plat/want.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | steps:
3 | - name: sample_echo octocat/hello-world
4 | image: alpine:latest
5 | commands:
6 | - echo octocat/hello-world
--------------------------------------------------------------------------------
/template/template.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Target Brands, Inc. All rights reserved.
2 | //
3 | // Use of this source code is governed by the LICENSE file in this repository.
4 |
5 | package template
6 |
7 | import "github.com/go-vela/types/yaml"
8 |
9 | // Engine represents the interface for Vela integrating
10 | // with the different supported template engines.
11 | type Engine interface {
12 | // RenderBuild defines a function that combines
13 | // the template with the build.
14 | RenderBuild(template string, step *yaml.Step) (yaml.StepSlice, error)
15 | // RenderStep defines a function that combines
16 | // the template with the step.
17 | RenderStep(template string, step *yaml.Step) (yaml.StepSlice, error)
18 | }
19 |
--------------------------------------------------------------------------------