Static site tracking application versions returned by the Evergreen PowerShell module.
Deployed to GitHub Pages at https://stealthpuppy.com/apptracker/.
Previously built with Jekyll (Just the Docs). Migrated to Astro + React to support the interactive two-panel SPA UI that matches the Evergreen Workbench desktop application.
json/ ← App version data (one JSON per app, updated by update-apps.yml)
src/
pages/
index.astro ← Shell page, mounts <AppsPage client:load />
appdata.json.ts ← Build-time API endpoint; reads json/*.json + git log for lastUpdated
layouts/
Layout.astro ← HTML shell, Inter font, theme script
components/
AppsPage.tsx ← Root SPA component; owns all state (tab, selectedApp, favourites)
AppsSidebar.tsx ← App list; favourites pinned at top; green dot badge for recent apps
AppDetails.tsx ← Version table; dynamic columns in JSON key order; copy-URI on row click
DashboardPage.tsx ← Stat cards + CSS bar charts (Architecture, File type top 10)
AboutPage.tsx ← Version info + changelog
GlobalSearch.tsx ← Ctrl+K overlay; searches all apps + version data; portal outside #root
ThemeToggle.tsx ← Light/dark toggle; persists to localStorage
styles/
global.css ← Evergreen brand palette; CSS custom properties for light/dark
public/
assets/ ← Static images / icons
astro.config.mjs ← base: '/apptracker', output: 'static'
package.json
tsconfig.json
.github/workflows/
update-apps.yml ← DO NOT EDIT. Runs Evergreen on windows-latest; commits json/*.json
astro-gh-pages.yml ← Builds + deploys Astro site to GitHub Pages
update-apps.yml (windows-latest)
└─ scripts/Update-Json.ps1
└─ Saves one JSON file per app to json/
└─ Commits to main with message "Update json YYYY.MM.DD..."
↓ workflow_run trigger (on: completed, conclusion == success)
astro-gh-pages.yml (ubuntu-latest)
└─ actions/checkout@v4 (fetch-depth: 0 — full history for git log)
└─ npm ci
└─ npm run build
└─ src/pages/appdata.json.ts runs at build time:
• Globs json/*.json → versions[]
• Runs `git log -1 --format=%cI -- json/<app>.json` per app → lastUpdated
• Outputs dist/appdata.json { meta, apps[] }
└─ upload-pages-artifact (dist/)
└─ deploy-pages → https://stealthpuppy.com/apptracker/
Also triggers on:
- Direct push to
maintouchingsrc/**,public/**,astro.config.mjs,package.json - Manual
workflow_dispatch
When the Astro site is extracted to its own repository, the following needs updating:
src/
public/
json/ ← Copy current snapshot; kept live by update-apps.yml commits
astro.config.mjs
package.json
tsconfig.json
.gitignore ← Ensure dist/, .astro/, node_modules/ are excluded
.github/workflows/
astro-gh-pages.yml ← Update workflow_run reference (see below)
docs/ ← Old Jekyll site, no longer used
scripts/New-Report.ps1 ← Jekyll report generator, no longer used
.github/workflows/update-docs.yml ← Jekyll workflow, replaced by astro-gh-pages.yml
.github/workflows/update-apps.yml ← Stays in original repo (or move if json/ moves)
Option B — json/ stays in original repo, fetched at build time
Keep update-apps.yml and json/ in the original repo unchanged.
In the new repo's astro-gh-pages.yml, add a step to clone only the json/ folder
from the original repo before building:
- name: Fetch app JSON data
run: |
git clone --depth=1 --filter=blob:none --sparse \
https://github.com/stealthpuppy/apptracker.git _source
cd _source && git sparse-checkout set json
cp -r _source/json ./jsonNote: fetch-depth: 0 on the main checkout is still needed for git log date
resolution to work correctly in appdata.json.ts.
| Feature | Component |
|---|---|
| Two-panel SPA (sidebar + detail) | AppsPage.tsx |
| Light/dark theme (Evergreen brand palette) | global.css, ThemeToggle.tsx |
| Favourites / pinned apps with star button | AppsSidebar.tsx, AppsPage.tsx |
| Green dot badge for apps updated within 48 h | AppsSidebar.tsx |
| Sortable + per-column searchable version table | AppDetails.tsx |
| Dynamic columns in JSON key order | AppDetails.tsx |
| Copy URI on row click with flash animation | AppDetails.tsx |
PowerShell Get-EvergreenApp snippet per app |
AppDetails.tsx |
| Global search overlay (Ctrl+K) with blur | GlobalSearch.tsx |
| Search by URI/URL in global search | GlobalSearch.tsx |
| Architecture + File type filters | AppDetails.tsx |
| Dashboard with stat cards + CSS bar charts | DashboardPage.tsx |
| About page with changelog | AboutPage.tsx |
Hash-based permalink routing (#AppName) |
AppsPage.tsx |
| Keyboard navigation (↑↓ arrow, / for search) | AppsPage.tsx |
| Persist selected app + theme to localStorage | AppsPage.tsx |
Build-time lastUpdated via git log |
src/pages/appdata.json.ts |
| GitHub Pages deploy workflow | .github/workflows/astro-gh-pages.yml |
- Cross-repo data strategy decision (Option A vs B above)
- Set up GitHub Pages in new repository
- Validate
workflow_dispatchtriggers on new repo before going live - Commit all current changes and open PR from
astro→main - After merge: confirm
workflow_runchain fires end-to-end - Consider adding
package-lock.jsonto version control (currently gitignored)
npm install # install dependencies
npm run dev # dev server at http://localhost:4321/apptracker/
npm run build # production build → dist/
npm run preview # preview dist/ locallyBuild requires full git history for lastUpdated resolution:
git fetch --unshallow # if cloned with --depthWhy Astro? Static output compatible with GitHub Pages. Build-time data loading (no runtime API calls). React islands for interactivity without a full SPA framework.
Why no charting library?
CSS width percentages on .bar-chart__bar elements are sufficient for the simple
horizontal bar charts on the Dashboard. Zero JS bundle cost.
Why React Portal for GlobalSearch?
The search overlay needs to render outside #root so a filter: blur(4px) on #root
blurs the background without blurring the overlay itself.
Why git log for lastUpdated?
The JSON files don't contain a last-updated timestamp. The git commit date of the most
recent change to each json/<app>.json file is the canonical source of truth for when
Evergreen last saw a new version.