Skip to content

Commit 4d163e0

Browse files
edigaryevfkorotkov
andauthored
Starlark documentation (#122)
* Starlark documentation Resolves #53. * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> * Update STARLARK.md Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com> Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
1 parent 3bab8e5 commit 4d163e0

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

STARLARK.md

+257
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Starlark
2+
3+
In addition to the [YAML configuration format](https://cirrus-ci.org/guide/writing-tasks/), Cirrus CLI supports evaluation of `.cirrus.star` scripts written in the Starlark language.
4+
5+
[Starlark](https://github.com/bazelbuild/starlark) is essentially a stripped-down dialect of Python. The major differences are explained in the [Bazel documentation](https://docs.bazel.build/versions/master/skylark/language.html).
6+
7+
Cirrus CLI embeds a Starlark interpreter and introduces the following additions:
8+
9+
* [entrypoints](#entrypoints)
10+
* [module loading](#module-loading)
11+
* [builtins](#builtins)
12+
13+
## Compatibility with the YAML format
14+
15+
When you execute `cirrus run`, it looks for the following files in the current directory:
16+
17+
* `.cirrus.yml`
18+
* `.cirrus.star`
19+
20+
While the first configuration is sent directly to the YAML parser, the second configuration is evaluated with the Starlark interpreter first, and only then the evaluation output is parsed as YAML.
21+
22+
You can also have both `.cirrus.yml` and `.cirrus.star` configurations at the same time — their output will be simply merged.
23+
24+
## Writing Starlark scripts
25+
26+
One of the most trivial `.cirrus.star` examples look like this:
27+
28+
```python
29+
def main(ctx):
30+
return [
31+
{
32+
"container": {
33+
"image": "debian:latest",
34+
},
35+
"script": "make",
36+
},
37+
]
38+
```
39+
40+
Once `run` in the Cirrus CLI, it will internally generate and parse the following YAML configuration to produce the actual tasks to run:
41+
42+
```yaml
43+
task:
44+
container:
45+
image: debian:latest
46+
script: make
47+
```
48+
49+
You might ask why not simply use the YAML format here? With Starlark, you can generate parts of the configuration dynamically based on some external conditions: by [making an HTTP request](#http) to check the previous build status or by [parsing files inside the repository](#fs) to pick up some common settings (for example, parse `package.json` to see if it contains `lint` script and generate a linting task).
50+
51+
And even more importantly: with the [module loading](#module-loading) you can re-use other people's code to avoid wasting time on things written from scratch. For example, there are official [task helpers](https://github.com/cirrus-templates/helpers) available that reduce the boilerplate when generating tasks:
52+
53+
```python
54+
load("github.com/cirrus-templates/helpers", "task", "container", "script")
55+
56+
def main(ctx):
57+
return [
58+
task(instance=container("debian:latest"), instructions=[script("make")]),
59+
]
60+
```
61+
62+
## Entrypoints
63+
64+
Different events will call different top-level functions in the `.cirrus.star`. These functions reserve certain names and will be called with different arguments depending on an event which triggered the execution.
65+
66+
Currently only build generation entrypoint is supported with more to come in the future. For example, there will be a way to declare a function to be called on a task failure to analyze logs and if necessary re-run the task automatically.
67+
68+
### Build generation
69+
70+
Entrypoint: `main(ctx)`
71+
72+
Cirrus CLI will call this function in the `.cirrus.star` when being executed as `cirrus run` to retrieve a list of tasks to run.
73+
74+
Arguments:
75+
76+
* `ctx` — reserved for future use
77+
78+
Return value:
79+
80+
* a list of dicts, where each dict closely represents a task in the YAML configuration format
81+
82+
## Module loading
83+
84+
Module loading is done through the Starlark's [`load()`](https://github.com/bazelbuild/starlark/blob/master/spec.md#load-statements) statement.
85+
86+
Besides the ability to load [builtins](#builtins) with it, Cirrus CLI can load other `.star` files from local and remote locations to facilitate code re-use.
87+
88+
### Local
89+
90+
Local loads are relative to the project's root (where `.cirrus.star` is located):
91+
92+
```python
93+
load(".ci/notify-slack.star", "notify_slack")
94+
```
95+
96+
### Remote from Git
97+
98+
To load a specific branch of the template from GitHub:
99+
100+
```python
101+
load("github.com/cirrus-templates/golang@master", "task", "container")
102+
```
103+
104+
In the example above, the name of the `.star` file was not provided, because `lib.star` is assumed by default. This is equivalent to:
105+
106+
```python
107+
load("github.com/cirrus-templates/golang/lib.star@master", "task", "container")
108+
```
109+
110+
You can also specify an exact commit hash instead of the `master` branch name to prevent accidental changes.
111+
112+
To load `.star` files from repositories other than GitHub, add a `.git` suffix at the end of the repository name, for example:
113+
114+
```python
115+
load("gitlab.com/fictional/repository.git/validator.star", "validate")
116+
^^^^ note the suffix
117+
```
118+
119+
## Builtins
120+
121+
Cirrus CLI provides builtins all nested in the `cirrus` module that greatly extend what can be done with the Starlark alone.
122+
123+
### `fs`
124+
125+
These builtins allow for read-only filesystem access.
126+
127+
All paths are relative to the project's directory.
128+
129+
#### `fs.exists(path)`
130+
131+
Returns `True` if `path` exists and `False` otherwise.
132+
133+
#### `fs.read(path)`
134+
135+
Returns a [`string`](https://github.com/bazelbuild/starlark/blob/master/spec.md#strings) with the file contents or `None` if the file doesn't exist.
136+
137+
Note that this is an error to read a directory with `fs.read()`.
138+
139+
#### `fs.readdir(dirpath)`
140+
141+
Returns a [`list`](https://github.com/bazelbuild/starlark/blob/master/spec.md#lists) of [`string`'s](https://github.com/bazelbuild/starlark/blob/master/spec.md#strings) with names of the entries in the directory.
142+
143+
Note that this is an error to read a file with `fs.readdir()`.
144+
145+
Example:
146+
147+
```python
148+
load("cirrus", "fs")
149+
150+
def main(ctx):
151+
tasks = base_tasks()
152+
153+
if fs.exists("go.mod"):
154+
tasks += go_tasks()
155+
156+
return tasks
157+
```
158+
159+
### `env`
160+
161+
While not technically a builtin, `env` is dict that contains environment variables passed via `cirrus run --environment`.
162+
163+
Example:
164+
165+
```python
166+
load("cirrus", "env")
167+
168+
def main(ctx):
169+
tasks = base_tasks()
170+
171+
if env.get("CIRRUS_TAG") != None:
172+
tasks += release_tasks()
173+
174+
return tasks
175+
```
176+
177+
### `http`
178+
179+
Provides HTTP client implementation with `http.get()`, `http.post()` and other HTTP method functions.
180+
181+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/http) for more details.
182+
183+
### `hash`
184+
185+
Provides cryptographic hashing functions, such as `hash.md5()`, `hash.sha1()` and `hash.sha256()`.
186+
187+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/hash) for more details.
188+
189+
### `base64`
190+
191+
Provides Base64 encoding and decoding functions using `base64.encode()` and `base64.decode()`.
192+
193+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/encoding/base64) for more details.
194+
195+
### `json`
196+
197+
Provides JSON document marshalling and unmarshalling using `json.dumps()` and `json.loads()` functions.
198+
199+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/encoding/json) for more details.
200+
201+
### `yaml`
202+
203+
Provides YAML document marshalling and unmarshalling using `yaml.dumps()` and `yaml.loads()` functions.
204+
205+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/encoding/yaml) for more details.
206+
207+
### `re`
208+
209+
Provides regular expression functions, such as `findall()`, `split()` and `sub()`.
210+
211+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/re) for more details.
212+
213+
### `zipfile`
214+
215+
`cirrus.zipfile` module provides methods to read Zip archives.
216+
217+
You instantiate a `ZipFile` object using `zipfile.ZipFile(data)` function call and then call `namelist()` and `open(filename)` methods to retrieve information about archive contents.
218+
219+
Refer to the [starlib's documentation](https://github.com/qri-io/starlib/tree/master/zipfile) for more details.
220+
221+
Example:
222+
223+
```python
224+
load("cirrus", "fs", "zipfile")
225+
226+
def is_java_archive(path):
227+
# Read Zip archive contents from the filesystem
228+
archive_contents = fs.read(path)
229+
if archive_contents == None:
230+
return False
231+
232+
# Open Zip archive and a file inside of it
233+
zf = zipfile.ZipFile(archive_contents)
234+
manifest = zf.open("META-INF/MANIFEST.MF")
235+
236+
# Does the manifest contain the expected version?
237+
if "Manifest-Version: 1.0" in manifest.read():
238+
return True
239+
240+
return False
241+
```
242+
243+
## Security
244+
245+
### Remote loads
246+
247+
Cirrus CLI always uses HTTPS to fetch files from Git.
248+
249+
### Builtins
250+
251+
While builtins provide functionality that is considered non-altering to the local system, there are some cases when this may not be enough:
252+
253+
* `cirrus.fs` methods can traverse upwards the project root and read files and folders there
254+
255+
* `cirrus.http` methods can access local services running on `127.0.0.1` or inside of LAN and potentially interact with the services running on these hosts in malicious ways
256+
257+
It's recommended that you don't run Starlark scripts from potentially untrusted sources, similarly to how you probably wouldn't run build scripts from random repositories found on the internet.

0 commit comments

Comments
 (0)