Coding‑hours report #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Coding‑hours report | |
on: | |
schedule: | |
- cron: '0 0 * * 1' # every Monday at 00:00 UTC | |
workflow_dispatch: # manual trigger | |
inputs: | |
window_start: | |
description: 'Report since YYYY‑MM‑DD' | |
required: false | |
permissions: | |
contents: write # needed for committing to the metrics branch | |
jobs: | |
report: | |
runs-on: ubuntu-latest | |
steps: | |
# 1️⃣ Check out full history | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
# 2️⃣ Set up Go (>=1.24 to support git-hours v0.1.2 go.mod) | |
- name: Setup Go | |
uses: actions/setup-go@v4 | |
with: | |
go-version: '1.24' | |
# 3️⃣ Install git-hours (Go) v0.1.2 with go.mod patch | |
- name: Install git-hours (Go) v0.1.2 | |
run: | | |
git clone --depth 1 --branch v0.1.2 https://github.com/trinhminhtriet/git-hours.git git-hours-src | |
cd git-hours-src | |
sed -i 's/go 1.24.1/go 1.24/' go.mod | |
go install . | |
# 4️⃣ Generate the report as JSON | |
- name: Generate report | |
run: | | |
ARGS="" | |
if [ -n "${{ github.event.inputs.window_start }}" ]; then | |
ARGS+=" -since ${{ github.event.inputs.window_start }}" | |
fi | |
git-hours --json $ARGS > git-hours.json | |
# 4️⃣½ Build a Shields.io badge from the JSON report | |
- name: Build badge.json | |
run: | | |
HOURS=$(jq '.total.hours' git-hours.json) | |
cat > badge.json <<EOF | |
{ | |
"schemaVersion": 1, | |
"label": "Coding hours", | |
"message": "${HOURS}h", | |
"color": "informational" | |
} | |
EOF | |
# 5️⃣ Publish the raw report to the run summary | |
- name: Add workflow summary | |
run: | | |
echo "### ⏱ Coding‑hours report" >> $GITHUB_STEP_SUMMARY | |
echo '```' >> $GITHUB_STEP_SUMMARY | |
jq -r ' | |
["Contributor","Hours","Commits"], | |
(.[] | select(.name!="") | [ .name, .hours, .commits ]) | |
| @tsv' git-hours.json \ | |
| column -t -s $'\''\t'\'' >> $GITHUB_STEP_SUMMARY | |
echo '```' >> $GITHUB_STEP_SUMMARY | |
# 6️⃣ Upload the JSON output as an artifact | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: git-hours-output-${{ github.run_id }} | |
path: git-hours.json | |
retention-days: 30 | |
# 7️⃣ (Optional) Commit to metrics branch | |
- name: Commit to metrics branch | |
if: github.ref == 'refs/heads/develop' | |
env: | |
GH_TOKEN: ${{ secrets.GH_PAT }} # PAT needed for write access | |
run: | | |
git config --global user.name "git-hours bot" | |
git config --global user.email "[email protected]" | |
git switch -C metrics || git checkout metrics | |
mkdir -p reports | |
mv git-hours.json reports/git-hours-$(date +%F).json | |
# Keep badge.json at repo root (in metrics branch) for a stable URL | |
git add reports badge.json | |
git commit -m "chore(metrics): add report $(date +%F)" || echo "No change" | |
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }} metrics | |
############################################################################### | |
# build a static site and push it to the gh-pages branch | |
############################################################################### | |
build-pages: | |
needs: report # wait for the main 'report' job | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
# ① Pull the raw JSON we just produced | |
- name: Download git-hours artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: git-hours-output-${{ github.run_id }} | |
path: tmp # contains git-hours.json | |
# ② Prepare a static site under ./site | |
- name: Build site | |
run: | | |
mkdir -p site/data | |
# Keep a dated copy for time‑series work | |
cp tmp/git-hours.json "site/data/git-hours-$(date +%F).json" | |
# Produce index.html | |
python - <<'PY' | |
import json, datetime, pathlib | |
data = json.load(open('tmp/git-hours.json')) | |
total = data['total'] | |
rows = "\n".join( | |
f"<tr><td>{k}</td><td>{v['hours']}</td><td>{v['commits']}</td></tr>" | |
for k,v in data.items() if k != 'total' | |
) | |
html = f"""<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Collaborator KPIs</title> | |
<link rel="stylesheet" | |
href="https://cdn.jsdelivr.net/npm/simpledotcss/simple.min.css"> | |
<script src="https://cdn.jsdelivr.net/npm/sortable-tablesort/sortable.min.js" defer></script> | |
</head> | |
<body> | |
<h1>Collaborator KPIs</h1> | |
<p><em>Last updated {datetime.datetime.utcnow().strftime('%F %H:%M UTC')}</em></p> | |
<h2>Totals</h2> | |
<ul> | |
<li><strong>Hours</strong>: {total['hours']}</li> | |
<li><strong>Commits</strong>: {total['commits']}</li> | |
<li><strong>Contributors</strong>: {len(data)-1}</li> | |
</ul> | |
<h2>Per‑contributor breakdown</h2> | |
<table class="sortable"> | |
<thead><tr><th>Contributor</th><th>Hours</th><th>Commits</th></tr></thead> | |
<tbody>{rows}</tbody> | |
</table> | |
<p>Raw data files live in <code>/data</code> for further analysis.</p> | |
</body></html>""" | |
pathlib.Path('site/index.html').write_text(html) | |
PY | |
# ③ Deploy ./site to the gh-pages branch | |
- name: Deploy to GitHub Pages | |
uses: peaceiris/actions-gh-pages@v4 # battle‑tested Pages deployer | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
publish_dir: site | |
publish_branch: gh-pages |