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
8 changes: 3 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:
sudo snap install snapcraft --classic

- name: Install Frontend Dependencies
run: npm install
run: npm ci

# --- Windows Build (x64) ---
- name: Build Windows x64
Expand All @@ -128,8 +128,7 @@ jobs:
run: |
VERSION="${{ needs.create-release.outputs.version }}"
cp src-tauri/target/release/Markpad.exe "Markpad_${VERSION}_x64.exe"
cp "Markpad_${VERSION}_x64.exe" "MarkpadInstaller_${VERSION}_x64.exe"
gh release upload v$VERSION "Markpad_${VERSION}_x64.exe" "MarkpadInstaller_${VERSION}_x64.exe" --clobber
gh release upload v$VERSION "Markpad_${VERSION}_x64.exe" --clobber
find src-tauri/target/release/bundle/nsis -name "*-setup.exe" -exec gh release upload v$VERSION {} --clobber \;
find src-tauri/target/release/bundle/nsis -name "*-setup.exe.sig" -exec gh release upload v$VERSION {} --clobber \;

Expand Down Expand Up @@ -171,8 +170,7 @@ jobs:
run: |
VERSION="${{ needs.create-release.outputs.version }}"
cp src-tauri/target/aarch64-pc-windows-msvc/release/Markpad.exe "Markpad_${VERSION}_arm64.exe"
cp "Markpad_${VERSION}_arm64.exe" "MarkpadInstaller_${VERSION}_arm64.exe"
gh release upload v$VERSION "Markpad_${VERSION}_arm64.exe" "MarkpadInstaller_${VERSION}_arm64.exe" --clobber
gh release upload v$VERSION "Markpad_${VERSION}_arm64.exe" --clobber
find src-tauri/target/aarch64-pc-windows-msvc/release/bundle/nsis -name "*-setup.exe" -exec gh release upload v$VERSION {} --clobber \;
find src-tauri/target/aarch64-pc-windows-msvc/release/bundle/nsis -name "*-setup.exe.sig" -exec gh release upload v$VERSION {} --clobber \;

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ Download the latest executable or installer from the [releases page](https://git
- Run `npm run tauri build` to build the executable
- [Optional] Rename to `MarkpadInstaller.exe` to run as installer

### Isolated macOS test bundle

For local verification without opening or replacing `/Applications/Markpad.app`, build an unsigned test-only app with an independent identifier:

```bash
MARKPAD_TEST_BUNDLE_ID=dev.example.markpad.test npm run build:test-bundle
```

The result is placed in `dist/test-bundle/`. It is not a distributable release: it has no Developer ID notarization or Windows Authenticode signature.

## Issues & Feedback

If you find a bug, have a feature request, or just want to leave some feedback, please [open an issue](https://github.com/alecdotdev/Markpad/issues/new/choose). I'm actively developing Markpad and love hearing from users!
Expand Down
6 changes: 4 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ If you ever lose the private key:

## Per-release workflow

The workflow uses `npm ci`, so its installed dependency graph is exactly the committed lockfile. Do not replace it with `npm install` in release jobs.

1. **Bump version in both files** (mandatory — Tauri reads runtime version from `Cargo.toml`):
- [`package.json`](package.json) `version`
- [`src-tauri/Cargo.toml`](src-tauri/Cargo.toml) `[package].version`
Expand All @@ -59,8 +61,8 @@ If you ever lose the private key:
4. **Wait** ~30 min for matrix builds to finish, plus ~2 min for `generate-update-feed`.
5. **Open the draft release** on the [Releases page](https://github.com/alecdotdev/Markpad/releases). Verify the assets:
- **macOS**: `*.dmg`, `*.app.tar.gz`, `*.app.tar.gz.sig`
- **Windows x64**: `*_x64.exe` (portable), `*_x64-setup.exe` (NSIS installer), `*_x64-setup.exe.sig`
- **Windows ARM64**: `*_arm64.exe` (portable), `*_arm64-setup.exe` (NSIS installer), `*_arm64-setup.exe.sig`
- **Windows x64**: `Markpad_<version>_x64.exe` (portable), `*_x64-setup.exe` (NSIS installer), `*_x64-setup.exe.sig`
- **Windows ARM64**: `Markpad_<version>_arm64.exe` (portable), `*_arm64-setup.exe` (NSIS installer), `*_arm64-setup.exe.sig`
- **Linux**: `*.deb`, `*.rpm`, `*.AppImage`, `*.AppImage.sig`
- **Update feed**: `latest.json` (one entry per successfully built platform)
6. **Click "Publish release"** — this is the gate that activates auto-update for all clients pointing at `releases/latest/download/latest.json`.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"test:settings-scroll": "node --test --import tsx scripts/previewScrollSync.test.ts scripts/toolbarCustomizationWiring.test.ts",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"build:test-bundle": "node scripts/build-test-bundle.mjs",
"tauri": "tauri"
},
"license": "MIT",
Expand Down
41 changes: 41 additions & 0 deletions scripts/build-test-bundle.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
import { resolve } from 'node:path';
import { execFileSync } from 'node:child_process';

const root = process.cwd();
const identifier = process.env.MARKPAD_TEST_BUNDLE_ID;

if (!identifier || !identifier.startsWith('dev.') || identifier === 'com.alecdotdev.markpad') {
throw new Error('Set MARKPAD_TEST_BUNDLE_ID to a non-production identifier beginning with dev.');
}

const config = JSON.stringify({
identifier,
productName: 'Markpad 2.7.0 Test',
bundle: { targets: ['app'], createUpdaterArtifacts: false },
});
const rustRoot = resolve(root, '.local-rust');
const env = {
...process.env,
CARGO_HOME: resolve(rustRoot, 'cargo'),
RUSTUP_HOME: resolve(rustRoot, 'rustup'),
PATH: `${resolve(rustRoot, 'rustup/toolchains/stable-aarch64-apple-darwin/bin')}:${process.env.PATH}`,
};

execFileSync('npm', ['run', 'tauri', '--', 'build', '--bundles', 'app', '--config', config], {
cwd: root,
env,
stdio: 'inherit',
});

const macosDir = resolve(root, 'src-tauri/target/release/bundle/macos');
const appName = readdirSync(macosDir).find((entry) => entry.endsWith('.app'));
if (!appName) throw new Error('Tauri did not produce a macOS .app bundle.');

const outputDir = resolve(root, 'dist/test-bundle');
const output = resolve(outputDir, appName);
mkdirSync(outputDir, { recursive: true });
rmSync(output, { recursive: true, force: true });
cpSync(resolve(macosDir, appName), output, { recursive: true });
if (!existsSync(output)) throw new Error(`Failed to copy test bundle to ${output}`);
console.log(`Test bundle: ${output}`);
20 changes: 20 additions & 0 deletions scripts/releaseWorkflow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';

const workflow = readFileSync('.github/workflows/build.yml', 'utf8');
const packageJson = JSON.parse(readFileSync('package.json', 'utf8')) as { scripts: Record<string, string> };

test('release builds install the locked dependency graph', () => {
assert.match(workflow, /name: Install Frontend Dependencies\s+run: npm ci/);
assert.doesNotMatch(workflow, /npm install/);
});

test('portable executables are not mislabeled as installers', () => {
assert.doesNotMatch(workflow, /MarkpadInstaller_/);
assert.match(workflow, /bundle\/nsis.*-setup\.exe/);
});

test('the test bundle command uses an isolated builder', () => {
assert.equal(packageJson.scripts['build:test-bundle'], 'node scripts/build-test-bundle.mjs');
});
Loading