Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
42f1bcc
feat(desktop): add Tauri shell
awsl233777 May 25, 2026
6e2d283
fix(desktop): include Tauri Cargo manifest
awsl233777 May 25, 2026
f15c332
fix(desktop): drop backend state lock before shutdown
awsl233777 May 25, 2026
04966dc
fix(desktop): hide backend console window on Windows
awsl233777 May 25, 2026
d69d704
fix(desktop): stage runtime resources before Tauri packaging
awsl233777 May 26, 2026
adccc52
chore(site): update logo
ymkiux May 26, 2026
e232564
chore(site): update logo and add logo-v variant
ymkiux May 26, 2026
dcb7205
chore(desktop): regenerate Tauri app icons
awsl233777 May 26, 2026
6242a2b
fix(desktop): address security review findings
awsl233777 May 27, 2026
bd6cd6b
fix(ci): use resolvable checkout action sha
awsl233777 May 27, 2026
af1e3b5
fix(desktop): restrict cli fallback to debug builds
awsl233777 May 27, 2026
b13f13a
ci(release): attach desktop installers to releases
awsl233777 May 27, 2026
948fdd3
feat(analytics): export usage data
awsl233777 May 27, 2026
c621391
fix(analytics): address usage export review feedback
awsl233777 May 27, 2026
e91b464
test(desktop): verify Tauri icon bundle contract
awsl233777 May 27, 2026
90cab7f
fix(desktop): expose startup diagnostics logs
awsl233777 May 28, 2026
89cfa3f
merge main into pr-168
awsl233777 May 28, 2026
b56e661
fix(desktop): write backend startup log
awsl233777 May 28, 2026
25cb231
fix(desktop): clear stale backend port
awsl233777 May 28, 2026
abcbbe0
fix(desktop): force clear occupied backend port
awsl233777 May 28, 2026
e60905b
fix(desktop): show occupied port guidance
awsl233777 May 28, 2026
77f6d43
fix(desktop): show native startup failures
awsl233777 May 28, 2026
21cc4d4
fix(desktop): elevate stale backend cleanup
awsl233777 May 29, 2026
4be79e9
fix(desktop): require admin via Windows manifest
awsl233777 May 29, 2026
4ebfd74
test(desktop): verify packaged UAC manifest
awsl233777 May 29, 2026
64b24bc
ci(desktop): upload verified Windows exe manifest
awsl233777 May 29, 2026
ba938f1
fix(desktop): bundle node runtime for packaged startup
awsl233777 May 29, 2026
be1a193
fix(release): resolve PR 172 main conflicts
awsl233777 Jun 22, 2026
099964c
chore(desktop): align tauri package version
awsl233777 Jun 22, 2026
4fea3e1
fix(desktop): reuse healthy startup backend
awsl233777 Jun 24, 2026
f051133
fix(desktop): wait for occupied backend readiness
awsl233777 Jun 24, 2026
f6432d6
fix(desktop): surface backend startup failures
awsl233777 Jun 24, 2026
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
97 changes: 97 additions & 0 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: desktop-build

on:
workflow_dispatch:
pull_request:
paths:
- 'src-tauri/**'
- 'tools/desktop/**'
- 'web-ui/**'
- 'cli.js'
- 'cli/**'
- 'lib/**'
- 'plugins/**'
- 'package.json'
- 'package-lock.json'
- '.github/workflows/desktop-build.yml'

permissions:
contents: read

jobs:
tauri:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: macOS
os: macos-latest
- name: Windows
os: windows-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '22'
cache: npm

- name: Setup Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8

- name: Install dependencies
run: npm ci

- name: Verify npm package payload
run: npm pack --dry-run --json

- name: Stage desktop runtime resources
run: npm run desktop:stage

- name: Build desktop app
run: npm run desktop:build

- name: Verify Windows app UAC manifest
if: matrix.name == 'Windows'
shell: pwsh
run: |
$exe = Join-Path $PWD 'src-tauri/target/release/codexmate-desktop.exe'
if (!(Test-Path $exe)) {
throw "Built app exe not found: $exe"
}

$mt = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter mt.exe |
Sort-Object FullName -Descending |
Select-Object -First 1
if (!$mt) {
throw 'Windows manifest tool mt.exe not found'
}

$manifest = Join-Path $env:RUNNER_TEMP 'codexmate-desktop.manifest.xml'
& $mt.FullName -nologo "-inputresource:$exe;#1" "-out:$manifest"
if ($LASTEXITCODE -ne 0) {
throw "mt.exe failed to extract manifest from $exe"
}

