GitHub Action¶
marshal-ledger/marshal/collect reads agent definitions from the checked-out repository, sends them to the Marshal ingest API, and on pull requests posts the risk-ranked changes as a comment. Later runs on the same pull request update the comment in place.
It runs on GitHub-hosted runners, and on self-hosted runners or GitHub Enterprise Server with runner v2.327.1 or later, which run Node 24 actions. The job needs these permissions:
permissions:
contents: read
id-token: write # OIDC token for Marshal; no stored secret
pull-requests: write # only for the PR comment
A complete workflow is in examples/github-workflow.yml. The pattern is to record proposed on pull requests and shipped after the deploy step succeeds:
- uses: marshal-ledger/marshal/collect@v0
with:
event: proposed
artifacts: .
target: prod
url: https://marshal.acme.example
@v0 follows the latest 0.x release. To decide when to upgrade, pin a release tag such as @v0.4.0, or a commit SHA. The Action installs Marshal from the same commit, so the client always matches the Action's version.
| Input | Required | Meaning |
|---|---|---|
event |
yes | proposed, shipped, or observed for a scheduled export of a live definition |
artifacts |
yes | Path of the agent artifact, such as a bundle directory |
url |
yes | Base URL of the ingest API |
target |
no | Deployment target whose overrides to apply |
environment |
no | Environment the event applies to; defaults to target |
audience |
no | OIDC audience (default marshal) |
comment |
no | Comment on pull requests (default true) |
portability |
no | Space-separated platforms to score portability to in the comment, such as bedrock copilot-studio |
python-version |
no | Python used to run Marshal (default 3.12) |
The report is also added to the job summary and exposed as the report output.
A typical comment:
**eval_testAgent** (copilot-studio) · v1 → v2 · 3 changes, 3 high risk
HIGH ~ permissions.identity: mixed → shared_credential
HIGH ~ extensions.copilot-studio.actions.SearchDocs.connection_mode: Invoker → Maker
HIGH ~ extensions.copilot-studio.actions.SearchDocs.credentials: end user → maker
Completeness 38%
Pull requests from forks don't receive OIDC tokens, so the action can't record events for them.
Runners without internet access¶
The Action has three steps that reach beyond the runner, so a self-hosted runner without internet access needs each one served from inside your network:
- Python. The Action runs
actions/setup-python, pinned to a commit, withpython-version(default3.12). Without access to github.com, setup-python takes Python only from the runner's tool cache (RUNNER_TOOL_CACHE/Python). Populate it as GitHub describes in Setting up the tool cache on self-hosted runners without internet access, with a version that matchespython-version. - Marshal. The Action installs Marshal from its own checkout with
pip install, which builds it from source. pip reads its index fromPIP_INDEX_URLorpip.confon the runner (see Locked-down installs), and the build uses the same index. Besidespydanticandpyyaml, the index must serve the build backendshatchlingandhatch-fancy-pypi-readme, and their dependencies. A pull-through proxy of PyPI serves them on demand; a curated mirror must list them. On a connected machine,pip download hatchling hatch-fancy-pypi-readme pydantic pyyamlfetches the whole set for that machine's Python and platform. - Other Actions. On GitHub Enterprise Server without GitHub Connect, sync
marshal-ledger/marshalandactions/setup-python, at the commit the Action pins, to the appliance withactions-sync.
The runner also needs to reach the Marshal ingest URL and your GitHub host, which issues the OIDC token and, for the pull request comment, serves the API. The comment step runs the gh CLI, so a self-hosted runner needs it installed, or set comment: "false".
If your runners already have Python and an internal index, you can skip the Action and run the client directly: pip install marshal-ledger, then marshal push <artifacts> --event proposed --target prod --url <ingest URL> --commit "$GITHUB_SHA", in a job with id-token: write. That installs the published wheel, which needs no build backends.