weekly-digest.yml
Monday Teams digest workflow. Lives in the redline-metrics repo.
What this is
Posts a Monday-morning stakeholder digest (open/stale PR counts, seed recall, dashboard link) to Teams as an Adaptive Card, via scripts/build-digest.mjs.
How to onboard it
Copy this file into the redline-metrics repository and set REDLINE_ORG_READ_TOKEN and TEAMS_WEBHOOK_URL there. The webhook must be a Power Automate "When a Teams webhook request is received" flow; the retired Office 365 connector shape no longer delivers.
- Lives in: The redline-metrics repo — not this one.
- Trigger: Every Monday at 07:00 UTC, plus workflow_dispatch.
How to use it
Nothing, normally — it runs itself every Monday. Run scripts/build-digest.mjs locally to preview digest.json before changing what the card reports.
What a run does, in order:
- Counts open and stale (>7 days untouched) org PRs via the GitHub search API.
- Builds the Adaptive Card with scripts/build-digest.mjs --out digest.json.
- POSTs it to TEAMS_WEBHOOK_URL — a Power Automate "When a Teams webhook request is received" flow; the old Office 365 connector shape no longer delivers. Fails the step outright if the webhook secret isn't set.
Works, but only where installed: needs REDLINE_ORG_READ_TOKEN and TEAMS_WEBHOOK_URL. Delivers an empty-looking digest on a repo with no collected telemetry yet.
Expected output
An Adaptive Card posted to Teams each Monday: open and stale PR counts, seed recall, and a dashboard link. With no collected telemetry yet the card still posts and reads empty. A missing webhook secret fails the step outright rather than posting nothing quietly.
How to edit it
- Edit the YAML in workflows/ or .github/workflows/workflows/ holds files destined for other repositories; .github/workflows/ is this repository's own CI. The two are not interchangeable — check where this one lives before editing.
- actionlintCI lints .github/workflows/*.yml, workflows/*.yml and templates/redline.yml together. workflows/ is pointed at explicitly because actionlint's own discovery would skip it.
- node scripts/check-pins.mjsIf you add a third-party action, pin it to a 40-character commit SHA with a trailing # vX.Y.Z comment. First-party actions/* are referenced by tag. The pin checker re-resolves the SHA against the tag the comment claims.
- node scripts/validate.mjsAsserts the workflow files the bundle depends on still exist, and that the gate's job ids still match the required check name derived from them.
The full file
# Lives in the redline-metrics repo. Monday-morning stakeholder digest.
#
# TEAMS_WEBHOOK_URL must point at a Power Automate "When a Teams webhook request is
# received" flow. The old Office 365 connector webhooks (the `{"text": "..."}` shape)
# were retired by Microsoft and silently stop delivering.
name: Redline Weekly Digest
on:
schedule:
- cron: '0 7 * * 1'
workflow_dispatch:
permissions:
contents: read
jobs:
digest:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Count open and stale PRs
id: prs
env:
GH_TOKEN: ${{ secrets.REDLINE_ORG_READ_TOKEN }}
ORG: ${{ github.repository_owner }}
run: |
set -euo pipefail
cutoff=$(date -u -d '7 days ago' +%Y-%m-%d)
# gh search caps at 1000 results; --json + jq length would silently truncate.
# total_count from the REST search endpoint is exact.
count() {
gh api -X GET search/issues -f q="$1" -f per_page=1 --jq '.total_count'
}
open_prs=$(count "org:$ORG is:pr is:open draft:false")
stale_prs=$(count "org:$ORG is:pr is:open draft:false updated:<$cutoff")
echo "open=$open_prs" >> "$GITHUB_OUTPUT"
echo "stale=$stale_prs" >> "$GITHUB_OUTPUT"
- name: Build Adaptive Card
run: |
npx --yes "redlinegate@0.0.3" metrics digest \
--org "${{ github.repository_owner }}" \
--days 7 \
--open-prs "${{ steps.prs.outputs.open }}" \
--stale-prs "${{ steps.prs.outputs.stale }}" \
--seed-scores data/seed-scores.jsonl \
--dashboard-url "${{ vars.REDLINE_DASHBOARD_URL }}" \
--out digest.json
- name: Post to Teams
env:
WEBHOOK: ${{ secrets.TEAMS_WEBHOOK_URL }}
run: |
set -euo pipefail
if [[ -z "${WEBHOOK:-}" ]]; then
echo "::error::TEAMS_WEBHOOK_URL is not set — digest built but not delivered."
exit 1
fi
curl -sS --fail-with-body -X POST \
-H 'Content-Type: application/json' \
--data @digest.json "$WEBHOOK"
- name: Attach digest to the run
if: always()
run: |
{
echo '### Digest payload'
echo '```json'
cat digest.json 2>/dev/null || echo '{}'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"