Skip to content

Commit b8096dd

Browse files
committed
release: v2.3.1 — dynamic website changelog, auto release notes
- Website changelog now fetches from GitHub Releases API - CI auto-generates release notes from git log - Pages deploys on tag push - No more manual website updates for releases
2 parents 4e72f56 + 0b6e698 commit b8096dd

4 files changed

Lines changed: 104 additions & 93 deletions

File tree

.github/workflows/build.yml

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,28 +131,42 @@ jobs:
131131
id: version
132132
run: echo "version=$(grep 'MARKETING_VERSION' project.yml | head -1 | awk '{print $2}' | tr -d '\"')" >> $GITHUB_OUTPUT
133133

134+
- name: Generate release notes
135+
id: notes
136+
run: |
137+
# Get commits since last tag
138+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
139+
if [ -n "$PREV_TAG" ]; then
140+
CHANGES=$(git log "$PREV_TAG"..HEAD --pretty=format:"- %s" --no-merges | grep -v "^- chore:" | grep -v "^- Merge" | head -20)
141+
else
142+
CHANGES="- Initial release"
143+
fi
144+
145+
# Write to file for multiline support
146+
cat > build/release-notes.md << 'HEADER'
147+
## DX Tools v${{ steps.version.outputs.version }}
148+
149+
### Downloads
150+
- **DXTools.dmg** — macOS app (drag to Applications)
151+
- **dx** — CLI binary (copy to `~/bin/` or `/usr/local/bin/`)
152+
153+
### Install via Homebrew
154+
```
155+
brew tap openstruct/tap && brew install dx-tools
156+
```
157+
158+
### Changes
159+
HEADER
160+
161+
echo "$CHANGES" >> build/release-notes.md
162+
134163
- name: Create GitHub Release
135164
uses: softprops/action-gh-release@v2
136165
with:
137166
tag_name: v${{ steps.version.outputs.version }}
138167
name: DX Tools v${{ steps.version.outputs.version }}
139168
make_latest: true
140-
body: |
141-
## DX Tools v${{ steps.version.outputs.version }}
142-
143-
### Downloads
144-
- **DXTools.dmg** — macOS app (drag to Applications)
145-
- **dx** — CLI binary (copy to `~/bin/` or `/usr/local/bin/`)
146-
147-
### Install via Homebrew
148-
```
149-
brew tap openstruct/tap && brew install dx-tools
150-
```
151-
152-
### What's Included
153-
- 33 developer tools in one native macOS app
154-
- CLI with 10 subcommands: json, jwt, epoch, env, hash, base64, uuid, color, pass, port
155-
- 300 tests, zero dependencies, ~3 MB
169+
body_path: build/release-notes.md
156170
files: |
157171
build/DXTools.dmg
158172
.build/release/dx

.github/workflows/pages.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches: [main]
66
paths: ['web/**']
7+
tags: ['v*']
78
workflow_dispatch:
89

910
permissions:

project.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ settings:
1010
base:
1111
SWIFT_VERSION: "5.9"
1212
MACOSX_DEPLOYMENT_TARGET: "14.0"
13-
MARKETING_VERSION: "2.3.0"
13+
MARKETING_VERSION: "2.3.1"
1414
CURRENT_PROJECT_VERSION: 1
1515

1616
targets:

web/index.html

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -393,71 +393,8 @@ <h3>Pro Code Editor</h3>
393393
<h2 class="section-title">What's New</h2>
394394
<p class="section-sub">Every release, every fix, every improvement.</p>
395395