$text = Get-Content $manifest -Raw
Write-Host $text
if ($text -notmatch 'requestedExecutionLevel\s+level="requireAdministrator"\s+uiAccess="false"') {
throw 'Windows app manifest does not require administrator privileges'
}
Copy-Item $manifest (Join-Path (Split-Path $exe) 'codexmate-desktop.manifest.xml') -Force

- name: Upload desktop bundles
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: codexmate-desktop-${{ matrix.name }}
path: |
src-tauri/target/release/bundle/**
src-tauri/target/release/codexmate-desktop.exe
src-tauri/target/release/codexmate-desktop.manifest.xml
if-no-files-found: error
157 changes: 113 additions & 44 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: release
name: release
run-name: "${{ github.event.repository.name }} ${{ inputs.tag || 'auto' }}"
on:
workflow_dispatch:
Expand All @@ -11,17 +11,28 @@ permissions:
contents: write

jobs:
release:
resolve:
runs-on: ubuntu-latest
outputs:
release_tag: ${{ steps.resolve.outputs.release_tag }}
release_version: ${{ steps.resolve.outputs.release_version }}
release_mode: ${{ steps.resolve.outputs.release_mode }}
latest_tag: ${{ steps.resolve.outputs.latest_tag }}
package_version: ${{ steps.resolve.outputs.package_version }}
base_version: ${{ steps.resolve.outputs.base_version }}
base_source: ${{ steps.resolve.outputs.base_source }}
tag_exists: ${{ steps.resolve.outputs.tag_exists }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- name: Fetch tags
run: git fetch --tags --force
- uses: actions/setup-node@v4
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '18'
cache: 'npm'
Expand Down Expand Up @@ -113,18 +124,6 @@ jobs:
baseSource = tagExists ? 'package_tag' : 'package_version';
}

const envLines = [
`RELEASE_TAG=${resolvedTag}`,
`RELEASE_VERSION=${expectedVersion}`,
`RELEASE_MODE=${mode}`,
`LATEST_TAG=${latestTag}`,
`PACKAGE_VERSION=${pkgVersion}`,
`BASE_VERSION=${baseVersion}`,
`BASE_SOURCE=${baseSource}`,
`TAG_EXISTS=${tagExists ? 'true' : 'false'}`
].join('\n') + '\n';
fs.appendFileSync(process.env.GITHUB_ENV, envLines);

const outputLines = [
`release_tag=${resolvedTag}`,
`release_version=${expectedVersion}`,
Expand Down Expand Up @@ -152,28 +151,98 @@ jobs:
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summaryLines + '\n');
console.log(`::notice title=Resolved Tag::${resolvedTag}`);
NODE

desktop:
needs: resolve
name: desktop-${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: macOS
os: macos-latest
- name: Windows
os: windows-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- name: Fetch tags
run: git fetch --tags --force
- name: Checkout target tag
if: ${{ steps.resolve.outputs.tag_exists == 'true' }}
if: ${{ needs.resolve.outputs.tag_exists == 'true' }}
env:
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
git rev-parse "refs/tags/${RELEASE_TAG}" >/dev/null 2>&1
git checkout "${RELEASE_TAG}"
- name: Verify tag matches package.json version
if: ${{ steps.resolve.outputs.tag_exists == 'true' }}
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '22'
cache: npm
- name: Verify package.json matches release tag
env:
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
node -e "const pkg=require('./package.json'); const tag=process.env.RELEASE_TAG; const expected='v'+pkg.version; if(tag!==expected){ console.error('package.json '+expected+' does not match resolved release tag '+tag); process.exit(1);} console.log('Package matches '+expected);"
- name: Setup Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
- name: Install dependencies
run: npm ci
- name: Verify npm package payload
run: npm pack --dry-run --json
- name: Stage desktop runtime resources
run: npm run desktop:stage
- name: Build desktop app
run: npm run desktop:build
- name: Upload desktop release assets
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: codexmate-desktop-${{ matrix.name }}
path: |
src-tauri/target/release/bundle/dmg/*.dmg
src-tauri/target/release/bundle/msi/*.msi
src-tauri/target/release/bundle/nsis/*.exe
if-no-files-found: error

release:
needs:
- resolve
- desktop
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- name: Fetch tags
run: git fetch --tags --force
- name: Checkout target tag
if: ${{ needs.resolve.outputs.tag_exists == 'true' }}
env:
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
node -e "const pkg=require('./package.json'); const tag=process.env.RELEASE_TAG; const expected='v'+pkg.version; if(tag!==expected){ console.error('Tag '+tag+' does not match package.json version '+expected); process.exit(1);} console.log('Tag matches '+expected);"
git rev-parse "refs/tags/${RELEASE_TAG}" >/dev/null 2>&1
git checkout "${RELEASE_TAG}"
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: '18'
- name: Verify package.json matches release tag
if: ${{ steps.resolve.outputs.tag_exists != 'true' }}
env:
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
node -e "const pkg=require('./package.json'); const tag=process.env.RELEASE_TAG; const expected='v'+pkg.version; if(tag!==expected){ console.error('Current commit package.json '+expected+' does not match resolved release tag '+tag); process.exit(1);} console.log('Current package matches '+expected);"
node -e "const pkg=require('./package.json'); const tag=process.env.RELEASE_TAG; const expected='v'+pkg.version; if(tag!==expected){ console.error('package.json '+expected+' does not match resolved release tag '+tag); process.exit(1);} console.log('Package matches '+expected);"
- name: Compute release name
env:
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
node -e "const p=require('./package.json'); const tag=process.env.RELEASE_TAG; const name=p.name.includes('/')? p.name.split('/')[1]: p.name; const value=name+' '+tag; console.log('RELEASE_NAME='+value);" >> "$GITHUB_ENV"
- name: Pack npm artifact
Expand All @@ -191,11 +260,17 @@ jobs:
cli.js cli/ lib/ plugins/ web-ui.html web-ui/ \
node_modules/ package.json LICENSE README.md README.zh.md
echo "STANDALONE_TGZ=$name" >> "$GITHUB_ENV"
- name: Download desktop release assets
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
pattern: codexmate-desktop-*
path: desktop-release-assets
merge-multiple: true
- name: Fetch contributors from GitHub API
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
LATEST_TAG: ${{ steps.resolve.outputs.latest_tag }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
LATEST_TAG: ${{ needs.resolve.outputs.latest_tag }}
CONTRIBUTORS_FILE: release-contributors.txt
run: |
if [ -z "${LATEST_TAG}" ]; then
Expand All @@ -213,22 +288,13 @@ jobs:
trap 'rm -f "${tmp_logins}"' EXIT

# Fetch PR authors in range using base...head comparison
gh pr list \
--repo "${GITHUB_REPOSITORY}" \
--limit 500 \
--json author \
--jq '.[].author.login' 2>/dev/null | sort -u > "${tmp_logins}" || true
gh pr list --repo "${GITHUB_REPOSITORY}" --limit 500 --json author --jq '.[].author.login' 2>/dev/null | sort -u > "${tmp_logins}" || true

# Fetch merged PRs in range using commits
tmp_merged=$(mktemp)
git log "${LATEST_TAG}...${RELEASE_TAG}" --pretty=format:%s \
| grep -oE '#[0-9]+' \
| sed 's/^#//' \
| sort -u \
| while read -r pr_number; do
git log "${LATEST_TAG}...${RELEASE_TAG}" --pretty=format:%s | grep -oE '#[0-9]+' | sed 's/^#//' | sort -u | while read -r pr_number; do
gh pr view "${pr_number}" --repo "${GITHUB_REPOSITORY}" --json author --jq '.author.login' 2>/dev/null || true
done \
| sort -u > "${tmp_merged}" || true
done | sort -u > "${tmp_merged}" || true

if [ -s "${tmp_merged}" ]; then
cat "${tmp_merged}" > "${CONTRIBUTORS_FILE}"
Expand All @@ -240,8 +306,8 @@ jobs:
fi
- name: Generate release notes from actual commit range
env:
RELEASE_TAG: ${{ steps.resolve.outputs.release_tag }}
TAG_EXISTS: ${{ steps.resolve.outputs.tag_exists }}
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
TAG_EXISTS: ${{ needs.resolve.outputs.tag_exists }}
RELEASE_CHANGELOG_FILE: release-changelog.md
CONTRIBUTORS_FILE: release-contributors.txt
run: |
Expand All @@ -256,9 +322,9 @@ jobs:
node tools/release/changelog.js
test -s "${RELEASE_CHANGELOG_FILE}"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65
with:
tag_name: ${{ steps.resolve.outputs.release_tag }}
tag_name: ${{ needs.resolve.outputs.release_tag }}
target_commitish: ${{ github.sha }}
name: ${{ env.RELEASE_NAME }}
prerelease: false
Expand All @@ -267,4 +333,7 @@ jobs:
files: |
${{ env.PACKAGE_TGZ }}
${{ env.STANDALONE_TGZ }}
desktop-release-assets/**/*.dmg
desktop-release-assets/**/*.msi
desktop-release-assets/**/*.exe
generate_release_notes: false
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,3 @@ codex-switcher.exe
log.txt
tmp/
.gitnexus/

Loading
Loading