Skip to content

Commit 929a803

Browse files
authored
Merge pull request #1205 from getlarge/fix/issue-121-pem-input-regression
fix(ci): pass MoltNet secrets to legreffier-run-task via job env
2 parents 5e1eb93 + c02de66 commit 929a803

3 files changed

Lines changed: 90 additions & 128 deletions

File tree

.github/actions/legreffier-run-task/action.yml

Lines changed: 48 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,35 @@ description: |
1313
file with shape `{ taskType, correlationId, input }`, and passing
1414
its path in `task-spec-path`. Composers MUST NOT touch MoltNet
1515
creds.
16-
3. Wiring credentials through the explicit inputs below — those are
17-
what this action owns.
16+
3. Setting the MoltNet credential env vars at the **job** level
17+
(`env:` on the job, not as action inputs). This action reads them
18+
directly from the process env. Passing them through composite
19+
action `inputs:` round-trips multi-line secrets (notably
20+
`MOLTNET_GITHUB_APP_PRIVATE_KEY`) through GitHub Actions YAML
21+
expression substitution, which collapses newlines and produces an
22+
unparseable PEM — see issue #121 for the discovery.
23+
24+
Required job env:
25+
MOLTNET_AGENT_NAME, MOLTNET_IDENTITY_ID, MOLTNET_CLIENT_ID,
26+
MOLTNET_CLIENT_SECRET, MOLTNET_PUBLIC_KEY, MOLTNET_PRIVATE_KEY,
27+
MOLTNET_FINGERPRINT, MOLTNET_TEAM_ID, MOLTNET_DIARY_ID,
28+
MOLTNET_API_URL.
29+
30+
Optional job env (only if the agent uses `gh` from the VM):
31+
MOLTNET_GITHUB_APP_ID, MOLTNET_GITHUB_APP_INSTALLATION_ID,
32+
MOLTNET_GITHUB_APP_SLUG, MOLTNET_GITHUB_APP_PRIVATE_KEY.
33+
34+
Optional job env (provider-specific):
35+
OLLAMA_API_KEY (when provider=ollama),
36+
PI_AUTH_JSON (Pi/Codex auth dump, single-line JSON OK as input
37+
but kept here for symmetry).
1838
1939
inputs:
2040
task-spec-path:
2141
description: |
2242
Path to a JSON file with shape `{ taskType, correlationId, input }`.
23-
`teamId` and `diaryId` are taken from the action's inputs (which
24-
mirror the agent's env), not from the spec — composers are
43+
`teamId` and `diaryId` are taken from MoltNet env vars (which mirror
44+
the agent's config), not from the spec — composers are
2545
credential-free by design.
2646
required: true
2747
skip-validation:
@@ -31,76 +51,13 @@ inputs:
3151
false.
3252
required: false
3353
default: 'false'
34-
agent-name:
35-
description: MoltNet agent name (used for `.moltnet/<agent>/` paths).
36-
required: true
3754
provider:
3855
description: Agent runtime provider (e.g. `ollama`, `openai`).
3956
required: true
4057
model:
4158
description: Agent runtime model id.
4259
required: true
4360

44-
# MoltNet credentials. Forwarded into the agent dir materialization step.
45-
moltnet-identity-id:
46-
description: MoltNet identity UUID.
47-
required: true
48-
moltnet-client-id:
49-
description: OAuth client id.
50-
required: true
51-
moltnet-client-secret:
52-
description: OAuth client secret.
53-
required: true
54-
moltnet-public-key:
55-
description: Agent Ed25519 public key (base64 raw).
56-
required: true
57-
moltnet-private-key:
58-
description: Agent Ed25519 private key (base64 raw).
59-
required: true
60-
moltnet-fingerprint:
61-
description: Agent fingerprint.
62-
required: true
63-
moltnet-team-id:
64-
description: Team UUID.
65-
required: true
66-
moltnet-diary-id:
67-
description: Diary UUID.
68-
required: true
69-
moltnet-api-url:
70-
description: MoltNet API base URL.
71-
required: true
72-
73-
# GitHub App credentials for the agent's gh calls (optional — required if
74-
# the imposer or the task itself uses `gh`).
75-
moltnet-github-app-id:
76-
description: GitHub App id.
77-
required: false
78-
default: ''
79-
moltnet-github-app-installation-id:
80-
description: GitHub App installation id.
81-
required: false
82-
default: ''
83-
moltnet-github-app-slug:
84-
description: GitHub App slug.
85-
required: false
86-
default: ''
87-
moltnet-github-app-private-key:
88-
description: GitHub App private key (PEM).
89-
required: false
90-
default: ''
91-
92-
# Provider-specific secrets.
93-
ollama-api-key:
94-
description: Ollama API key (required when provider=ollama).
95-
required: false
96-
default: ''
97-
pi-auth-json:
98-
description: |
99-
Contents of `~/.pi/auth.json` for Codex/Pi providers. Optional — if
100-
absent, Pi falls back to env-var providers.
101-
required: false
102-
default: ''
103-
10461
runs:
10562
using: composite
10663
steps:
@@ -113,23 +70,9 @@ runs:
11370
11471
- name: Materialize MoltNet agent dir from env
11572
shell: bash
116-
env:
117-
MOLTNET_AGENT_NAME: ${{ inputs.agent-name }}
118-
MOLTNET_IDENTITY_ID: ${{ inputs.moltnet-identity-id }}
119-
MOLTNET_CLIENT_ID: ${{ inputs.moltnet-client-id }}
120-
MOLTNET_CLIENT_SECRET: ${{ inputs.moltnet-client-secret }}
121-
MOLTNET_PUBLIC_KEY: ${{ inputs.moltnet-public-key }}
122-
MOLTNET_PRIVATE_KEY: ${{ inputs.moltnet-private-key }}
123-
MOLTNET_FINGERPRINT: ${{ inputs.moltnet-fingerprint }}
124-
MOLTNET_TEAM_ID: ${{ inputs.moltnet-team-id }}
125-
MOLTNET_DIARY_ID: ${{ inputs.moltnet-diary-id }}
126-
MOLTNET_API_URL: ${{ inputs.moltnet-api-url }}
127-
MOLTNET_GITHUB_APP_ID: ${{ inputs.moltnet-github-app-id }}
128-
MOLTNET_GITHUB_APP_INSTALLATION_ID: ${{ inputs.moltnet-github-app-installation-id }}
129-
MOLTNET_GITHUB_APP_SLUG: ${{ inputs.moltnet-github-app-slug }}
130-
MOLTNET_GITHUB_APP_PRIVATE_KEY: ${{ inputs.moltnet-github-app-private-key }}
13173
run: |
13274
set -euo pipefail
75+
: "${MOLTNET_AGENT_NAME:?MOLTNET_AGENT_NAME must be set at the job level}"
13376
AGENT_DIR="$GITHUB_WORKSPACE/.moltnet/$MOLTNET_AGENT_NAME"
13477
# Idempotent: a caller workflow that needs the agent dir earlier
13578
# (e.g. to run an imposer script) may already have materialized it.
@@ -141,13 +84,32 @@ runs:
14184
{
14285
echo "GIT_CONFIG_GLOBAL=$AGENT_DIR/gitconfig"
14386
echo "MOLTNET_AGENT_DIR=$AGENT_DIR"
144-
echo "MOLTNET_AGENT_NAME=$MOLTNET_AGENT_NAME"
14587
} >> "$GITHUB_ENV"
14688
89+
- name: Verify GitHub App PEM is well-formed
90+
shell: bash
91+
run: |
92+
set -euo pipefail
93+
# `config init-from-env` writes the PEM at <agent>.pem when the
94+
# GitHub App secrets are present. Skip the check if the file is
95+
# absent (the agent will just not call `gh`).
96+
PEM="$MOLTNET_AGENT_DIR/$MOLTNET_AGENT_NAME.pem"
97+
if [ ! -f "$PEM" ]; then
98+
echo "::notice::No GitHub App PEM at $PEM — agent gh calls will fall back."
99+
exit 0
100+
fi
101+
if ! head -1 "$PEM" | grep -q "^-----BEGIN .*PRIVATE KEY-----$"; then
102+
echo "::error::GitHub App PEM at $PEM is not a valid PEM — first line is not a BEGIN header. This usually means MOLTNET_GITHUB_APP_PRIVATE_KEY was passed through a composite-action input, which collapses newlines. Set it as a JOB env var instead."
103+
exit 1
104+
fi
105+
if ! tail -1 "$PEM" | grep -q "^-----END .*PRIVATE KEY-----$"; then
106+
echo "::error::GitHub App PEM at $PEM is not a valid PEM — last line is not an END footer."
107+
exit 1
108+
fi
109+
echo "::notice::GitHub App PEM at $PEM looks well-formed."
110+
147111
- name: Materialize Pi auth.json
148112
shell: bash
149-
env:
150-
PI_AUTH_JSON: ${{ inputs.pi-auth-json }}
151113
run: |
152114
set -euo pipefail
153115
if [ -n "${PI_AUTH_JSON:-}" ]; then
@@ -200,10 +162,6 @@ runs:
200162
env:
201163
TASK_SPEC_PATH: ${{ inputs.task-spec-path }}
202164
SKIP_VALIDATION: ${{ inputs.skip-validation }}
203-
MOLTNET_AGENT_NAME: ${{ inputs.agent-name }}
204-
MOLTNET_TEAM_ID: ${{ inputs.moltnet-team-id }}
205-
MOLTNET_DIARY_ID: ${{ inputs.moltnet-diary-id }}
206-
MOLTNET_API_URL: ${{ inputs.moltnet-api-url }}
207165
run: |
208166
set -euo pipefail
209167
if [ ! -f "$TASK_SPEC_PATH" ]; then
@@ -253,10 +211,8 @@ runs:
253211
shell: bash
254212
env:
255213
TASK_ID: ${{ steps.create-task.outputs.task-id }}
256-
MOLTNET_AGENT_NAME: ${{ inputs.agent-name }}
257214
MOLTNET_AGENT_PROVIDER: ${{ inputs.provider }}
258215
MOLTNET_AGENT_MODEL: ${{ inputs.model }}
259-
OLLAMA_API_KEY: ${{ inputs.ollama-api-key }}
260216
run: |
261217
set -euo pipefail
262218
if [ -z "$TASK_ID" ]; then
@@ -276,7 +232,7 @@ runs:
276232
PI_AGENT_DIR="${PI_CODING_AGENT_DIR:-$RUNNER_TEMP/.pi/agent}"
277233
if [ "$PROVIDER" = "ollama" ]; then
278234
if [ -z "${OLLAMA_API_KEY:-}" ]; then
279-
echo "::error::ollama-api-key input is required when provider=ollama" >&2
235+
echo "::error::OLLAMA_API_KEY env var is required when provider=ollama" >&2
280236
exit 1
281237
fi
282238
export PI_CODING_AGENT_DIR="$PI_AGENT_DIR"

.github/workflows/legreffier-complexity-review.yml

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@ jobs:
2626
env:
2727
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2828
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29+
# MoltNet credentials are set at the job level (not as composite
30+
# action inputs) because GitHub Actions YAML expression substitution
31+
# collapses newlines in multi-line secrets — see issue #121 / the
32+
# legreffier diary for the incident write-up. Composite-action steps
33+
# inherit job env directly, with no string-substitution round trip.
34+
MOLTNET_AGENT_NAME: ${{ vars.MOLTNET_AGENT_NAME }}
35+
MOLTNET_IDENTITY_ID: ${{ secrets.MOLTNET_IDENTITY_ID }}
36+
MOLTNET_CLIENT_ID: ${{ secrets.MOLTNET_CLIENT_ID }}
37+
MOLTNET_CLIENT_SECRET: ${{ secrets.MOLTNET_CLIENT_SECRET }}
38+
MOLTNET_PUBLIC_KEY: ${{ secrets.MOLTNET_PUBLIC_KEY }}
39+
MOLTNET_PRIVATE_KEY: ${{ secrets.MOLTNET_PRIVATE_KEY }}
40+
MOLTNET_FINGERPRINT: ${{ secrets.MOLTNET_FINGERPRINT }}
41+
MOLTNET_TEAM_ID: ${{ vars.MOLTNET_TEAM_ID }}
42+
MOLTNET_DIARY_ID: ${{ vars.MOLTNET_DIARY_ID }}
43+
MOLTNET_API_URL: ${{ vars.MOLTNET_API_URL }}
44+
MOLTNET_GITHUB_APP_ID: ${{ vars.MOLTNET_GITHUB_APP_ID }}
45+
MOLTNET_GITHUB_APP_INSTALLATION_ID: ${{ vars.MOLTNET_GITHUB_APP_INSTALLATION_ID }}
46+
MOLTNET_GITHUB_APP_SLUG: ${{ vars.MOLTNET_GITHUB_APP_SLUG }}
47+
MOLTNET_GITHUB_APP_PRIVATE_KEY: ${{ secrets.MOLTNET_GITHUB_APP_PRIVATE_KEY }}
48+
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
49+
PI_AUTH_JSON: ${{ secrets.PI_AUTH_JSON }}
2950
steps:
3051
- uses: actions/checkout@v6
3152
- uses: pnpm/action-setup@v4
@@ -55,21 +76,5 @@ jobs:
5576
with:
5677
task-spec-path: ${{ steps.compose.outputs.task-spec-path }}
5778
skip-validation: 'false'
58-
agent-name: ${{ vars.MOLTNET_AGENT_NAME }}
5979
provider: ${{ vars.MOLTNET_AGENT_PROVIDER }}
6080
model: ${{ vars.MOLTNET_AGENT_MODEL }}
61-
moltnet-identity-id: ${{ secrets.MOLTNET_IDENTITY_ID }}
62-
moltnet-client-id: ${{ secrets.MOLTNET_CLIENT_ID }}
63-
moltnet-client-secret: ${{ secrets.MOLTNET_CLIENT_SECRET }}
64-
moltnet-public-key: ${{ secrets.MOLTNET_PUBLIC_KEY }}
65-
moltnet-private-key: ${{ secrets.MOLTNET_PRIVATE_KEY }}
66-
moltnet-fingerprint: ${{ secrets.MOLTNET_FINGERPRINT }}
67-
moltnet-team-id: ${{ vars.MOLTNET_TEAM_ID }}
68-
moltnet-diary-id: ${{ vars.MOLTNET_DIARY_ID }}
69-
moltnet-api-url: ${{ vars.MOLTNET_API_URL }}
70-
moltnet-github-app-id: ${{ vars.MOLTNET_GITHUB_APP_ID }}
71-
moltnet-github-app-installation-id: ${{ vars.MOLTNET_GITHUB_APP_INSTALLATION_ID }}
72-
moltnet-github-app-slug: ${{ vars.MOLTNET_GITHUB_APP_SLUG }}
73-
moltnet-github-app-private-key: ${{ secrets.MOLTNET_GITHUB_APP_PRIVATE_KEY }}
74-
ollama-api-key: ${{ secrets.OLLAMA_API_KEY }}
75-
pi-auth-json: ${{ secrets.PI_AUTH_JSON }}

.github/workflows/legreffier-security-review.yml

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ permissions: {}
1010
# `@legreffier /security-review` to kick off a security-focused review.
1111
# Reuses the legreffier-run-task composite action; the only difference
1212
# from the complexity workflow is the composer + rubric + skill.
13-
#
14-
# `skip-validation: 'true'` until the API has the updated `pr_review`
15-
# schema (with optional `context: TaskContext`). Flip to 'false' once
16-
# the schema is deployed.
1713
jobs:
1814
security-review:
1915
if: |
@@ -31,6 +27,25 @@ jobs:
3127
env:
3228
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3329
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
# MoltNet credentials are set at the job level (not as composite
31+
# action inputs) because GitHub Actions YAML expression substitution
32+
# collapses newlines in multi-line secrets. See issue #121.
33+
MOLTNET_AGENT_NAME: ${{ vars.MOLTNET_AGENT_NAME }}
34+
MOLTNET_IDENTITY_ID: ${{ secrets.MOLTNET_IDENTITY_ID }}
35+
MOLTNET_CLIENT_ID: ${{ secrets.MOLTNET_CLIENT_ID }}
36+
MOLTNET_CLIENT_SECRET: ${{ secrets.MOLTNET_CLIENT_SECRET }}
37+
MOLTNET_PUBLIC_KEY: ${{ secrets.MOLTNET_PUBLIC_KEY }}
38+
MOLTNET_PRIVATE_KEY: ${{ secrets.MOLTNET_PRIVATE_KEY }}
39+
MOLTNET_FINGERPRINT: ${{ secrets.MOLTNET_FINGERPRINT }}
40+
MOLTNET_TEAM_ID: ${{ vars.MOLTNET_TEAM_ID }}
41+
MOLTNET_DIARY_ID: ${{ vars.MOLTNET_DIARY_ID }}
42+
MOLTNET_API_URL: ${{ vars.MOLTNET_API_URL }}
43+
MOLTNET_GITHUB_APP_ID: ${{ vars.MOLTNET_GITHUB_APP_ID }}
44+
MOLTNET_GITHUB_APP_INSTALLATION_ID: ${{ vars.MOLTNET_GITHUB_APP_INSTALLATION_ID }}
45+
MOLTNET_GITHUB_APP_SLUG: ${{ vars.MOLTNET_GITHUB_APP_SLUG }}
46+
MOLTNET_GITHUB_APP_PRIVATE_KEY: ${{ secrets.MOLTNET_GITHUB_APP_PRIVATE_KEY }}
47+
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
48+
PI_AUTH_JSON: ${{ secrets.PI_AUTH_JSON }}
3449
steps:
3550
- uses: actions/checkout@v6
3651
- uses: pnpm/action-setup@v4
@@ -59,22 +74,8 @@ jobs:
5974
uses: ./.github/actions/legreffier-run-task
6075
with:
6176
task-spec-path: ${{ steps.compose.outputs.task-spec-path }}
77+
# Flip to 'false' once the deployed `pr_review` schema includes
78+
# the optional `context: TaskContext` field.
6279
skip-validation: 'true'
63-
agent-name: ${{ vars.MOLTNET_AGENT_NAME }}
6480
provider: ${{ vars.MOLTNET_AGENT_PROVIDER }}
6581
model: ${{ vars.MOLTNET_AGENT_MODEL }}
66-
moltnet-identity-id: ${{ secrets.MOLTNET_IDENTITY_ID }}
67-
moltnet-client-id: ${{ secrets.MOLTNET_CLIENT_ID }}
68-
moltnet-client-secret: ${{ secrets.MOLTNET_CLIENT_SECRET }}
69-
moltnet-public-key: ${{ secrets.MOLTNET_PUBLIC_KEY }}
70-
moltnet-private-key: ${{ secrets.MOLTNET_PRIVATE_KEY }}
71-
moltnet-fingerprint: ${{ secrets.MOLTNET_FINGERPRINT }}
72-
moltnet-team-id: ${{ vars.MOLTNET_TEAM_ID }}
73-
moltnet-diary-id: ${{ vars.MOLTNET_DIARY_ID }}
74-
moltnet-api-url: ${{ vars.MOLTNET_API_URL }}
75-
moltnet-github-app-id: ${{ vars.MOLTNET_GITHUB_APP_ID }}
76-
moltnet-github-app-installation-id: ${{ vars.MOLTNET_GITHUB_APP_INSTALLATION_ID }}
77-
moltnet-github-app-slug: ${{ vars.MOLTNET_GITHUB_APP_SLUG }}
78-
moltnet-github-app-private-key: ${{ secrets.MOLTNET_GITHUB_APP_PRIVATE_KEY }}
79-
ollama-api-key: ${{ secrets.OLLAMA_API_KEY }}
80-
pi-auth-json: ${{ secrets.PI_AUTH_JSON }}

0 commit comments

Comments
 (0)