Skip to content

Commit 90758bc

Browse files
authored
feat: auto updater (#64)
1 parent 3f8aa61 commit 90758bc

File tree

13 files changed

+605
-6
lines changed

13 files changed

+605
-6
lines changed

.github/workflows/release.yml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: Publish Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: "Version tag (e.g. v0.1.0). Leave empty to reuse package.json."
11+
required: false
12+
type: string
13+
14+
permissions:
15+
contents: write
16+
17+
jobs:
18+
determine-version:
19+
runs-on: ubuntu-latest
20+
outputs:
21+
should_publish: ${{ steps.detect.outputs.should_publish || steps.manual.outputs.should_publish }}
22+
version: ${{ steps.detect.outputs.version || steps.manual.outputs.version }}
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v5
26+
with:
27+
fetch-depth: 0
28+
29+
- name: Manual version input
30+
if: github.event_name == 'workflow_dispatch'
31+
id: manual
32+
run: |
33+
VERSION="${{ inputs.tag }}"
34+
if [ -z "$VERSION" ]; then
35+
VERSION=$(jq -r .version package.json)
36+
fi
37+
VERSION="${VERSION#v}"
38+
if [ -z "$VERSION" ]; then
39+
echo "Failed to determine version for manual publish."
40+
exit 1
41+
fi
42+
echo "Using manual version $VERSION"
43+
echo "should_publish=true" >> "$GITHUB_OUTPUT"
44+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
45+
46+
- name: Detect version change on main
47+
if: github.event_name != 'workflow_dispatch'
48+
id: detect
49+
run: |
50+
if ! git rev-parse HEAD~1 >/dev/null 2>&1; then
51+
echo "Initial commit detected, skipping publish."
52+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
53+
exit 0
54+
fi
55+
CURRENT_VERSION=$(jq -r .version package.json)
56+
PREVIOUS_VERSION=$(git show HEAD~1:package.json | jq -r .version)
57+
if [ "$CURRENT_VERSION" = "$PREVIOUS_VERSION" ]; then
58+
echo "Version unchanged ($CURRENT_VERSION), skipping publish."
59+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
60+
exit 0
61+
fi
62+
echo "Detected version bump from $PREVIOUS_VERSION to $CURRENT_VERSION"
63+
echo "should_publish=true" >> "$GITHUB_OUTPUT"
64+
echo "version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
65+
66+
publish:
67+
needs: determine-version
68+
if: needs.determine-version.outputs.should_publish == 'true'
69+
runs-on: macos-latest
70+
env:
71+
GH_TOKEN: ${{ secrets.GH_PUBLISH_TOKEN }}
72+
GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_TOKEN }}
73+
NODE_ENV: production
74+
APP_VERSION: ${{ needs.determine-version.outputs.version }}
75+
steps:
76+
- name: Checkout
77+
uses: actions/checkout@v5
78+
with:
79+
fetch-depth: 0
80+
- name: Setup pnpm
81+
uses: pnpm/action-setup@v4
82+
with:
83+
version: 10.14.0
84+
- name: Setup Node.js
85+
uses: actions/setup-node@v4
86+
with:
87+
node-version: 22
88+
cache: "pnpm"
89+
- name: Install dependencies
90+
run: pnpm install --frozen-lockfile
91+
- name: Verify package version
92+
run: |
93+
PACKAGE_VERSION=$(jq -r .version package.json)
94+
if [ "$PACKAGE_VERSION" != "$APP_VERSION" ]; then
95+
echo "Package version $PACKAGE_VERSION does not match expected $APP_VERSION"
96+
exit 1
97+
fi
98+
- name: Create or reuse tag
99+
run: |
100+
TAG="v$APP_VERSION"
101+
git fetch --tags
102+
if git rev-parse "refs/tags/$TAG" >/dev/null 2>&1; then
103+
echo "Tag $TAG already exists, reusing it."
104+
else
105+
git config user.name "posthog-bot"
106+
git config user.email "[email protected]"
107+
git tag -a "$TAG" -m "Release $TAG"
108+
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }} "$TAG"
109+
fi
110+
- name: Build native modules
111+
run: pnpm run build-native
112+
- name: Publish with Electron Forge
113+
run: pnpm run publish

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ AGENTS.md
3333

3434
pnpm-workspace.yaml
3535

