-
Notifications
You must be signed in to change notification settings - Fork 0
167 lines (156 loc) · 8.03 KB
/
Copy pathleave-tracker.yml
File metadata and controls
167 lines (156 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
name: Leave Tracker
# Parses the committed leave-tracker xlsx and publishes the normalized report to the
# `leave-tracker/report` branch, which the Netlify dashboard reads at runtime.
#
# HARDENED FLOW (simple + reproducible): this AUTO-RUNS whenever the source workbook or an
# override file changes on `main` (e.g. an "Add person" PR merges), so the dashboard updates
# with no manual step. The PI is inferred from the xlsx filename, so no inputs are required.
# You can still trigger it by hand (workflow_dispatch) to force a refresh or override the PI.
#
# HOURLY GOOGLE SHEETS SYNC: the team edits leave in a Google Sheet, and for a long time the only
# way that reached the dashboard was someone downloading the .xlsx and committing it by hand — so
# the dashboard silently showed month-old data. When the repo variable LEAVE_SHEET_ID is set, the
# hourly run re-exports that sheet as .xlsx and parses it, and the committed workbook in
# leave/source/ becomes a FALLBACK for when the export is unreachable. Nothing is committed to
# `main` by this job; only the report branch moves.
on:
schedule:
- cron: "0 * * * *" # hourly: re-export the Google Sheet (see LEAVE_SHEET_ID below)
push:
branches: [main]
paths:
- "leave/overrides/**"
- "leave/source/**"
- ".github/workflows/leave-tracker.yml"
workflow_dispatch:
inputs:
xlsx-path:
description: "Path to the leave-tracker xlsx"
type: string
required: false
default: "leave/source/DSE Leave Tracker - 26.4.xlsx"
pi:
description: "Program Increment label (blank = inferred from the xlsx filename)"
type: string
required: false
default: ""
year:
description: "Override inferred year (blank = derive from PI)"
type: string
required: false
default: ""
risk-threshold:
description: "Default fraction of a team OUT to flag (0-1)"
type: string
required: false
default: "0.30"
permissions:
contents: write # publish the report to the leave-tracker/report branch
# One report run at a time; a newer override supersedes an in-flight run.
concurrency:
group: leave-tracker-report
cancel-in-progress: true
env:
DASHBOARD_URL: "https://veda-leave-dashboard.netlify.app"
jobs:
leave-tracker:
runs-on: ubuntu-latest
steps:
- name: "1 · Checkout repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so the report branch can be based/pushed
# Overwrites the workspace copy of the workbook only — never committed to `main`. Keeping the
# same PATH means the PI keeps being inferred from the committed filename, and a failed or
# revoked export degrades to the committed workbook instead of breaking the run.
- name: "1b · Refresh the workbook from Google Sheets"
if: vars.LEAVE_SHEET_ID != ''
env:
SHEET_ID: ${{ vars.LEAVE_SHEET_ID }}
XLSX: ${{ github.event.inputs.xlsx-path || 'leave/source/DSE Leave Tracker - 26.4.xlsx' }}
run: |
set -uo pipefail
URL="https://docs.google.com/spreadsheets/d/${SHEET_ID}/export?format=xlsx"
# A sheet that loses link-sharing returns 200 + an HTML sign-in page, which `curl -f`
# happily accepts — so prove the download is really a zip (xlsx) before trusting it.
if curl -fsSL --max-time 120 -o "$RUNNER_TEMP/sheet.xlsx" "$URL" \
&& python3 -c "import sys,zipfile; sys.exit(0 if zipfile.is_zipfile(sys.argv[1]) else 1)" \
"$RUNNER_TEMP/sheet.xlsx"; then
cp "$RUNNER_TEMP/sheet.xlsx" "$XLSX"
echo "Refreshed from Google Sheets: $(wc -c < "$XLSX") bytes."
else
echo "::warning::Google Sheets export failed or was not an .xlsx — using the committed workbook."
fi
- name: "2 · Parse the leave tracker (reusable action)"
id: run
uses: ./leave
with:
# On `push` github.event.inputs.* are empty strings, so fall back to defaults here
# (an empty xlsx-path would otherwise fail the file check). pi stays blank on purpose —
# the generator infers it from the xlsx filename.
xlsx-path: ${{ github.event.inputs.xlsx-path || 'leave/source/DSE Leave Tracker - 26.4.xlsx' }}
pi: ${{ github.event.inputs.pi }}
year: ${{ github.event.inputs.year }}
risk-threshold: ${{ github.event.inputs.risk-threshold || '0.30' }}
out-dir: reports
- name: "3 · Job summary"
run: |
echo "people=${{ steps.run.outputs.people }} teams=${{ steps.run.outputs.teams }} leave-days=${{ steps.run.outputs.leave-days }}"
{
echo ""
echo "🗓️ **Leave dashboard →** ${DASHBOARD_URL}"
echo "_${{ steps.run.outputs.people }} people · ${{ steps.run.outputs.teams }} teams · ${{ steps.run.outputs.leave-days }} out-days_"
} >> "$GITHUB_STEP_SUMMARY"
- name: "4 · Upload report artifact"
uses: actions/upload-artifact@v4
with:
name: leave-tracker-report
path: reports/
- name: "5 · Publish report to the leave-tracker/report branch (accumulate by PI)"
run: |
BR="leave-tracker/report"
mkdir -p "$RUNNER_TEMP/out" && cp -R reports/. "$RUNNER_TEMP/out/" # stash before switching branches
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch origin "$BR" || true
if git rev-parse --verify --quiet "origin/$BR"; then
git checkout -f -B "$BR" "origin/$BR" # continue history: prior PIs persist
else
git checkout -f -B "$BR"
fi
mkdir -p reports
# Keep only slugged leave reports + the index (drop main's other samples / fixed names).
find reports -maxdepth 1 -type f \
! -name 'leaves_*.csv' ! -name 'leaves_*.json' ! -name 'leave_coverage_*.json' \
! -name 'index.json' -delete 2>/dev/null || true
# Copy in THIS run's slugged files (new content wins for the same slug), incl. the manifest.
MAN="$RUNNER_TEMP/out/leave_manifest.json"
cp "$MAN" "reports/leave_manifest.json"
for k in csv json coverage; do
f="$(jq -r ".files.$k" "$MAN")"; cp "$RUNNER_TEMP/out/$f" "reports/$f"
done
# Upsert this run's entry into reports/index.json (dedupe by slug, newest first).
[ -f reports/index.json ] || echo '[]' > reports/index.json
ENTRY="$(jq -c '{slug,pi,year,generated,files,stats}' "$MAN")"
jq --argjson e "$ENTRY" '(map(select(.slug != $e.slug))) + [$e] | sort_by(.generated) | reverse' \
reports/index.json > reports/index.json.tmp && mv reports/index.json.tmp reports/index.json
git add -f reports
# The manifest carries a fresh `generated` timestamp on every run, so once this job runs
# hourly an idle run would still "change" a file and commit — 24 empty commits a day. If
# the ONLY staged file is the manifest and the ONLY differing lines in it are "generated",
# nothing about the leave data actually moved: leave the branch alone.
CHANGED="$(git diff --cached --name-only)"
if [ "$CHANGED" = "reports/leave_manifest.json" ] \
&& ! git diff --cached -U0 -- reports/leave_manifest.json \
| grep -E '^[+-][^+-]' | grep -qv '"generated"'; then
echo "No data change for $BR — only the generated timestamp moved."
elif [ -z "$CHANGED" ]; then
echo "No report changes for $BR."
else
git commit -m "Leave tracker report — $(jq -r .slug "$MAN") (run ${{ github.run_id }})"
git push origin "$BR"
echo "Report pushed to branch: $BR"
fi
- name: "6 · Restore the default checkout"
if: always()
run: git checkout -f "${{ github.sha }}" 2>/dev/null || git checkout -f main