AndaX
AndaX is a port of the rhai (opens in a new tab) scripting language embedded into Andaman.
In short, AndaX is rhai but with extra functions and modules. These functions
make AndaX a very viable choice for pre/post scripts, performing automatic
updates (anda update
) or just scripting in general.
For documentations, playground and built-in functions in rhai (opens in a new tab), check out their website.
This page contains documentation for additional functions and quirks in AndaX.
;
)Running AndaX scripts
To execute a script, use the anda run myscript.rhai
command:
$ anda run --help
Run .rhai scripts
Usage: anda run [OPTIONS] [SCRIPTS]...
Arguments:
[SCRIPTS]...
Options:
-l, --labels <LABELS>
-v, --verbose... Increase logging verbosity
-q, --quiet... Decrease logging verbosity
-h, --help Print help
The --labels
flag can be used to pass in labels. For more information, read
the section Working with Labels.
As expected, the shebang #!anda run
may be used.
Project Scripts
Some scripts that exist in the same directory as anda.hcl
will be executed
during anda build
. The path to these scripts can be modified in anda.hcl
:
project "project_name" {
// these are default values. Even when not given in the config file, anda will
// run them if they just exist in the same directory as the config.
pre_script = "pre.rhai"
post_script = "post.rhai"
rpm {
pre_script = "rpm_pre.rhai"
post_script = "rpm_post.rhai"
}
// this script is not executed during `anda build`.
// update = "update.rhai"
}
However, if these options are modified and the extension does not end with
.rhai
, anda will run these files using sh -c path/to/file
.
For more information about project.*.update
(update.rhai
), check out the
Autoupdate section.
For more information about the anda.hcl
configuration file, check out the
Configuration page.
Special Constants
const USER_AGENT = "AndaX";
const IS_WIN32 = false; // `cfg!(windows)` in rust
const ANDAX_VER = "0.1.3"; // `env!("CARGO_PKG_VERSION")` in rust
Custom Functions
Networking
// `get(link)` is basically a built-in `curl`
let html_content = get("https://fyralabs.com");
// if you want more control over how the request can be made:
let req = new_req("https://fyralabs.com");
req.redirects(max_number_of_redirections_allowed);
req.head("Content-Type", "application/json");
let html_result = req.get();
// github-related version tracking functions
let latest_ver = gh("group/repo_name");
// for gh repos with tags but not releases, use gh_tag() instead:
let latest_tag = gh_tag("group/repo_name");
let latest_commit = gh_commit("group/repo_name");
// "main" refers to the branch here
let raw_readme_content = gh_rawfile("group/repo_name", "main", "README.md");
// the above gh functions are also available for gitlab, just replace `gh` with
// `gitlab`, and replace group/repo_name with repoid
// you can also specify the custom domain as the first argument:
// special: need to specify branch for gitlab commits
print(gitlab_commit("gitlab.gnome.org", "1551", "main"));
print(gitlab_tag("25716028"));
// WARN: there is no gitlab_rawfile().
// others
let pypi_latest = pypi("example");
let max_stable_version = crates("anda");
let max_version = crates_max("anda");
let newest_version = crates_newest("anda");
let npm_latest = npm("example");
JSON Operations
let obj = json(`{"a": 1}`);
print(obj.a); // 1
print(obj["a"]); // 1
let arr = json_arr("[1,2,3]");
print(arr[1]); // 2
let str = to_json(obj);
let back_to_map_obj = from_json(str);
Maps (opens in a new tab) are also known as dictionaries in some other languages (like Python).
Regex Operations
let found = find("(\\d{3})", "abc1c2345", 1); // gets regex group 1
print(found); // 234
let substituted = sub("(\\s+)", " ", "I hate spaces !!!");
print(substituted); // I hate spaces !!!
IO and Commands
sh(command, opts)
command
You can supply command
as a string or an array of strings.
If command
is a string, sh -c <command>
will be used.
Otherwise it'll just call command[0]
with the arguments command[1..]
.
opts
opts
is an rhai::Map
with the following key-value pairs: (all optional)
stdout
: possible values are "inherit" (default), "null" and "piped".stderr
: possible values are "inherit" (default), "null" and "piped".cwd
: path of current working directory as a string
Errors
If you didn't use the function correctly, you will receive the following error:
#{
"outcome": "fatal",
"ctx": #{
"kind": "bad_param_type", // or: "empty_cmd_arr" | "bad_stdio_opt" | "bad_stdio_type"
"expect": "…", // expected type
"found": "…", // actual type received
},
}
If you did use the function correctly but there are some problems running the command:
(Note: this doesn't include cases where the command itself returns a non-zero exit code)
#{
"outcome": "failure",
"ctx": #{
"error": "…", // error message from the rust lib / operating system / idk
},
}
Otherwise, the function will return this:
#{
"outcome": "success",
"ctx": #{
"stdout": "…",
"stderr": "…",
"rc": 0, // or other integers (status code)
},
}
Examples
// shows most likely 0, and prints out stdout to console in real time, so you will see `hai` then `0`
print(sh("echo hai", #{}).ctx.rc);
// shows `hai`
print(sh(["echo", "hai"], #{ "stdout": "piped" }).ctx.stdout);
// prints out the command output in real time, then: `#{ "outcome": "success", "ctx": … }`
print(sh("ls -alh", #{ "cwd": "anda/tools/umstellar/" }));
ls(dir)
Returns an array of strings (files, folders inside the given dir)
Examples
for x in ls("/") {
if x == "bin" {
print("I found the `/bin` folder!");
}
}
write(text, filepath)
Write text
into filepath
, overwriting existing content.
let foo = "bar";
foo.write("bar.txt"); // counterintuitive but the string "bar" is written into the file "bar.txt"
obj.write("tmp.json"); // object (maps) are turned into JSON automatically
rhai-fs
Visit the rhai book (opens in a new tab) for the
documentation for rhai-fs
.
Others
// === Templates ===
print(template(#{a: "value", b: "hai"}, "%{a} and %{b} @{random_rpm_spec_macro}"));
// value and hai %{random_rpm_spec_macro}
// Note that @{} are used to represent rpm macros (they unfortunate conflict)
// `@{` are converted to `%{` automatically.
print(template_file(#{...}, "path/to/a.hcl"));
// === RPMBuild ===
// use these functions with sh() :3
anda::rpmbuild::cmd_srpm("test.spec", "sources"); // ["mock", "--buildsrpm", "--spec", "test.spec", "--sources", "sources", ...];
anda::rpmbuild::cmd_rpm("test.spec", "sources"); // ["mock", "--rebuild", ...];
// === Anda-Config ===
let cfg_map_obj = anda::cfg::load("path/to/anda.hcl");
// === Environment Variables ===
let env_val = env("KEY");
Working with Labels
Labels are one of the ways you may pass in runtime variables into the script.
Think of awk -v var=val
/ awk --assign=var=val
/ nim --define:var=val
.
To pass in labels, provide them as a comma-separated list to --labels
.
A label is a key-value entry, joined together using =
. In the script, labels
can be accessed using the labels
variable (a map):
// assume run with `anda run myscript.rhai --labels a=1,b=2,c=x=y --labels d=hai`
print(labels["a"]); // 1
print(labels.b); // 2
print(labels.get("c")); // x=y
print(labels.remove("d")); // hai
print(labels.get("d") == ()); // true
Autoupdate
Andaman comes with an autoupdate system, readily available via anda update
:
$ anda update --help
Update all projects
Usage: anda update [OPTIONS]
Options:
-l, --labels <LABELS>
Labels to pass to the scripts
-f, --filters <FILTERS>
Only run update scripts in project with the specified labels
This should be a comma-separated list of filters. Each time `--filters=...` is specified,
the comma-separated list of key-values will be checked against a project. If missing or
different, the project will be ignored. However, specifying `--filters` multiple times
will create an "or" effect --- the project will not be ignored if it satisfies one of the
list of `--filters`. For example, `-f a=1,b=2 -f c=3` means the project needs to satisfy
either "a=1" and "b=2", or only "c=3".
-e, --excludes <EXCLUDES>
Exclude update scripts in project with the specified labels
This should be a comma-separated list of excludes. Each time `--exclude=...` is specified,
the comma-separated list of key-values will be checked against the labels of a project,
and it will be ignored if all the key-values are present. In addition, specifying
`--exclude` multiple times will create an "or" effect --- a project will be excluded if it
satisfies one of the list of `--filters`. For example, `-e a=1,b=2 -e c=3` means projects
with "a=1" and "b=2" at the same time, or "c=3", are excluded. Projects with only "a=1" or
"b=2" are not excluded.
This will always override `--filters`.
-v, --verbose...
Increase logging verbosity
-q, --quiet...
Decrease logging verbosity
-h, --help
Print help (see a summary with '-h')
The --labels
flag can be used to pass in labels. For more information, read
the section Working with Labels.
anda update
execute all update scripts found in the repository recursively as
defined by the project.*.update
entry in anda.hcl
separately:
project "project_name" {
// the default value is "update.rhai"
update = "my_update_script.rhai"
}
Additionally, the rpm
object is provided under anda update
:
// note that the `rpm` object is only available in `anda update`, but not `anda run`.
rpm.version(latest_ver); // note that version() resets the release back to 1 automatically
// Source0: https://github.com/FyraLabs/anda/archive/refs/tags/0.1.17.tar.gz
rpm.source(0, "https://github.com/FyraLabs/anda/archive/refs/tags/0.1.17.tar.gz");
// %define abc hai bai
rpm.define("abc", "hai bai");
// %global def give rabonuko a headpat!
rpm.global("def", "give rabonuko a headpat!");
rpm.release(); // resets release to 1: `Release: 1%?dist`
rpm.release(3); // Release: 3%?dist
let spec_content = rpm.f;
rpm.f = new_spec_content;
// returns boolean that determine if the spec content is changed
rpm.changed();