Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Changesets

Add a `.md` file here in any PR that has user-visible changes.
Name it anything — `my-feature.md`, `fix-datatable.md`, etc.

When the PR is merged with a `release:*` label, `auto-release.yml`
folds all changeset files into `CHANGELOG.md` under the new version
and deletes the files.

## Format

Use the same headings as CHANGELOG.md:

```markdown
### New Features

#### Canvas Table
- **Feature name** — What it does and why it's useful

#### SQL Editor
- **Another feature** — Description

### Bug Fixes
- Fixed something that was broken

### Changes
- Removed or changed behaviour
```

Supported top-level headings (case-insensitive):
- `### New Features` — shows as ✨ New Features in the app
- `### Bug Fixes` — shows as 🐛 Bug Fixes
- `### Changes` — shows as 🔧 Improvements

Sub-sections (`####`) are optional but group items nicely in the release notes page.

If a PR has no user-visible changes (docs, CI, refactors), skip the changeset file.
17 changes: 17 additions & 0 deletions .changeset/improvements-and-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### New Features

#### Release Pipeline

- **In-app release notes** — the update dialog now shows a structured release notes page with ✨ New Features, 🐛 Bug Fixes, and 🔧 Improvements sections parsed from the changelog
- **Code signing** — builds are now signed on macOS (Developer ID + notarization) and Windows (certificate thumbprint via cert store) in CI

### Bug Fixes
- Fixed rel columns remaining visible in the canvas after being hidden from the columns panel — the toolbar was capped at showing 5 rel entries, so hiding all 5 caused DataTable to promote the next batch of FK tables into view with no way to hide them
- Fixed the `rel` badge not dimming visually when a relationship column is toggled to hidden in the columns panel
- Fixed the "Hide all" button in the columns panel not including relationship columns — clicking Hide now hides both regular and rel columns together

### Changes
- Renamed app references from **DB Studio** to **Stroke** across all files, workflows, and documentation
- Updated domain from `dbstudio.app` to `stroke.app` in license gate and about dialog
- Updated GitHub repo references from `broisnischal/studio` to `broisnischal/stroke` in updater endpoint, release workflow, and README
- Updated macOS bundle artifact name from `db-studio.app` to `Stroke.app` and all installer filenames from `db-studio_*` to `stroke_*` to match the `productName` in tauri config
185 changes: 185 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Triggered when a PR is merged into master.
#
# Label the PR with one of:
# release:patch → 0.0.x (bug fixes)
# release:minor → 0.x.0 (new features, backwards-compatible)
# release:major → x.0.0 (breaking changes)
#
# The workflow will:
# 1. Bump the version in tauri.conf.json + package.json
# 2. Fold .changeset/*.md files into CHANGELOG.md under the new version
# 3. Commit the bump + changelog to master
# 4. Push tag vX.Y.Z → triggers release.yml to build and publish

name: Auto Release

on:
pull_request:
types: [closed]
branches: [master]

permissions:
contents: write

jobs:
auto-release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# If master has branch protection that blocks the default bot token,
# replace with: token: ${{ secrets.RELEASE_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/setup-node@v4
with:
node-version: 22

# ── 1. Determine bump type from PR labels ─────────────────────────────
- name: Resolve bump type
id: bump
run: |
LABELS='${{ toJson(github.event.pull_request.labels.*.name) }}'
echo "PR labels: $LABELS"

if echo "$LABELS" | grep -q '"release:major"'; then echo "type=major" >> "$GITHUB_OUTPUT"
elif echo "$LABELS" | grep -q '"release:minor"'; then echo "type=minor" >> "$GITHUB_OUTPUT"
elif echo "$LABELS" | grep -q '"release:patch"'; then echo "type=patch" >> "$GITHUB_OUTPUT"
else
echo "type=skip" >> "$GITHUB_OUTPUT"
echo "No release:* label — skipping release."
fi

