[object Object]

← back to build-pages

csv: /fleet.csv — every commit across every project in one file + 3 smoke

f2d736354ed832302618dbd746672e62e5c684d1 · 2026-05-13 14:48:54 -0700 · SteveStudio2

Files touched

Diff

commit f2d736354ed832302618dbd746672e62e5c684d1
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 14:48:54 2026 -0700

    csv: /fleet.csv — every commit across every project in one file + 3 smoke
---
 server.js     | 25 ++++++++++++++++++++++++-
 test/smoke.js |  8 +++++++-
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/server.js b/server.js
index c9d2065..5d3ef07 100644
--- a/server.js
+++ b/server.js
@@ -648,6 +648,28 @@ app.get('/api/projects/:slug/commits', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// Fleet-wide CSV — every commit across every project in one file, sorted by
+// date desc. Adds a `project` column so the consumer can slice/filter. Use:
+// `curl https://projects.agentabrams.com/fleet.csv > fleet.csv && open fleet.csv`.
+app.get('/fleet.csv', async (_req, res, next) => {
+  try {
+    const q = (s) => `"${String(s ?? '').replace(/"/g, '""')}"`;
+    const all = [];
+    for (const [slug, p] of Object.entries(PROJECTS)) {
+      const bundle = await getProjectBundle(slug, p).catch(() => null);
+      if (!bundle) continue;
+      for (const c of bundle.commits) {
+        all.push([q(slug), q(c.shortHash), q(c.dateISO), q(c.author), q(c.subject), c.body.length]);
+      }
+    }
+    all.sort((a, b) => (b[2] < a[2] ? -1 : b[2] > a[2] ? 1 : 0));
+    const rows = ['project,hash,date,author,subject,body_len'].concat(all.map(r => r.join(',')));
+    res.setHeader('Cache-Control', 'public, max-age=60, stale-while-revalidate=120');
+    res.setHeader('Content-Disposition', 'attachment; filename="fleet-commits.csv"');
+    res.type('text/csv').send(rows.join('\n') + '\n');
+  } catch (e) { next(e); }
+});
+
 // CSV export — every commit in spreadsheet form. Useful for the "drop this
 // into Numbers and slice by date / author" workflow. Header row + RFC-4180
 // quoting (double-quote inside double-quoted field).
@@ -744,7 +766,8 @@ app.get('/api', (_req, res) => {
       'GET /p/:slug':                          'Project journal HTML page (?q= deep-link supported)',
       'GET /p/:slug/c/:hash':                  'Commit detail page — full diff + body',
       'GET /p/:slug/feed.atom':                'Atom 1.0 feed — up to 50 recent commits, autodiscovered on project page',
-      'GET /p/:slug/commits.csv':              'Spreadsheet export — every commit as CSV (hash, date, author, subject, body_len)',
+      'GET /p/:slug/commits.csv':              'Per-project commits as CSV (hash, date, author, subject, body_len)',
+      'GET /fleet.csv':                        'Fleet-wide commits CSV — every commit across every project, date-sorted desc',
       'GET /p/:slug/file?path=<path>':         'View any tracked file at HEAD',
       'GET /sitemap.xml':                      'XML sitemap — home + every /p/:slug',
       'GET /robots.txt':                       'Crawler policy (allow all, sitemap advertised)',
diff --git a/test/smoke.js b/test/smoke.js
index 9ecd240..5596085 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -167,13 +167,19 @@ function check(name, cond, hint = '') {
   r = await fetch('/p/butlr');
   check('butlr: feed autodiscover',/rel="alternate" type="application\/atom\+xml"/.test(r.body));
 
-  // 11b. CSV export
+  // 11b. CSV export (per project)
   r = await fetch('/p/butlr/commits.csv');
   check('csv: 200',                r.status === 200);
   check('csv: text/csv',           /text\/csv/.test(r.headers['content-type'] || ''));
   check('csv: header row',         /^hash,date,author,subject,body_len/.test(r.body));
   check('csv: attachment',         /attachment;.*butlr-commits/.test(r.headers['content-disposition'] || ''));
 
+  // 11c. CSV export (fleet-wide)
+  r = await fetch('/fleet.csv');
+  check('fleet csv: 200',          r.status === 200);
+  check('fleet csv: project col',  /^project,hash,date,author,subject,body_len/.test(r.body));
+  check('fleet csv: butlr in mix', /"butlr",/.test(r.body));
+
   // 12. /api/projects/:slug/commits — paginated raw JSON
   r = await fetch('/api/projects/butlr/commits?limit=3');
   check('commits api: 200',        r.status === 200);

← fedcbfa csv: /p/:slug/commits.csv spreadsheet export (RFC-4180 quoti  ·  back to build-pages  ·  ui: visible export affordances — fleet.csv in footer + commi 29d618d →