396-
<div class="changelog-timeline">
397-
<div class="cl-entry latest">
398-
<div class="cl-version">
399-
<span class="v">v2.2.0</span>
400-
<span class="badge-new">Latest</span>
401-
<span class="date">March 15, 2026</span>
402-
</div>
403-
<ul class="cl-list">
404-
<li><span class="cl-icon cl-fix"></span> API Request Builder — params now correctly applied to requests</li>
405-
<li><span class="cl-icon cl-improve"></span> Port Manager — redesigned kill buttons with outline style, hover reveal</li>
406-
<li><span class="cl-icon cl-improve"></span> QR Code — shorter correction level picker (L/M/Q/H segments)</li>
407-
<li><span class="cl-icon cl-improve"></span> All 33 tools use single clean ToolHeader — no more double headers</li>
408-
<li><span class="cl-icon cl-fix"></span> 300 tests, 0 failures across 25 test suites</li>
409-
</ul>
410-
</div>
411-
412-
<div class="cl-entry">
413-
<div class="cl-version">
414-
<span class="v">v2.1.0</span>
415-
<span class="date">March 15, 2026</span>
416-
</div>
417-
<ul class="cl-list">
418-
<li><span class="cl-icon cl-new"></span> QR Code Generator — 4 error correction levels, copy/save PNG</li>
419-
<li><span class="cl-icon cl-new"></span> Image Base64 — encode images to base64/data URI, decode back</li>
420-
<li><span class="cl-icon cl-new"></span> SQL Formatter — format & minify SQL, 3 indent styles</li>
421-
<li><span class="cl-icon cl-new"></span> JSON Schema Validator — type, required, min/max, pattern, arrays</li>
422-
<li><span class="cl-icon cl-new"></span> HTTP Status Codes — searchable reference, color-coded categories</li>
423-
<li><span class="cl-icon cl-new"></span> Input syntax highlighting — live JSON/SQL coloring in editor</li>
424-
<li><span class="cl-icon cl-new"></span> History panel — browse & restore past inputs per tool</li>
425-
<li><span class="cl-icon cl-new"></span> URL handler — <code>dx://tool-name</code> opens app to specific tool</li>
426-
<li><span class="cl-icon cl-new"></span> Auto-update checker — checks GitHub Releases on launch</li>
427-
<li><span class="cl-icon cl-improve"></span> Custom app icon — orange "DX" monogram on dark background</li>
428-
<li><span class="cl-icon cl-improve"></span> Settings view with font size, indent style, theme toggle</li>
429-
<li><span class="cl-icon cl-improve"></span> Onboarding — 3-screen first-launch walkthrough</li>
430-
<li><span class="cl-icon cl-improve"></span> Command Palette — ⌘K overlay with fuzzy search</li>
431-
</ul>
432-
</div>
433-
434-
<div class="cl-entry">
435-
<div class="cl-version">
436-
<span class="v">v2.0.0</span>
437-
<span class="date">March 15, 2026</span>
438-
</div>
439-
<ul class="cl-list">
440-
<li><span class="cl-icon cl-new"></span> 23 developer tools in one native macOS app</li>
441-
<li><span class="cl-icon cl-new"></span> Port Manager, Network Info, URL Encoder, Unix Permissions, Cron Parser</li>
442-
<li><span class="cl-icon cl-new"></span> SSH Key Generator, Docker Manager, Git Stats, Timestamp Converter, Text Diff</li>
443-
<li><span class="cl-icon cl-new"></span> API Request Builder, smart clipboard, menu bar, CLI companion</li>
444-
<li><span class="cl-icon cl-improve"></span> Pro code editor, dark/light theme, favorites system</li>
445-
</ul>
446-
</div>
447-
448-
<div class="cl-entry">
449-
<div class="cl-version">
450-
<span class="v">v1.0.0</span>
451-
<span class="date">Initial Release</span>
452-
</div>
453-
<ul class="cl-list">
454-
<li><span class="cl-icon cl-new"></span> JSON Formatter, JSON → Go/Swift/TypeScript, JSON Diff</li>
455-
<li><span class="cl-icon cl-new"></span> JWT Decoder, Base64, Hash Generator</li>
456-
<li><span class="cl-icon cl-new"></span> UUID Generator, Color Converter, Epoch Converter</li>
457-
<li><span class="cl-icon cl-new"></span> Password Generator, Env Manager</li>
458-
<li><span class="cl-icon cl-new"></span> cURL → Code, Regex Tester, Markdown Preview, Lorem Generator</li>
459-
</ul>
460-
</div>
396+
<div class="changelog-timeline" id="changelog-timeline">
397+
<div class="cl-entry"><div class="cl-version"><span class="v" style="color:var(--text3)">Loading releases…</span></div></div>
461398
</div>
462399
</section>
463400

@@ -568,29 +505,88 @@ <h2 class="section-title">Keyboard-First</h2>
568505
setTimeout(() => toast.classList.remove('show'), 2000);
569506
}
570507