36-
**.car
36+
**.car
37+
38+
.envrc

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ Output will be in:
5555
pnpm build-native
5656
```
5757

58+
### Auto Updates & Releases
59+
60+
Array uses Electron's built-in `autoUpdater` pointed at the public `update.electronjs.org` service for `PostHog/Array`. Every time a non-draft GitHub release is published with the platform archives, packaged apps will automatically download and install the newest version on macOS and Windows.
61+
62+
Publishing a new release:
63+
64+
1. Export a GitHub token with `repo` scope as `GH_PUBLISH_TOKEN`; set both `GH_TOKEN` and `GITHUB_TOKEN` to its value locally (e.g., in `.envrc`). In GitHub, store the token as the `GH_PUBLISH_TOKEN` repository secret.
65+
2. Run `pnpm run make` locally to sanity check artifacts, then bump `package.json`'s version (e.g., `pnpm version patch`).
66+
3. Merge the version bump into `main`. The `Publish Release` GitHub Action auto-detects the new version, tags `vX.Y.Z`, runs `pnpm run publish`, and uploads the release artifacts. You can also run the workflow manually (`workflow_dispatch`) and supply a tag if you need to re-publish.
67+
68+
Set `ELECTRON_DISABLE_AUTO_UPDATE=1` if you ever need to ship a build with auto updates disabled.
69+
5870
### Liquid Glass Icon (macOS 26+)
5971

6072
The app supports macOS liquid glass icons for a modern, layered appearance. The icon configuration is in `build/icon.icon/`.
@@ -109,4 +121,4 @@ array/
109121
- `Enter` - Open selected task
110122
- `⌘R` - Refresh task list
111123
- `⌘⇧[/]` - Switch between tabs
112-
- `⌘W` - Close current tab
124+
- `⌘W` - Close current tab

forge.config.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,49 @@
11
import { execSync } from "node:child_process";
2-
import { existsSync } from "node:fs";
2+
import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
3+
import path from "node:path";
34
import { MakerDMG } from "@electron-forge/maker-dmg";
45
import { MakerZIP } from "@electron-forge/maker-zip";
56
import { VitePlugin } from "@electron-forge/plugin-vite";
7+
import { PublisherGithub } from "@electron-forge/publisher-github";
68
import type { ForgeConfig } from "@electron-forge/shared-types";
79

10+
function copyNativeDependency(
11+
dependency: string,
12+
destinationRoot: string,
13+
): void {
14+
const source = path.resolve("node_modules", dependency);
15+
if (!existsSync(source)) {
16+
console.warn(
17+
`[forge] Native dependency "${dependency}" not found, skipping copy`,
18+
);
19+
return;
20+
}
21+
22+
const nodeModulesDir = path.join(destinationRoot, "node_modules");
23+
mkdirSync(nodeModulesDir, { recursive: true });
24+
25+
const destination = path.join(nodeModulesDir, dependency);
26+
rmSync(destination, { recursive: true, force: true });
27+
cpSync(source, destination, { recursive: true, dereference: true });
28+
console.log(
29+
`[forge] Copied native dependency "${dependency}" into ${path.relative(
30+
process.cwd(),
31+
destination,
32+
)}`,
33+
);
34+
}
35+
836
const config: ForgeConfig = {
937
packagerConfig: {
10-
asar: true,
38+
asar: {
39+
unpack: "**/*.node",
40+
},
41+
prune: false,
1142
name: "Array",
1243
executableName: "Array",
1344
icon: "./build/app-icon", // Forge adds .icns/.ico/.png based on platform
45+
appBundleId: "com.posthog.array",
46+
appCategoryType: "public.app-category.productivity",
1447
extraResource: existsSync("build/Assets.car") ? ["build/Assets.car"] : [],
1548
extendInfo: existsSync("build/Assets.car")
1649
? {
@@ -52,7 +85,20 @@ const config: ForgeConfig = {
5285
}
5386
}
5487
},
88+
packageAfterCopy: async (_forgeConfig, buildPath) => {
89+
copyNativeDependency("node-pty", buildPath);
90+
},
5591
},
92+
publishers: [
93+
new PublisherGithub({
94+
repository: {
95+
owner: "PostHog",
96+
name: "Array",
97+
},
98+
draft: false,
99+
prerelease: false,
100+
}),
101+
],
56102
plugins: [
57103
new VitePlugin({
58104
build: [

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "array",
3-
"version": "0.1.0",
3+
"version": "0.1.2",
44
"description": "Array - PostHog desktop task manager",
55
"main": ".vite/build/index.js",
6+
"versionHash": "dynamic",
67
"engines": {
78
"node": ">=22.0.0",
89
"pnpm": ">=9.0.0"
@@ -38,6 +39,7 @@
3839
"@electron-forge/maker-squirrel": "^7.10.2",
3940
"@electron-forge/maker-zip": "^7.10.2",
4041
"@electron-forge/plugin-vite": "^7.10.2",
42+
"@electron-forge/publisher-github": "^7.10.2",
4143
"@electron-forge/shared-types": "^7.10.2",
4244
"@types/node": "^20.11.5",
4345
"@types/react": "^18.2.48",

0 commit comments

Comments
 (0)