blob: 75b48afeacfb55f05666eb2cc3f0da3e3d60f7ad [file] [log] [blame] [view]
# shac runtime standard library
shac uses the starlark language. Starlark is a python derivative.
https://bazel.build/rules/language is a great resource if the language is new to
you, just ignore the bazel references. The starlark language formal
specification is documented at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md.
While all [starlark-go's built-in constants and functions are
available](https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#built-in-constants-and-functions),
a few are explicitly documented here to highlight them.
These [experimental
features](https://pkg.go.dev/go.starlark.net/resolve#pkg-variables) are enabled:
- AllowSet: "set" built-in is enabled.
- AllowRecursion: allow while statements and recursion. This allows potentially
unbounded runtime.
Note: The shac runtime standard library is implemented in native Go.
## Table of contents
- [ctx](#ctx)
- [dir](#dir)
- [fail](#fail)
- [json](#json)
- [load](#load)
- [print](#print)
- [register_check](#register_check)
- [struct](#struct)
## ctx
ctx is the object passed to register_check(...) callback.
Fields:
- exec
- io
- re
- scm
## ctx.io
ctx.io is the object that exposes the API to interact with the file system.
Fields:
- read_file
## ctx.io.read_file
Returns the content of a file.
### Example
```python
def cb(ctx):
content = str(ctx.io_read_file("path/to/file.txt"))
# Usually run a regexp via ctx.re.match(), or other simple text
# processing.
print(content)
register_check(cb)
```
### Arguments
* **path**: path of the file to read. The file must be within the workspace. The path must be relative and in POSIX format, using / separator.
### Returns
Content of the file as bytes.
## ctx.re
ctx.re is the object that exposes the API to run regular expressions on
starlark strings.
Fields:
- allmatches
- match
## ctx.re.allmatches
Returns all the matches of the regexp pattern onto content.
### Example
```python
def cb(ctx):
content = str(ctx.io_read_file("path/to/file.txt"))
for match in ctx.re.allmatches("TODO\(([^)]+)\).*", content):
print(match)
register_check(cb)
```
### Arguments
* **pattern**: regexp to run. It must use the syntax as described at https://golang.org/s/re2syntax.
* **str**: string to run the regexp on.
### Returns
list(struct(offset=bytes_offset, groups=list(matches)))
## ctx.re.match
Returns the first match of the regexp pattern onto content.
### Example
```python
def cb(ctx):
content = str(ctx.io_read_file("path/to/file.txt"))
# Only print the first match, if any.
match = ctx.re.match("TODO\(([^)]+)\).*", "content/true")
print(match)
register_check(cb)
```
### Arguments
* **pattern**: regexp to run. It must use the syntax as described at https://golang.org/s/re2syntax.
* **str**: string to run the regexp on.
### Returns
struct(offset=bytes_offset, groups=list(matches))
## ctx.scm
ctx.scm is the object exposes the API to query the source control
management (e.g. git).
Fields:
- affected_files
- all_files
## ctx.scm.affected_files
Returns affected files as determined by the SCM.
If shac detected that the tree is managed by a source control management
system, e.g. git, it will detect the upstream branch and return only the files
currently modified.
If the current directory is not controlled by a SCM, the result is equivalent
to ctx.scm.all_files().
If shac is run with the --all options, all files are considered "added" to do
a full run on all files.
### Example
```python
def new_todos(cb):
# Prints only the TODO that were added compared to upstream.
for path, meta in ctx.scm.affected_files().items():
for num, line in meta.new_lines():
m = ctx.re.match("TODO\(([^)]+)\).*", line)
print(path + "(" + str(num) + "): " + m.groups[0])
register_check(new_todos)
```
### Arguments
* **glob**: TODO: Will later accept a glob.
### Returns
A map of {path: struct()} where the struct has a string field action and a
function new_line().
## ctx.scm.all_files
Returns all files found in the current workspace.
It considers all files "added".
### Example
```python
def all_todos(cb):
for path, meta in ctx.scm.all_files().items():
for num, line in meta.new_lines():
m = ctx.re.match("TODO\(([^)]+)\).*", line)
print(path + "(" + str(num) + "): " + m.groups[0])
register_check(all_todos)
```
### Arguments
* **glob**: TODO: Will later accept a glob.
### Returns
A map of {path: struct()} where the struct has a string field action and a
function new_line().
## ctx.exec
Runs a command as a subprocess.
### Example
```python
def cb(ctx):
if ctx.exec("echo", "hello world", cwd="."):
fail("echo failed")
register_check(cb)
```
### Arguments
* **cmd**: Subprocess command line.
* **cwd**: Relative path to cwd for the subprocess.
### Returns
An integer corresponding to the subprocess exit code.
## dir
Starlark builtin that returns all the attributes of an object.
Primarily used to explore and debug a starlark file.
See the official documentation at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#dir.
### Example
```python
def print_attributes(name, obj):
for attrname in dir(obj):
attrval = getattr(obj, attrname)
attrtype = type(attrval)
fullname = name + "." + attrname
if attrtype in ("builtin_function_or_method", "function"):
print(fullname + "()")
elif attrtype == "struct":
print_attributes(fullname, attrval)
else:
print(fullname + "=" + repr(attrval))
def cb(ctx):
print_attributes("ctx", ctx)
print_attributes("str", "")
print_attributes("dict", {})
print_attributes("set", set())
print_attributes("struct", struct(foo = "bar", p = print_attributes))
register_check(cb)
```
### Arguments
* **x**: object that will have its properties enumerated.
### Returns
list of x object properties as strings. You can use getattr() to retrieve
each attributes in a loop.
## fail
Starlark builtin that fails immediately the execution.
This function should not be used normally. It can be used as a quick debugging
tool or when there is an irrecoverable failure that should immediately stop
all execution.
See the official documentation at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#fail.
### Example
```python
fail("implement me")
```
### Arguments
* **\*args**: arguments to print out.
* **sep**: separator between the items in args, defaults to " ".
## json
json is a global module that exposes json functions.
The documentation here is listed as a struct instead of a module. The two are
functionally equivalent.
The implementation matches the official bazel's documentation at
https://bazel.build/rules/lib/json except that encode_indent is not
implemented.
Fields:
- decode
- encode
- indent
## json.decode
Decodes a JSON encoded string into the Starlark value that the string
denotes.
Supported types include null, bool, int, float, str, dict and list.
See the full documentation at https://bazel.build/rules/lib/json#decode.
### Example
```python
data = json.decode('{"foo":"bar}')
print(data["foo"])
def cb(ctx):
# Load a configuration from a json file in the tree, containing a
# dict with a "version" key.
decoded = ctx.io.read_file("config.json")
print(decoded["version"])
register_check(cb)
```
### Arguments
* **x**: string or bytes of JSON encoded data to convert back to starlark.
## json.encode
Encodes the starlark value into a JSON encoded string.
Supported types include null, bool, int, float, str, dict, list and struct.
See the full documentation at https://bazel.build/rules/lib/json#encode.
### Example
```python
config = struct(
foo = "bar",
)
print(json.encode(config))
```
### Arguments
* **x**: starlark value to encode to a JSON encoded string.
## json.indent
Returns the indented form of a valid JSON-encoded string.
See the full documentation at https://bazel.build/rules/lib/json#indent.
### Example
```python
config = struct(
foo = "bar",
)
d = json.encode(config)
print(json.indent(d))
```
### Arguments
* **x**: string or bytes of JSON encoded data to reformat.
* **prefix**: prefix for each new line.
* **indent**: indent for nested fields.
## load
Starlark builtin that loads an additional shac starlark package and make
symbols (var, struct, functions) from this file accessible.
See the official documentation at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#name-binding-and-variables
and at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#load-statements.
After a starlark module is loaded, its values are frozen as described at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#freezing-a-value.
### Example
```python
load("go.star", "gosec")
def _gosec(ctx):
# Use a specific gosec version, instead of upstream's default version.
gosec(ctx, version="v2.9.6")
register_checks(_gosec)
```
### Arguments
* **module**: path to a local module to load. In the future, a remote path will be allowed.
* **\*symbols**: symbols to load from the module.
* **\*\*kwsymbols**: symbols to load from the module that will be accessible under a new name.
## print
Starlark builtin that prints a debug log.
This function should only be used while debugging the starlark code.
See the official documentation at
https://github.com/google/starlark-go/blob/HEAD/doc/spec.md#print.
### Example
```python
print("shac", "is", "great")
```
### Arguments
* **args**: arguments to print out.
* **sep**: separator between the items in args, defaults to " ".
## register_check
Registers a shac check.
It must be called at least once for the starlark file be a valid check file.
Each callback will be run in parallel.
### Example
```python
def cb(ctx):
fail("implement me")
register_check(cb)
```
### Arguments
* **cb**: Starlark function that is called back to implement the check. Passed a single argument ctx(...).
## struct
Creates and return a structure instance.
This a non-standard function that enables creating an "object" that has
immutable properties. It is intentionally not as powerful as a python class
instance.
### Example
```python
def _do():
print("it works")
obj = struct(
value = "a value",
do = _do,
)
print(obj.value)
obj.do()
```
### Arguments
* **\*\*kwargs**: structure's fields.