Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,10 @@ card above.
| `color` | no | `''` | Alert-card bar color (hex, e.g. `#F9A825`). Only takes effect in alert mode. Overrides the result-derived green/red. |
| `status` | no | `''` | When set, the alert card's first field renders as **Status** with this text in place of the **Version** field. Only takes effect in alert mode. |
| `channel` | no | `slack` | Chat channel; only `slack` is implemented today. |
| `github-token` | no | `${{ github.token }}` | Token used to read the PR/commit, the workflow run, and (on failure) the run's jobs. The default job token covers a same-repo deploy; for a cross-org deploy pass a token minted in the caller from a GitHub App with read access to `target-repo`. |
| `github-token` | no | `${{ github.token }}` | Token used to read the PR/commit that `target-repo`/`target-sha` describes. The default job token covers a same-repo deploy; for a cross-org deploy pass a token minted in the caller from a GitHub App with read access to `target-repo`. |
| `target-repo` | no | `${{ github.repository }}` | `owner/repo` whose PR/commit the card describes. Override for a cross-repo deploy. |
| `target-sha` | no | `${{ github.sha }}` | Exact built/deployed commit SHA used to resolve the PR/commit. |
| `run-token` | no | falls back to `github-token` | Token used ONLY for the `/actions/runs/{id}` + `/jobs` lookups that populate `DURATION` and `FAILED AT`. Runs always live in the caller's repo (`github.repository`), so a cross-org `github-token` scoped to `target-repo` can't read them. Pass `${{ github.token }}` when overriding `github-token` cross-org; otherwise leave unset. |

Example — same-repo deploy (token/target default to this repo):

Expand Down Expand Up @@ -360,6 +361,9 @@ Example — cross-org deploy (card describes a PR/commit in another repo):
github-token: ${{ steps.app-token.outputs.token }}
target-repo: other-org/other-repo
target-sha: ${{ needs.build.outputs.app_sha }}
# github-token is scoped to other-org/other-repo and can't read this
# repo's Actions API — pass the job's own token for DURATION + FAILED AT.
run-token: ${{ github.token }}
```

## enforce — inputs & outputs
Expand Down
26 changes: 22 additions & 4 deletions actions/notify/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ inputs:
commit shown on the card. Defaults to this workflow's commit.
required: false
default: ${{ github.sha }}
run-token:
description: >-
Token used ONLY for the /actions/runs/{id} + /jobs API calls that
populate DURATION and FAILED AT. Defaults to `github-token` (fine when
that token has `actions: read` on the current repo). Override when
`github-token` is a cross-org token minted for `target-repo` and thus
has no access to the current repo's Actions API: pass the job's own
`github.token` so the run metadata is read with it. Requires
`actions: read` on the notify job.
required: false
default: ""

runs:
using: composite
Expand All @@ -98,9 +109,11 @@ runs:
WEBHOOK: ${{ inputs.webhook-url }}
TARGET_REPO: ${{ inputs.target-repo }}
TARGET_SHA: ${{ inputs.target-sha }}
RUN_REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GH_TOKEN: ${{ inputs.github-token }}
RUN_TOKEN: ${{ inputs.run-token }}
run: |
set -uo pipefail

Expand All @@ -109,6 +122,11 @@ runs:
exit 0
fi

# Runs live in github.repository (RUN_REPO), even when target-repo is
# a cross-org repo for PR attribution. Fall the run-token back to the
# PR-attribution token so single-repo callers keep working unchanged.
RUN_TOKEN="${RUN_TOKEN:-$GH_TOKEN}"

# Status icon only. Header emoji conveys success/failure — no separate
# subtitle line under it.
icon=":x:"; is_success=0
Expand Down Expand Up @@ -200,10 +218,10 @@ runs:
# -- Duration: pull run_started_at, compute elapsed vs now.
# Falls back silently if the API call fails.
duration=""
run_json="$(gh api "repos/${TARGET_REPO}/actions/runs/${RUN_ID}" 2>/dev/null || echo '{}')"
run_json="$(GH_TOKEN="$RUN_TOKEN" gh api "repos/${RUN_REPO}/actions/runs/${RUN_ID}" 2>/dev/null || echo '{}')"
started_at="$(jq -r '.run_started_at // .created_at // empty' <<<"$run_json")"
if [ -z "$started_at" ]; then
echo "::warning::notify: /actions/runs/${RUN_ID} returned no run_started_at. Check that the notify job grants 'actions: read' — DURATION will not render."
echo "::warning::notify: /actions/runs/${RUN_ID} in ${RUN_REPO} returned no run_started_at. Check that the notify job grants 'actions: read' and (for cross-org callers) run-token has access — DURATION will not render."
fi
if [ -n "$started_at" ]; then
started_epoch="$(date -u -d "$started_at" +%s 2>/dev/null || echo '')"
Expand All @@ -227,9 +245,9 @@ runs:
# always human-meaningful and match what users see in the Actions UI.
failed_step=""
if [ "$is_success" -eq 0 ]; then
jobs_json="$(gh api "repos/${TARGET_REPO}/actions/runs/${RUN_ID}/jobs" 2>/dev/null || echo '{}')"
jobs_json="$(GH_TOKEN="$RUN_TOKEN" gh api "repos/${RUN_REPO}/actions/runs/${RUN_ID}/jobs" 2>/dev/null || echo '{}')"
if [ "$(jq -r '(.jobs // []) | length' <<<"$jobs_json")" = "0" ]; then
echo "::warning::notify: /actions/runs/${RUN_ID}/jobs returned no jobs. Check that the notify job grants 'actions: read' — FAILED AT will not render."
echo "::warning::notify: /actions/runs/${RUN_ID}/jobs in ${RUN_REPO} returned no jobs. Check that the notify job grants 'actions: read' and (for cross-org callers) run-token has access — FAILED AT will not render."
fi
failed_step="$(jq -r '[.jobs[]? | select(.conclusion == "failure")] | .[0].name // ""' <<<"$jobs_json")"
fi
Expand Down