Skip to main content

Forge CLI

The Forge CLI (forge) is a Python command-line tool to work with and operate Forge services. It authenticates you against Forge ID and talks to the Forge APIs for you, so you can script things that would otherwise require clicking through Operator or hand-crafting curl calls.

It is the replacement for Poulpy, which is now deprecated and unmaintained. If you are coming from Poulpy, read Migrating from Poulpy.

The Forge CLI is experimental

Commands and options may change between versions. The output of forge <command> --help is always the source of truth: if this documentation and the CLI disagree, the CLI is right (and you can tell us about it).

What you can do with it​

  • Get a JWT to call Forge APIs manually or from your own scripts.
  • List the activities, groups and submissions you manage.
  • Download student traces in bulk, for one assignment or for one group.
  • Generate CSV reports about validation and success rates.
  • Rerun moulinettes on a whole submission.
  • Clone every student repository of an activity at once.
  • Generate a skeleton to bootstrap a new activity.
Deploying activities and tenants

The Forge CLI does not validate or deploy activity.yml and tenant.yml files. Deployment is handled by the GitLab CI pipeline of your activity repository, see Deploy Projects.

Installation​

The source code is hosted on the Forge GitLab. Start by cloning it:

git clone https://gitlab.cri.epita.fr/forge/tools/cli.git forge-cli
cd forge-cli

The Forge CLI is packaged with the uv Python package manager. Install uv by following the official instructions, then install the dependencies of the project:

uv sync

You can now run the CLI from the repository:

uv run forge --help

If you would rather have a forge command available everywhere instead of having to cd into the clone, install it as a uv tool:

uv tool install .
forge --version
Make sure uv tools are in your PATH

If your shell cannot find forge after uv tool install, run uv tool update-shell and open a new shell.

With Nix​

A flake is provided, so you can run the CLI without installing anything:

nix run .# -- --help

A development shell with Python and uv is also available with nix develop.

With Docker​

An image is published on the Forge registry. The entrypoint of the image is forge itself, so you only pass arguments to it:

docker run --rm -it \
-p 2097:2097 \
-v "$HOME/.config/forge-cli-credentials.json:/forge/.forge-cli-credentials.json" \
-v "$PWD:/forge/data" \
registry.cri.epita.fr/forge/tools/cli:latest --help

The -p 2097:2097 option is required for authentication to work, as the image is configured to listen for the OIDC callback on all interfaces. The container works in /forge, so mount the directory in which you want your downloads to land (here $PWD into /forge/data).

Create the credentials file first

Docker creates a directory when you bind-mount a file that does not exist yet. Run touch "$HOME/.config/forge-cli-credentials.json" before the first docker run.

Updating​

The CLI is not auto-updated: pull the repository and sync the dependencies again.

git pull
uv sync

If you installed it as a uv tool, reinstall it after pulling:

uv tool install --reinstall .

Check which version you are running with:

forge --version

Authentication​

Every command that talks to a Forge service authenticates you through Forge ID with an OIDC authorization code flow. The first time you run such a command, the CLI starts a local HTTP server on http://localhost:2097/complete/epita/ and prints a URL to visit:

$ forge auth token
You need to log in
Please visit this URL : https://cri.epita.fr/authorize?client_id=...

Open that URL in your browser, log in, and the CLI resumes on its own once the CRI redirects you back to localhost. Credentials (an ID token and a refresh token) are then written to disk, and subsequent runs refresh the token silently without asking anything.

Getting a JWT​

forge auth token prints the JWT on stdout, which makes it easy to inject in your own requests:

curl --fail -H "Authorization: Bearer $(forge auth token)" \
https://operator.forge.epita.fr/api/activities
Log in once before using $(forge auth token)

Unlike Poulpy, the login instructions are printed on stdout too. If your credentials are missing or expired, they end up captured by the command substitution instead of being shown to you. Always run forge auth token once on its own after installing, and whenever a script starts failing for no apparent reason.

To put the token directly in your clipboard on Linux:

  • forge auth token | wl-copy for Wayland (requires wl-clipboard)
  • forge auth token | xsel -b for X11
JWTs are confidential

JWTs are sensitive credentials that should not be shared with anyone, and neither should the credentials file described below.

Authenticating from a remote machine​

The OIDC callback goes to localhost:2097 on the machine running your browser. If you run the CLI over SSH, forward the port before logging in:

ssh -L 2097:localhost:2097 user@remote

The listening address can be changed with the FORGE_OIDC_BIND_ADDR environment variable (the port is always 2097).

Configuration​

Credentials file​

Tokens are stored in ~/.config/forge-cli-credentials.json. It is a plain JSON file, so you can read the current token with jq if you need to:

jq -r .id_token ~/.config/forge-cli-credentials.json

Note that CRI tokens are short-lived: prefer forge auth token, which refreshes the token for you, over reading the file directly. Deleting this file is the supported way to log out or to recover from a corrupted state.

Environment variables​

VariableDefaultDescription
FORGE_CREDENTIALS_PATH~/.config/forge-cli-credentials.jsonWhere tokens are stored
FORGE_OIDC_BIND_ADDR127.0.0.1Address the OIDC callback server listens on

URIs​

Most commands take a URI rather than a name. URIs are slash-separated paths built from the slugs of your tenant and of your activity configuration:

KindShapeExample
Activity URI<tenant>/<activity>epita-ing-assistants-acu/42sh
Assignment URI<tenant>/<activity>/<assignment>epita-ing-assistants-acu/42sh/step-1
Submission definition URI<tenant>/<activity>/<assignment>/<submission>epita-ing-assistants-acu/42sh/step-1/final

You can list the URIs you have access to with forge activities list and forge activities submissions list.

Troubleshooting​

Cannot get ... / HTTP 403​

You are authenticated, but you do not have the permissions required on that tenant or activity. Ask a manager of the tenant to grant them to you.

MaxRetriesExceeded​

The CLI retried a call twice after re-authenticating and still got a 401. Delete ~/.config/forge-cli-credentials.json and log in again with forge auth token.

The command hangs after the browser redirect​

Something else is already listening on port 2097, or the port is not reachable from your browser (remote machine, container). See Authenticating from a remote machine.

Next steps​