~ober/jerboa-gitlab
Imported from ~/mine/jerboa-gitlab
about
# jerboa-gitlab — `jgl`
A small GitLab CLI written in [Jerboa](https://git.jerboa.sh/ober/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`](https://github.com/ober/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`](https://git.jerboa.sh/ober/jerboa) — it bundles
Chez Scheme, the Jerboa stdlib, and the `jerboa-native` Rust crate (Rustls
HTTPS). You need `jerbuild`, a C compiler, and `cargo`.
```sh
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](#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`](docs/credential-handling.md).
Native dependency provenance is documented in
[`docs/dependency-provenance.md`](docs/dependency-provenance.md), and local
release evidence is documented in
[`docs/release-evidence.md`](docs/release-evidence.md).
### Configuration
`jgl` resolves each setting with the precedence **flag › environment ›
`~/.gitlab.yaml` › default**. Use whichever fits:
```sh
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`):
```yaml
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
```sh
# 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*:
```sh
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 `scheme` compiler — the Makefile falls back to
`$JERBOA_HOME/build/chez/tarm64osx/bin/tarm64osx/scheme` if 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.
recent commits
- Document local mine repository authority 05becb4 Jaime Fournier
- Use FreeBSD Forgejo CI runner 7e8f1e1 user
- Set up Forgejo CI/CD policy f358195 Jaime Fournier
- Update Jerboa toolchain and repository links 234f390 Jaime Fournier
- Update jpkg metadata for Forgejo 61d3f10 Jaime Fournier
- docs: remove jerboa-emacs restriction 22cc73d Jaime Fournier
- Remove GitHub workflow metadata 258cf3f Jaime Fournier
- jpkg: complete binary package setup 117e77a Jaime Fournier
- update AGENTS.md fc6ce28 Jaime Fournier
- fix(client): guard JSON decode of 2xx response bodies bbf65f5 Jaime Fournier