# ── 2. Bump versions + fold changesets into CHANGELOG.md ──────────────
- name: Bump version and update changelog
if: steps.bump.outputs.type != 'skip'
id: version
env:
BUMP_TYPE: ${{ steps.bump.outputs.type }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
node - <<'EOF'
const fs = require('fs')
const path = require('path')
const bump = process.env.BUMP_TYPE

// ── Compute new version ──────────────────────────────────────────
const cfgPath = 'src-tauri/tauri.conf.json'
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'))
const [maj, min, pat] = cfg.version.split('.').map(Number)

const next =
bump === 'major' ? `${maj + 1}.0.0` :
bump === 'minor' ? `${maj}.${min + 1}.0` :
`${maj}.${min}.${pat + 1}`

// ── Update tauri.conf.json ───────────────────────────────────────
cfg.version = next
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + '\n')

// ── Update package.json ──────────────────────────────────────────
const pkgPath = 'package.json'
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
pkg.version = next
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')

// ── Collect .changeset/*.md files ────────────────────────────────
const csDir = '.changeset'
const files = fs.existsSync(csDir)
? fs.readdirSync(csDir).filter(f => f.endsWith('.md') && f !== 'README.md')
: []

// sections: { 'New Features': { '#### Canvas Table': ['- item'] } }
/** @type {Record<string, Record<string, string[]>>} */
const sections = {}

for (const file of files) {
const content = fs.readFileSync(path.join(csDir, file), 'utf8')
let h3 = null
let h4 = null

for (const line of content.split('\n')) {
const t = line.trim()
if (t.startsWith('### ')) {
h3 = t.slice(4).trim()
if (!sections[h3]) sections[h3] = {}
h4 = null
} else if (t.startsWith('#### ')) {
h4 = t // keep full "#### Foo"
if (h3 && !sections[h3][h4]) sections[h3][h4] = []
} else if (/^[-*]\s/.test(t) && h3) {
const key = h4 ?? '__top__'
if (!sections[h3][key]) sections[h3][key] = []
sections[h3][key].push(t)
}
}
}

// ── Build the new CHANGELOG entry ────────────────────────────────
const today = new Date().toISOString().slice(0, 10)
const order = ['New Features', 'Bug Fixes', 'Changes']
const allKeys = [...new Set([...order, ...Object.keys(sections)])]
let entry = `## [${next}] - ${today}\n`
let hasContent = false

for (const heading of allKeys) {
if (!sections[heading] || Object.keys(sections[heading]).length === 0) continue
hasContent = true
entry += `\n### ${heading}\n`

// Top-level bullets (no sub-section)
const top = sections[heading]['__top__'] ?? []
for (const item of top) entry += `${item}\n`

// Sub-sections
for (const [sub, items] of Object.entries(sections[heading])) {
if (sub === '__top__') continue
entry += `\n${sub}\n`
for (const item of items) entry += `${item}\n`
}
}

if (!hasContent) {
// No changeset files — generate a minimal entry from PR title
const pr = process.env.PR_TITLE || 'No changelog provided'
const num = process.env.PR_NUMBER
entry += `\n### Changes\n- ${pr}${num ? ` (#${num})` : ''}\n`
}

// ── Inject at top of CHANGELOG.md (after the "---" separator) ───
const clPath = 'CHANGELOG.md'
const cl = fs.existsSync(clPath) ? fs.readFileSync(clPath, 'utf8') : '# Changelog\n\n---\n'
const sep = '---\n'
const idx = cl.indexOf(sep)
const before = idx !== -1 ? cl.slice(0, idx + sep.length) : cl
const after = idx !== -1 ? cl.slice(idx + sep.length) : ''
fs.writeFileSync(clPath, `${before}\n${entry}\n${after}`)

// ── Delete consumed changeset files ──────────────────────────────
for (const file of files) fs.unlinkSync(path.join(csDir, file))

console.log(`v${next} — merged ${files.length} changeset(s)`)
fs.appendFileSync(process.env.GITHUB_OUTPUT, `new_version=${next}\n`)
EOF

# ── 3. Commit everything and push the release tag ─────────────────────
- name: Commit and tag
if: steps.bump.outputs.type != 'skip'
env:
NEW_TAG: v${{ steps.version.outputs.new_version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git add src-tauri/tauri.conf.json package.json CHANGELOG.md .changeset/
git commit -m "chore: release ${{ steps.version.outputs.new_version }}"

git tag "$NEW_TAG"
git push origin master
git push origin "$NEW_TAG"

echo "✅ $NEW_TAG pushed — release.yml will now build and publish."
Loading
Loading