Grog Scripts
What are Grog Scripts?
Grog Scripts let you promote existing automation scripts to first-class build targets without having to add them as targets with bin_output
to your BUILD files (more below).
They combine lightweight metadata with the standard grog run
workflow so you can cache results, share helpers across teams, and keep using your preferred languages.
- Store scripts directly in your repository with names ending in
.grog.sh
or.grog.py
. - Add a
# @grog
metadata block at the top of the file to describe dependencies, inputs, and outputs just like annotated Makefile targets. - Run them anywhere in the workspace with
grog run path/to/script.grog.sh
(or the Python equivalent) while passing through arguments with--
.
This approach avoids scattering wrapper targets across BUILD files and keeps the execution logic beside the script that implements it.
Script discovery and invocation
Grog scans your workspace for executable files that match the naming convention *.grog.sh
or *.grog.py
and treats each match as a runnable tool.
Because the loader already understands annotated Makefile targets, the same # @grog
block is used to declare script metadata, keeping configuration consistent across formats.【F:docs/src/content/docs/get-started.mdx†L145-L175】【F:internal/loading/makefile_loader.go†L69-L151】
You can run a script from any directory with either a relative or absolute path.
Grog automatically builds any declared dependencies, restores cached outputs, and then executes the file using sh
.
.py
files therefore need a shebang to tell Python which interpreter to use.
Use --
to forward additional parameters to the script.
# Run a shell script directlygrog run scripts/format.grog.sh -- --check
# Explicitly run from the repo rootgrog run ./tools/release.grog.py -- --version 1.2.0
Annotating scripts with # @grog
A script becomes runnable once you add a comment header that starts with # @grog
.
All subsequent comment lines (prefixed with #
) are parsed as YAML until the first non-comment line, mirroring the Makefile loader.【F:internal/loading/makefile_loader.go†L69-L151】
The metadata keys are:
Key | Required | Description |
---|---|---|
name | Optional | Overrides the default target name that Grog derives from the file name. |
dependencies | Optional | A list of labels for other targets that must finish before the script executes. |
Scripts without a name
inherit their filename, so tools/script.grog.sh
can still be addressed as //tools:script.grog.sh
if you prefer to run it via a label.
Keep metadata close to the script so reviewers can understand its behavior at a glance.
Example: Shell script
#!/usr/bin/env bash# @grog# name: format# dependencies:# - //tools:install_prettierset -euo pipefail
# Format JavaScript and TypeScript filesprettier --write "src/**/*.{js,ts}" "$@"
chmod +x scripts/format.grog.sh# Run the script using the file namegrog run scripts/format.grog.sh -- --check# Run the script using the target labelgrog run //scripts:format -- --check
This script ensures the install_shfmt
tool is built first, tracks the files it reads, and records generated reports so they can be cached across builds.
Example: Python script
#!/usr/bin/env python3# @grog# name: test# dependencies:# - //tools:pytest
import subprocess
# Run unit tests with coverage reportsubprocess.run([ "pytest", "--cov=src", "--cov-report=html", "tests/",], check=True)
chmod +x scripts/smoke_tests.grog.py# Run the script using the file namegrog run scripts/smoke_tests.grog.py -- --maxfail=1# Run the script using the target labelgrog run //scripts:custom_name -- --maxfail=1
Python scripts follow the same rules: metadata stays in comments, the interpreter derives from the shebang, and --
cleanly forwards additional arguments to the script.
Dependency and caching semantics
The metadata-driven fields determine how Grog orchestrates script execution.
Dependencies make sure upstream targets are complete before the script runs, while inputs and outputs inform cache keys and restore behavior—just as they do for annotated Makefile targets.【F:internal/loading/makefile_loader.go†L103-L151】
This uniform treatment means scripts participate fully in commands like grog build
, grog changes
, and query subcommands without extra configuration.【F:docs/src/content/docs/guides/querying.mdx†L74-L183】
Comparison to binary output targets
Before this feature you would have to define a wrapper target that defines the existing script file as an executable target with a bin_output
like so:
amends "package://grog.build/releases/v0.16.1/grog@0.16.1#/package.pkl"
targets { new { name = "your_script" bin_output = "script_file.sh" tags { "no-cache" } }}
While this is less convenient and arguably a bit awkward, since there is no actual build step, it can still be useful in case you want to keep your build configuration separater from your scripts.