惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
T
The Blog of Author Tim Ferriss
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Engineering at Meta
Engineering at Meta
量子位
I
InfoQ
Jina AI
Jina AI
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
G
Google Developers Blog
J
Java Code Geeks
Recent Announcements
Recent Announcements
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
V
V2EX
腾讯CDC
P
Proofpoint News Feed
A
About on SuperTechFans
爱范儿
爱范儿
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI

Hacker News: Show HN

PurrrrrFocus: Pomodoro Timer App - App Store Workflow Engine — Multi-Step Orchestration for Bun RapidPhoto: Pro Photo Editor App - App Store GitHub - DheerG/swarms: Achieve extraordinary results with claude code across a variety of tasks SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads Show HN: VCoding – A 5 MB native Windows IDE with no dynamic dependencies Show HN: LLMs don't hallucinate because they're bad at math, it's the format GitHub - Agent-FM/agentfm-core: AgentFM is a peer-to-peer network that turns everyday computers into a decentralized AI supercomputer. AgentFM lets you run massive AI workloads directly across a global mesh of idle CPUs and GPUs. Show HN: Tracking Top US Science Olympiad Alumni over Last 25 Years GitHub - Potarix/agent-hub: One place to talk to all your agents Show HN: Runtime security for AI agents(injection,tool abuse, data exfiltration) GitHub - dubeyKartikay/lazyspotify: Terminal Spotify client for macOS and Linux GitHub - the-banana-tool/king-louie: Easy to use GUI Personal AI Assistant. Win/Linux/Mac. Show HN I made my vacation rental bookable by AI agents–no Airbnb, 0% commission GitHub - basteez/jsf-autoreload: maven plugin to enable hot reload on jsf projects uvm32/hosts/host-gdbstub at main · ringtailsoftware/uvm32 GitHub - labsai/EDDI: Config-driven engine that turns JSON into production-grade AI agents. Multi-agent orchestration, 12+ LLM providers, MCP/A2A protocols, RAG, persistent memory, and enterprise compliance (EU AI Act, GDPR, HIPAA). Built on Quarkus. GitHub - glitchnsec/fortyone-oss: AI Executive Assistant Platform Quickstart | Alien GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. GitHub - ocrbase-hq/ocrbase: 📄 PDF/IMG ->.MD/JSON Document OCR API for PaddleOCR and GLMOCR. Self-hostable. GitHub - impactjo/home-memory: MCP server that lets your AI assistant remember everything about your home. GitHub - Sets88/dbcls: DbCls is a powerful terminal database client that supports various databases GitHub - neptun2000/heor-agent-mcp GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh RollQuation: Math Puzzles - Apps on Google Play GitHub - dropbox/witchcraft Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis GitHub - opentalon/opentalon: OpenTalon is an open-source platform built from the ground up in Go as a robust alternative to OpenClaw LinkedIn™ 职位抓取工具 - Chrome 应用商店
GitHub - hatsu38/ghtrack
hatsu · 2026-06-22 · via Hacker News: Show HN

English | 日本語

A GitHub Action that records the execution time of each workflow run into your gh-pages branch on every run.

The goal is to apply the real-time accumulation pattern of benchmark-action/github-action-benchmark to workflow / step duration instead of benchmark values.

How it works

Collects job / step duration of a workflow run and writes it as a per-run JSON file under data/<track-name>/<YYYY>/<MM>/<DD>/<run_id>-<attempt>.json on the gh-pages branch. A Chart.js-powered index.html is bundled at the branch root and the time-series charts are visible in the browser once GitHub Pages is enabled.

🌐 Live demo: https://hatsu38.github.io/ghtrack/ ← this repo's own dogfood

Usage

Add this Action to the end of any workflow in the repository. If the gh-pages branch does not exist, an orphan branch is created automatically on the first run. Each workflow's data goes under data/<workflow-file-basename>/ by default, so you can drop the Action into multiple workflows without configuring anything.

# .github/workflows/your-workflow.yml
name: build

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: echo "build something"

  track:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write   # required to push to gh-pages
      actions: read     # required to fetch workflow run / job APIs
    steps:
      - uses: hatsu38/ghtrack@main

Inputs

All optional. The intent is that defaults are enough to start accumulating data into your repo's gh-pages branch out of the box.

name default description
github-token ${{ github.token }} used to fetch workflow run / job data and to push to gh-pages. Requires contents: write
gh-pages-branch gh-pages branch where the data is accumulated
track-name workflow file basename (e.g. test.ymltest) directory name under data/. Must match [a-zA-Z0-9._-]+. Override this to split one workflow into multiple tracks (matrix shards, environments, etc.)

File layout on the gh-pages branch

gh-pages/
├── index.html
└── data/
    ├── manifest.json                   # workflow registry
    └── <track-name>/
        ├── index.json                  # run list for this track
        └── <YYYY>/<MM>/<DD>/
            ├── <run_id>-<attempt>.json # one file per run (the Entry)
            └── ...