571-
// Fetch latest release info from GitHub API
572-
async function fetchRelease() {
508+
// Classify a changelog line as new/improve/fix
509+
function classifyLine(line) {
510+
const l = line.toLowerCase();
511+
if (l.startsWith('- feat') || l.includes('new tool') || l.includes('added') || l.match(/^- \*\*\w.*\*\* .*new/))
512+
return 'new';
513+
if (l.startsWith('- fix') || l.includes('bug') || l.includes('fixed') || l.includes('hotfix'))
514+
return 'fix';
515+
return 'improve';
516+
}
517+
518+
function lineIcon(type) {
519+
if (type === 'new') return '<span class="cl-icon cl-new">✦</span>';
520+
if (type === 'fix') return '<span class="cl-icon cl-fix">●</span>';
521+
return '<span class="cl-icon cl-improve">◆</span>';
522+
}
523+
524+
// Parse release body into clean bullet points
525+
function parseBody(body) {
526+
if (!body) return [];
527+
return body.split('\n')
528+
.map(l => l.trim())
529+
.filter(l => l.startsWith('- ') || l.startsWith('* '))
530+
.map(l => l.replace(/^[-*]\s+/, '').replace(/\*\*/g, ''))
531+
.filter(l => l.length > 0 && !l.startsWith('#') && !l.startsWith('```'));
532+
}
533+
534+
// Format date
535+
function fmtDate(dateStr) {
536+
const d = new Date(dateStr);
537+
return d.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
538+
}
539+
540+
// Fetch all releases and render changelog + hero
541+
async function fetchReleases() {
573542
try {
574-
const res = await fetch('https://api.github.com/repos/OpenStruct/dx-tools/releases/latest');
543+
const res = await fetch('https://api.github.com/repos/OpenStruct/dx-tools/releases?per_page=20');
575544
if (!res.ok) return;
576-
const data = await res.json();
545+
const releases = await res.json();
546+
if (!releases.length) return;
577547

578-
// Update version badge
548+
// Update hero version
549+
const latest = releases[0];
579550
const ver = document.getElementById('hero-version');
580-
if (ver && data.tag_name) ver.textContent = data.tag_name;
551+
if (ver) ver.textContent = latest.tag_name;
581552

582-
// Update download link to DMG asset
583-
const dmg = data.assets?.find(a => a.name.endsWith('.dmg'));
553+
// Update download button
554+
const dmg = latest.assets?.find(a => a.name.endsWith('.dmg'));
584555
if (dmg) {
585556
const btn = document.getElementById('dl-btn-main');
586557
if (btn) btn.href = dmg.browser_download_url;
587-
588558
const size = document.getElementById('dl-size');
589559
if (size) size.textContent = (dmg.size / 1024 / 1024).toFixed(1) + ' MB';
590560
}
591-
} catch (e) { /* silently fail, defaults work */ }
561+
562+
// Render changelog
563+
const timeline = document.getElementById('changelog-timeline');
564+
if (!timeline) return;
565+
566+
timeline.innerHTML = releases.map((rel, i) => {
567+
const isLatest = i === 0;
568+
const lines = parseBody(rel.body);
569+
const bullets = lines.length > 0
570+
? lines.map(l => {
571+
const type = classifyLine('- ' + l);
572+
return `<li>${lineIcon(type)} ${l}</li>`;
573+
}).join('')
574+
: `<li>${lineIcon('new')} ${rel.name || rel.tag_name}</li>`;
575+
576+
return `
577+
<div class="cl-entry${isLatest ? ' latest' : ''}">
578+
<div class="cl-version">
579+
<span class="v">${rel.tag_name}</span>
580+
${isLatest ? '<span class="badge-new">Latest</span>' : ''}
581+
<span class="date">${fmtDate(rel.published_at || rel.created_at)}</span>
582+
</div>
583+
<ul class="cl-list">${bullets}</ul>
584+
</div>`;
585+
}).join('');
586+
587+
} catch (e) { /* silently fail */ }
592588
}
593-
fetchRelease();
589+
fetchReleases();
594590
</script>
595591
</body>
596592
</html>

0 commit comments

Comments
 (0)