Track your own DORA + Code metrics with GitHub Actions → Recorder
A walk-through for pushing Deploy / LeadTime / Lines metrics from your own GitHub repo into the Recorder using a personal PAT. Get a feel for the Recorder API end-to-end. NASEBANAL runs the same shape internally across all 10 repos via a dedicated System User.
What this recipe records
Push DORA and code metrics from your own GitHub repo into the Recorder via GitHub Actions and a personal PAT. Also a good way to get a feel for the Recorder API end-to-end.
<MyApp>/DORA/Deploy— daily deploy count (daily cron)<MyApp>/DORA/LeadTime— PR open → merge duration (per merge)<MyApp>/Code/Lines— total LOC at HEAD (per merge)<MyApp>/Code/PushDelta— net lines added/removed per merge
Pick any prefix for <MyApp> (e.g. MyTeam/..., Frontend/...). Each becomes its own tag in the Recorder, so the prefix is your namespace if you later want to compare multiple apps or teams side by side.
Prerequisites
- A Recorder account
- A GitHub repo where you can drop in workflow files
Setup
1. Mint a Recorder PAT
Go to Settings → API Tokens and create a PAT. The token string is only shown once in the issuance dialog — copy it immediately.
2. Register it as a GitHub Secret
In your repo's Settings → Secrets and variables → Actions, add a secret named RECORDER_PAT and paste the token from step 1.
# Or via gh CLI
gh secret set RECORDER_PAT --repo your-org/your-repo3. Drop in the workflows
.github/workflows/post-on-deploy.yml (runs on each merge):
name: Post DORA metrics on deploy
on:
push:
branches: [main]
permissions:
contents: read
pull-requests: read
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Compute and post LeadTime + Lines
env:
RECORDER_PAT: ${{ secrets.RECORDER_PAT }}
APP_PREFIX: MyApp # pick your own prefix
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# PR open → merge duration (minutes)
PR_NUM=$(gh pr list --state merged --base main --limit 1 --json number --jq '.[0].number')
if [ -n "$PR_NUM" ]; then
OPENED=$(gh pr view "$PR_NUM" --json createdAt --jq '.createdAt')
MERGED=$(gh pr view "$PR_NUM" --json mergedAt --jq '.mergedAt')
LEAD_MIN=$(( ($(date -d "$MERGED" +%s) - $(date -d "$OPENED" +%s)) / 60 ))
curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
-H "Authorization: Bearer $RECORDER_PAT" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$APP_PREFIX/DORA/LeadTime\",\"unit\":\"min\",\"data_type\":\"integer\",\"chart_type\":\"line\",\"value\":$LEAD_MIN}"
fi
# Total LOC at HEAD
LINES=$(git ls-files | xargs wc -l 2>/dev/null | tail -1 | awk '{print $1}')
curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
-H "Authorization: Bearer $RECORDER_PAT" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$APP_PREFIX/Code/Lines\",\"unit\":\"lines\",\"data_type\":\"integer\",\"chart_type\":\"line\",\"value\":$LINES}".github/workflows/post-daily.yml (one daily cron):
name: Post deploy count daily
on:
schedule:
- cron: '5 0 * * *' # 00:05 UTC every day
workflow_dispatch:
jobs:
post:
runs-on: ubuntu-latest
steps:
- name: Count yesterday's deploys
env:
RECORDER_PAT: ${{ secrets.RECORDER_PAT }}
APP_PREFIX: MyApp
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SINCE=$(date -u -d 'yesterday 00:00' +%Y-%m-%dT%H:%M:%SZ)
UNTIL=$(date -u -d 'today 00:00' +%Y-%m-%dT%H:%M:%SZ)
COUNT=$(gh run list --workflow deploy.yml --created "$SINCE..$UNTIL" --json status --jq 'map(select(.status=="completed")) | length')
curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
-H "Authorization: Bearer $RECORDER_PAT" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$APP_PREFIX/DORA/Deploy\",\"unit\":\"count\",\"data_type\":\"integer\",\"chart_type\":\"bar\",\"value\":$COUNT}"4. Smoke test
Before relying on a merge, POST a single record by hand to confirm the token is wired up.
export RECORDER_PAT="the token you copied in step 1"
curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
-H "Authorization: Bearer $RECORDER_PAT" \
-H "Content-Type: application/json" \
-d '{"name":"MyApp/DORA/Deploy","unit":"count","data_type":"integer","chart_type":"bar","value":1,"comment":"smoke test"}'MyApp/DORA/Deploy should now appear in your Recorder tag list. Merge a PR and post-on-deploy.yml will start filling in the real metrics.
Sample output
After a week or two of data, the Recorder lets you line your metrics up as a time series. Below is NASEBANAL's own nb-recorder repo, so the tag prefix is System/... — yours would line up under your own <MyApp>/... prefix.

Left axis is Code/Lines (blue), right axis is Code/BranchDensity (yellow). LOC trending up while branch density edges down reads as "the codebase is growing, but the average density of if branches is thinning" — i.e. structural refactoring is keeping pace with growth.
⚠️ Secret handling
- The PAT string is shown only once in the issuance dialog and cannot be re-displayed
- Use the PAT only via GitHub Secrets — don't write it to a local file
- If you suspect a leak, revoke the PAT in Recorder Settings → API Tokens, mint a new one, and overwrite the GitHub Secret