Skip to content

Sync Changelog to Supabase #1

Sync Changelog to Supabase

Sync Changelog to Supabase #1

name: Sync Changelog to Supabase
on:
push:
branches: [main]
paths:
- 'changelog.md'
workflow_dispatch:
env:
SUPABASE_URL: https://gulptwduchsjcsbndmua.supabase.co
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Parse changelog.md and sync to Supabase
run: |
node << 'SCRIPT'
const fs = require('fs');
const https = require('https');
const content = fs.readFileSync('changelog.md', 'utf8');
const lines = content.split('\n');
const typeMap = {
'New': 'feature',
'Improved': 'improvement',
'Fixed': 'fix',
'Breaking': 'breaking',
};
const productMap = {
'Dashboard': 'dashboard',
'Agents': 'agents',
'Integrations': 'integrations',
'Platform': 'platform',
'DOSClaw': 'dosclaw',
'API Gateway': 'gateway',
'DOSafe': 'dosafe',
'Inference': 'inference',
};
const entries = [];
let currentDate = null;
let currentType = null;
for (const line of lines) {
// ## March 27, 2026
const dateMatch = line.match(/^## (.+)$/);
if (dateMatch) {
const d = new Date(dateMatch[1].trim());
currentDate = isNaN(d) ? null : d.toISOString().split('T')[0];
currentType = null;
continue;
}
// ### New / Fixed / Improved / Breaking
const typeMatch = line.match(/^### (.+)$/);
if (typeMatch) {
currentType = typeMap[typeMatch[1].trim()] || null;
continue;
}
// - `Product` **Title** — Description
const entryMatch = line.match(/^- `([^`]+)` \*\*([^*]+)\*\* — (.+)$/);
if (entryMatch && currentDate && currentType) {
const product = productMap[entryMatch[1]] || entryMatch[1].toLowerCase();
entries.push({
date: currentDate,
product,
type: currentType,
title: entryMatch[2].trim(),
description: entryMatch[3].trim(),
source: 'auto',
});
}
}
if (entries.length === 0) {
console.log('No entries parsed');
process.exit(0);
}
console.log(`Parsed ${entries.length} entries`);
// Call Supabase RPC
const body = JSON.stringify({
p_entries: entries,
p_write_key: process.env.CHANGELOG_WRITE_KEY,
});
const url = new URL(`${process.env.SUPABASE_URL}/rest/v1/rpc/insert_changelog`);
const options = {
hostname: url.hostname,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': process.env.SUPABASE_ANON_KEY,
'Authorization': `Bearer ${process.env.SUPABASE_ANON_KEY}`,
'Content-Profile': 'dosai',
'Content-Length': Buffer.byteLength(body),
},
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
console.log(`Supabase response (${res.statusCode}):`, data);
process.exit(res.statusCode >= 200 && res.statusCode < 300 ? 0 : 1);
});
});
req.on('error', (e) => { console.error(e); process.exit(1); });
req.write(body);
req.end();
SCRIPT
env:
SUPABASE_URL: ${{ env.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
CHANGELOG_WRITE_KEY: ${{ secrets.CHANGELOG_WRITE_KEY }}