- Scheme 72.2%
- Shell 18.9%
- Makefile 6.8%
- C 1.8%
- Common Lisp 0.3%
| .builds | ||
| .jerboa | ||
| docs | ||
| lib/jgl | ||
| scripts | ||
| support | ||
| test | ||
| .gitignore | ||
| .jerbuild | ||
| AGENTS.md | ||
| build-all.ss | ||
| build-jgl-cross.ss | ||
| jgl.ss | ||
| jpkg.lock | ||
| jpkg.policy.sexp | ||
| jpkg.sexp | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| SECURITY.md | ||
jerboa-gitlab — jgl
A small GitLab CLI written in Jerboa: fetch
issues, query CI/CD, and archive issues to markdown. It talks to the GitLab
REST API v4 over HTTPS (Rustls, via the bundled jerboa-native crate).
Inspired by gitlab-issue-exporter
(a Go issue-archiver); jgl keeps the markdown-export idea and adds CI/CD
queries and a JSON output mode.
What it does
- Issues — list (with state/label/search filters), show one issue with its comments, and export every issue to one markdown file per issue.
- CI/CD — list recent pipelines, show a pipeline and its jobs, list recent jobs, and report the latest pipeline status for a ref.
- Project — show project metadata (a quick auth/connectivity check).
- Output — human-readable text (default) or raw JSON (
--format json).
Build
jgl builds with just jerbuild — it bundles
Chez Scheme, the Jerboa stdlib, and the jerboa-native Rust crate (Rustls
HTTPS). You need jerbuild, a C compiler, and cargo.
make binary # -> ./jgl
make install # -> ~/.local/bin/jgl
make test # run the unit tests
make release-evidence
The same make binary works natively on macOS, Linux, and FreeBSD. To
cross-compile from macOS to other targets, see Cross-compilation.
Usage
jgl <command> [options]
| Command | Description |
|---|---|
project |
Show project info (verifies url/token/project) |
issues |
List issues |
issue <iid> |
Show one issue with comments |
export |
Export all issues to markdown (--output DIR) |
pipelines |
List recent CI/CD pipelines |
pipeline <id> |
Show a pipeline and its jobs |
jobs |
List recent jobs (--pipeline ID to scope) |
ci-status |
Latest pipeline status (--ref to scope) |
| Option | Description |
|---|---|
--url URL |
GitLab base URL (env GITLAB_URL, default https://gitlab.com) |
--token-fd FD |
Read an access token from a dedicated descriptor (GITLAB_TOKEN also supported) |
--trusted-origin O |
Required exact trust assertion for authenticated custom GitLab origins |
--allow-insecure-loopback |
Permit HTTP only for literal 127.0.0.1 development |
-p, --project P |
Project ID or path, e.g. 123 or group/proj (env GITLAB_PROJECT) |
-f, --format FMT |
text (default) or json |
--state STATE |
issues: opened | closed | all (default all) |
--labels A,B |
issues: filter by labels |
--search TEXT |
issues: search term |
--ref REF |
pipelines/ci-status: branch or tag |
--status STATUS |
pipelines: filter by status |
--pipeline ID |
jobs: restrict to a pipeline |
-o, --output DIR |
export: output directory (default ./issues) |
--config PATH |
Config file (default ~/.gitlab.yaml; also GITLAB_CONFIG) |
-h, --help |
Show help |
--version |
Show version |
A token with read_api scope is needed for private projects; public projects
read without one.
Security
jgl can read private GitLab project metadata, issues, comments, CI status, and
attachments. Prefer a read_api token; token arguments are rejected, so use a
secret-manager environment injection, a dedicated descriptor, or an owner-only
config. Treat jgl export output as sensitive. Credential and export
handling are documented in docs/credential-handling.md.
Native dependency provenance is documented in
docs/dependency-provenance.md, and local
release evidence is documented in
docs/release-evidence.md.
Configuration
jgl resolves each setting with the precedence flag › environment ›
~/.gitlab.yaml › default. Use whichever fits:
export GITLAB_URL="https://gitlab.com"
export GITLAB_TOKEN="glpat-xxxxxxxxxxxx"
export GITLAB_PROJECT="group/project"
or a flat ~/.gitlab.yaml (override the path with --config or GITLAB_CONFIG):
url: https://gitlab.com
token: glpat-xxxxxxxxxxxx
project: group/project
format: text
An existing config must be a canonical, non-symlink, owner-only regular file
(normally mode 0600). Authenticated custom instances must also set an exact
trusted_origin, for example https://gitlab.example:443. URLs use HTTPS;
plaintext HTTP is available only with --allow-insecure-loopback for literal
127.0.0.1 development.
Examples
# Issues (filters, JSON)
jgl issues -p group/proj --state opened --labels bug
jgl issue 42 -p group/proj
jgl issues -p group/proj -f json
# CI/CD
jgl pipelines -p group/proj --ref main
jgl pipeline 123456 -p group/proj
jgl ci-status -p group/proj --ref main
# Archive every issue to markdown
jgl export -p group/proj -o ./archive
Markdown export
jgl export writes one directory per issue:
issues/
├── issue-1/
│ ├── README.md
│ └── attachments/ (referenced /uploads/ files, best-effort)
└── issue-2/
└── README.md
Each README.md has the title, metadata (state, author, assignees, labels,
dates, URL), the description, and non-system comments. When the project is given
as a path (group/proj), referenced /uploads/ attachments are downloaded into
attachments/ and the links are rewritten to point at the local copies; if a
download fails the original link is left intact. Issue directory names and
attachment filenames derived from API responses are sanitized before local
filesystem writes. Export directories are descriptor-walked without following
symlinks, created mode 0700, and files are created exclusively at mode 0600;
an existing destination is never overwritten. Human-readable terminal output
escapes control/bidirectional characters. JSON and markdown exports remain
lossless representations of the project content.
Cross-compilation
make binary is portable — it runs natively on macOS, Linux, and FreeBSD. To
build a Linux x86_64 binary from macOS:
make cross-linux # -> jgl-linux-amd64 (static musl ELF)
jgl's HTTPS uses rustls (the jerboa-native crate), so this cross-builds that
crate for x86_64-unknown-linux-musl and static-links it — the same TLS
backend as the native build (not the OpenSSL shim some older jerboa-* cross
scripts use). It needs:
- a full Chez
schemecompiler — the Makefile falls back to$JERBOA_HOME/build/chez/tarm64osx/bin/tarm64osx/schemeif none is on PATH; - the cross-built Chez + xpatch under
$JERBOA_HOME(.chez-cross-ta6le,build/chez/xc-ta6le/s/xpatch); x86_64-linux-musl-gcc;- a rustup toolchain with the musl target (
rustup target add x86_64-unknown-linux-musl).
Override JERBOA_HOME, SCHEME, RUSTUP_TC, or MUSL_CC on the make line as
needed; see build-jgl-cross.ss for the details. (A FreeBSD cross would follow
the same shape with the x86_64-unknown-freebsd Rust target + a sysroot; not
wired up yet.)
Layout
jgl.ss entry point (argv via env or command line)
lib/jgl/client.ss HTTPS + JSON + pagination + percent-encoding
lib/jgl/config.ss resolve url/token/project from flags + env
lib/jgl/secure-fs.ss owner/mode checks + descriptor-relative exports
lib/jgl/format.ss field access, JSON re-serialization, formatters
lib/jgl/issues.ss issue list/get/notes + markdown export
lib/jgl/ci.ss pipelines / jobs / latest status
lib/jgl/cli.ss argument parsing, dispatch, rendering
test/test-jgl.ss policy/format/path unit tests
test/test-secure-fs.ss no-follow/private/exclusive filesystem tests
License
Apache-2.0.