Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

Commit 6c0d98e

Browse files
authored
Merge pull request #18 from closedloop-ai/FEAT-63
Add automated release workflow with Slack notifications
2 parents 95ad44e + 5cdcfb3 commit 6c0d98e

4 files changed

Lines changed: 159 additions & 1 deletion

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: 'Slack Notification'
2+
description: 'Send a notification to Slack via incoming webhook'
3+
inputs:
4+
webhook_url:
5+
description: 'Slack webhook URL'
6+
required: true
7+
message:
8+
description: 'Message text (supports Slack mrkdwn and \n for newlines)'
9+
required: true
10+
11+
runs:
12+
using: 'composite'
13+
steps:
14+
- name: Send Slack notification
15+
shell: bash
16+
env:
17+
SLACK_WEBHOOK_URL: ${{ inputs.webhook_url }}
18+
SLACK_MESSAGE: ${{ inputs.message }}
19+
run: |
20+
PAYLOAD=$(jq -n --arg text "$SLACK_MESSAGE" '{text: $text}')
21+
curl --fail-with-body -X POST -H 'Content-type: application/json' \
22+
--data "$PAYLOAD" \
23+
"$SLACK_WEBHOOK_URL"

.github/workflows/release.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Release Desktop
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "apps/desktop/**"
8+
9+
concurrency:
10+
group: release-desktop
11+
cancel-in-progress: false
12+
13+
env:
14+
SKIP: "false"
15+
16+
jobs:
17+
release:
18+
runs-on: macos-latest
19+
timeout-minutes: 30
20+
permissions:
21+
contents: write
22+
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v4
26+
27+
- name: Read version from package.json
28+
id: version
29+
run: |
30+
VERSION=$(node -p "require('./apps/desktop/package.json').version")
31+
echo "version=$VERSION" >> $GITHUB_OUTPUT
32+
33+
- name: Check if version already released
34+
id: check_release
35+
env:
36+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37+
run: |
38+
VERSION="${{ steps.version.outputs.version }}"
39+
if gh release view "v$VERSION" --repo "${{ github.repository }}" > /dev/null 2>&1; then
40+
echo "skip=true" >> $GITHUB_OUTPUT
41+
echo "::warning::Version $VERSION already released — skipping build. Bump the version in apps/desktop/package.json to trigger a new release."
42+
else
43+
echo "skip=false" >> $GITHUB_OUTPUT
44+
fi
45+
46+
- name: Install pnpm
47+
if: steps.check_release.outputs.skip != 'true'
48+
uses: pnpm/action-setup@v4
49+
50+
- name: Setup Node.js
51+
if: steps.check_release.outputs.skip != 'true'
52+
uses: actions/setup-node@v4
53+
with:
54+
node-version: 22
55+
cache: pnpm
56+
57+
- name: Install dependencies
58+
if: steps.check_release.outputs.skip != 'true'
59+
run: pnpm install --frozen-lockfile
60+
61+
- name: Typecheck
62+
if: steps.check_release.outputs.skip != 'true'
63+
run: pnpm -C apps/desktop typecheck
64+
65+
- name: Run tests
66+
if: steps.check_release.outputs.skip != 'true'
67+
run: pnpm -C apps/desktop test
68+
69+
- name: Build and publish to GitHub Releases
70+
if: steps.check_release.outputs.skip != 'true'
71+
env:
72+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73+
run: pnpm -C apps/desktop release
74+
75+
- name: Upload DMG as workflow artifact
76+
if: steps.check_release.outputs.skip != 'true'
77+
uses: actions/upload-artifact@v4
78+
with:
79+
name: ClosedLoop-macOS-${{ steps.version.outputs.version }}
80+
path: apps/desktop/dist-dmg/*.dmg
81+
if-no-files-found: error
82+
83+
- name: Build Slack message
84+
if: steps.check_release.outputs.skip != 'true'
85+
id: slack_message
86+
run: |
87+
VERSION="${{ steps.version.outputs.version }}"
88+
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v${VERSION}"
89+
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
90+
MSG=$(printf "New ClosedLoop Desktop build ready\nVersion: %s\nRelease: %s\nArtifact: %s" \
91+
"$VERSION" "$RELEASE_URL" "$RUN_URL")
92+
echo "message<<EOF" >> $GITHUB_OUTPUT
93+
echo "$MSG" >> $GITHUB_OUTPUT
94+
echo "EOF" >> $GITHUB_OUTPUT
95+
96+
- name: Notify Slack
97+
if: steps.check_release.outputs.skip != 'true'
98+
continue-on-error: true
99+
uses: ./.github/actions/slack-notify
100+
with:
101+
webhook_url: ${{ secrets.SLACK_GITHUB_REPO_WEBHOOK_URL }}
102+
message: ${{ steps.slack_message.outputs.message }}

CLAUDE.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,38 @@ curl -s -H "X-Desktop-Session-Token: <TOKEN>" -H "Origin: http://localhost" \
7575
"http://localhost:19432/api/engineer/directories?path=/Users/<you>/.ssh"
7676
```
7777

78+
## Releasing Desktop Builds
79+
80+
Releases are automated via CI. When a PR that touches `apps/desktop/**` is merged to main, the release workflow runs:
81+
82+
1. Reads the `version` from `apps/desktop/package.json`
83+
2. Checks if that version already has a GitHub Release — if so, skips with a warning
84+
3. Builds a universal macOS DMG via `electron-builder`
85+
4. Publishes the DMG to GitHub Releases and uploads it as a workflow artifact
86+
5. Sends a Slack notification to the team
87+
88+
### Triggering a new release
89+
90+
Bump the `version` field in `apps/desktop/package.json` as part of your PR. When it merges, CI will build and publish automatically.
91+
92+
```jsonc
93+
// apps/desktop/package.json
94+
{ "version": "0.2.0" } // ← bump this
95+
```
96+
97+
If you merge desktop changes **without** bumping the version, the workflow will skip the build and log a warning — no harm done, no duplicate releases.
98+
99+
### Auto-update for users
100+
101+
- **Packaged builds** (DMG installs) use `electron-updater` to check GitHub Releases every 5 minutes. Users are notified in-app when a new version is available, and it auto-installs on quit.
102+
- **Dev builds** (running from source) compare `origin/main` commit hashes via `git fetch` and offer to pull + rebuild.
103+
104+
### Required GitHub secrets
105+
106+
- `SLACK_GITHUB_REPO_WEBHOOK_URL` — Slack incoming webhook for release notifications
107+
108+
The `GITHUB_TOKEN` (automatic in Actions) handles GitHub Releases publishing. If the repo has restricted default token permissions, ensure `contents: write` is allowed (Settings → Actions → General).
109+
78110
## Updating App Icons
79111

80112
The source of truth for the app icon is `apps/desktop/app-icon.svg`. All other icon assets are derived from it. To regenerate after updating the SVG:

apps/desktop/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"build": "pnpm prebuild && tsc -p tsconfig.json",
1212
"typecheck": "tsc -p tsconfig.json --noEmit",
1313
"test": "tsx --test test/*.test.ts",
14-
"package": "pnpm build && electron-builder --mac --config electron-builder.yml"
14+
"package": "pnpm build && electron-builder --mac --config electron-builder.yml",
15+
"release": "pnpm build && electron-builder --mac --publish always --config electron-builder.yml"
1516
},
1617
"dependencies": {
1718
"busboy": "^1.6.0",

0 commit comments

Comments
 (0)