Each per-run file is independent and append-only: a push never reads or modifies prior runs, so workflow run history scales linearly without hitting GitHub's createBlob size limit.

Schema

Per-run file (<track-name>/<YYYY>/<MM>/<DD>/<run_id>-<attempt>.json)

A duration_sec of null means the step is incomplete (because the run is observing itself, the final step of the track job is always recorded as incomplete).

Workflow index (data/<track-name>/index.json)

The dashboard uses runs[] to know which per-run files exist (GitHub Pages does not serve directory listings).

Manifest (data/manifest.json)

Accumulating from multiple workflows into the same repo

Each workflow automatically stores its data under data/<workflow-file-basename>/. You can drop the Action into multiple workflows without configuring track-name and the dashboard will display each workflow as a separate dataset.

Override track-name when you want to split one workflow into multiple tracks (e.g. matrix shards, environments):

- uses: hatsu38/ghtrack@<sha>
  with:
    track-name: e2e-shard-${{ matrix.shard }}

The dashboard reads the manifest, fetches each workflow's index.json, and renders a separate dataset per workflow. The Per-job chart uses ${workflow_file}::${job.name} as the key, so identically-named jobs (e.g. e2e_test) across multiple workflows do not get merged into one line.

Visualization (GitHub Pages)

On every run, the Action bundles index.html at the root of the gh-pages branch (auto-syncs if it changed). It loads Chart.js v4 + the date-fns adapter from a CDN (jsDelivr), reads ./data/manifest.json and each workflow's ./data/<track>/index.json, then fetches the per-run files that fall inside the active date range.

The default range is Last 30 days. Changing the range preset triggers an incremental fetch of any newly-needed per-run files (already-loaded ones are cached in memory).

Charts rendered per workflow:

  • Total duration per run: a line chart of each run's total_duration_sec
  • Per-job duration: a line chart with one dataset per job name. matrix permutations (name (N) / name (N, M) form) are aggregated under the base name by default, and max(matrix node duration) is shown for each run (the bottleneck node that determines wall-clock under parallel execution). A checkbox above the chart lets you expand the matrix into individual nodes (the setting is persisted in localStorage).

Light / dark theming follows prefers-color-scheme. Layout is responsive with viewport meta + max-width: 960px.

The dashboard UI defaults to English. When navigator.language starts with ja, labels and headings switch to Japanese automatically.

How to publish

  1. Make the repository public (or enable Pages on a GitHub Pro plan or higher)
  2. Settings → Pages:
    • Source: Deploy from a branch
    • Branch: gh-pages / /(root)
  3. After a few minutes, the charts appear at https://<owner>.github.io/<repo>/

If you only want to inspect the data, you can open https://<owner>.github.io/<repo>/data/manifest.json directly (and follow the listed paths).

Running locally

fetch() calls from index.html do not work when opened via file:// due to browser CORS policy (Chrome/Edge/Safari etc.). To check locally, check out the gh-pages branch and serve it via a local HTTP server:

# switch to the gh-pages branch (so index.html and data/* are at the root)
git switch gh-pages

# serve via any static server. With Node.js, npx is enough
npx serve .
# or
npx http-server -p 8000

Open http://localhost:<port>/ in the browser to see the same charts as production Pages.

Example (this repo's own dogfood)

Required permissions and notes

  • Always grant contents: write to the permissions: of the track job. Even when Repository Settings → Actions → Workflow permissions is set to "Read and write", declaring it explicitly in the workflow is recommended (so a Settings change does not silently break the action)
  • actions: read is also required on the track job (to call workflow run APIs)
  • Runs from fork PRs: GITHUB_TOKEN cannot write to the base repo, so the Action exits early with a core.notice and does not collect or push anything
  • Concurrent push contention: per-run files are append-only at unique paths, so they never conflict with each other. Only the per-workflow index.json is read-modify-write, and that small file is retried with exponential backoff + jitter (up to 10 attempts) when contended

Migration from the v1 single-file layout

If you have an existing gh-pages branch that used the previous data/<workflow>.json (entries[] aggregated) layout, run the one-shot migration script:

# 1. back up the old gh-pages on the remote
git fetch origin gh-pages
git push origin refs/remotes/origin/gh-pages:refs/heads/gh-pages-v1-backup
git push origin --delete gh-pages

# 2. check out the backup locally
git worktree add /tmp/ghtrack-v1 gh-pages-v1-backup

# 3. run the migration script (writes the v2 tree to a new dir)
node scripts/migrate-v1-to-v2.mjs /tmp/ghtrack-v1 /tmp/ghtrack-v2

# 4. push /tmp/ghtrack-v2 as the new gh-pages
cd /tmp/ghtrack-v2
git init -b gh-pages
git add .
git commit -m "chore(ghtrack): migrate to v2 per-run file layout"
git remote add origin <repo-url>
git push origin gh-pages

After step 4 the next ghtrack run will append in v2 mode without further changes. The gh-pages-v1-backup branch can be kept or deleted later.

Local development

pnpm install
pnpm typecheck
pnpm build   # bundle into dist/ (committed to the repo)

License

MIT