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
133 changes: 133 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Release
on:
push:
tags:
- 'v*'

jobs:
release-linux:
permissions:
contents: write
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: setup node
uses: actions/setup-node@v4
with:
node-version: 20

- name: setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: install rust stable
uses: dtolnay/rust-toolchain@stable

- name: install dependencies (ubuntu)
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
Comment on lines +28 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Tauri version in Cargo.toml
fd -t f 'Cargo.toml' -x grep -H 'tauri' {} \;

Repository: remcostoeten/dora

Length of output: 478


🌐 Web query:

Tauri v2 Linux dependencies libwebkit2gtk libappindicator official documentation

💡 Result:

In Tauri v2, the official docs list these Linux packages relevant to your question:

  • Development (build) dependencies (Debian/Ubuntu example):

    • libwebkit2gtk-4.1-dev (WebView / WebKit2GTK)
    • libayatana-appindicator3-dev (AppIndicator/system tray support; Tauri v2 uses Ayatana on many distros) (v2.tauri.app)
  • Runtime (shipping) dependencies for Debian-based .deb packages:

    • libwebkit2gtk-4.1-0
    • libgtk-3-0
    • libappindicator3-1 only if your app uses the system tray (v2.tauri.app)

If you specifically meant libappindicator vs libayatana-appindicator: Tauri v2’s prerequisites page uses Ayatana (libayatana-appindicator3-dev) for the Linux dev setup example. (v2.tauri.app)

Citations:


Update Linux dependencies for Tauri v2.

The workflow is using Tauri v2.8.0 but has outdated dependencies. For Tauri v2, replace:

  • libwebkit2gtk-4.0-devlibwebkit2gtk-4.1-dev
  • libappindicator3-devlibayatana-appindicator3-dev

This aligns with the official Tauri v2 prerequisites.

🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 28 - 31, Update the Ubuntu
install step "install dependencies (ubuntu)" to use the Tauri v2-required
packages: replace libwebkit2gtk-4.0-dev with libwebkit2gtk-4.1-dev and replace
libappindicator3-dev with libayatana-appindicator3-dev in the apt-get install
list so the workflow matches Tauri v2 prerequisites.


- name: install frontend dependencies
run: bun install
working-directory: ./apps/desktop

- name: build frontend
run: bun run build
working-directory: ./apps/desktop

- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: ./apps/desktop
tagName: ${{ github.ref_name }}
releaseName: 'App v__VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: true
prerelease: false
Comment on lines +41 to +50
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

latest stable version tauri-apps/tauri-action GitHub Action

💡 Result:

Latest stable release of tauri-apps/tauri-action is v0.6.1 (tag: action-v0.6.1, released 2026-01-03). [1]

You can pin it as:

- uses: tauri-apps/tauri-action@v0.6.1

Source: GitHub Releases page. [1]

[1] https://github.com/tauri-apps/tauri-action/releases


🏁 Script executed:

# Let's examine the release.yml workflow to understand the job structure
cat -n .github/workflows/release.yml

Repository: remcostoeten/dora

Length of output: 4572


Pin tauri-action to v0.6.1 and serialize release jobs to prevent race conditions.

  1. Using @v0 is very loose and could introduce breaking changes unexpectedly. Pin to the latest stable version: @v0.6.1.

  2. All three jobs run in parallel and each attempts to create/update the same GitHub release. This can cause race conditions. Consider making Windows and macOS jobs depend on the Linux job using needs: release-linux.

♻️ Proposed fix for job dependencies
   release-windows:
+    needs: release-linux
     permissions:
       contents: write
     runs-on: windows-latest
   release-macos:
+    needs: release-linux
     permissions:
       contents: write
     runs-on: macos-latest
🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 41 - 50, The workflow uses the
loose tauri-action@v0 and runs release jobs in parallel causing race conditions;
update the action reference in the step that currently says "uses:
tauri-apps/tauri-action@v0" to the pinned "tauri-apps/tauri-action@v0.6.1" and
serialize the release jobs by adding job dependencies so the Windows and macOS
release jobs (e.g., job ids like release-windows and release-macos) include
"needs: release-linux" (or whatever the Linux release job id is) so only the
Linux job creates/updates the GitHub release first.


release-windows:
permissions:
contents: write
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: setup node
uses: actions/setup-node@v4
with:
node-version: 20

- name: setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: install rust stable
uses: dtolnay/rust-toolchain@stable

- name: install frontend dependencies
run: bun install
working-directory: ./apps/desktop

- name: build frontend
run: bun run build
working-directory: ./apps/desktop

- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: ./apps/desktop
tagName: ${{ github.ref_name }}
releaseName: 'App v__VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: true
prerelease: false

release-macos:
permissions:
contents: write
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: setup node
uses: actions/setup-node@v4
with:
node-version: 20

- name: setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: install rust stable
uses: dtolnay/rust-toolchain@stable

- name: install frontend dependencies
run: bun install
working-directory: ./apps/desktop

- name: build frontend
run: bun run build
working-directory: ./apps/desktop

- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
with:
projectPath: ./apps/desktop
tagName: ${{ github.ref_name }}
releaseName: 'App v__VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: true
prerelease: false
Comment on lines +119 to +133
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

tauri-apps tauri-action v0 macOS notarization APPLE_TEAM_ID environment variables

💡 Result:

For tauri-apps/tauri-action@v0 on macOS, notarization credentials are passed via environment variables (typically from GitHub Secrets). If you notarize using Apple ID + app-specific password, you should set all of these:

  • APPLE_ID (your Apple ID email)
  • APPLE_PASSWORD (app-specific password)
  • APPLE_TEAM_ID (your Apple Developer Team ID) — required when using Apple ID auth for notarization in current tooling/docs
  • (for signing) APPLE_CERTIFICATE (base64 .p12), APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY (keychain identity name)

Example (as used with tauri-action@v0):

- uses: tauri-apps/tauri-action@v0
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
    APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
    APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
    APPLE_ID: ${{ secrets.APPLE_ID }}
    APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
    APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

Sources: Tauri v1 signing guide (shows tauri-action@v0 + APPLE_TEAM_ID) [1], Tauri environment variables reference (states APPLE_TEAM_ID is required when APPLE_ID is provided) [2], and tauri-bundler release notes calling out APPLE_TEAM_ID being required for Apple ID notarization auth [3].

References
[1] https://v1.tauri.app/v1/guides/distribution/sign-macos/
[2] https://v2.tauri.app/reference/environment-variables/
[3] https://v2.tauri.app/release/tauri-bundler/v1.4.1/


Add missing APPLE_TEAM_ID environment variable for macOS notarization.

The workflow is missing APPLE_TEAM_ID, which is required for Apple ID-based notarization with tauri-action@v0. Without it, notarization will fail. Add:

APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

to the environment variables alongside the existing APPLE_ID and APPLE_PASSWORD.

🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 119 - 133, Add the missing
APPLE_TEAM_ID environment variable to the tauri-action@v0 step so macOS
notarization can use Apple ID-based notarization; update the env block that
already contains APPLE_ID and APPLE_PASSWORD to include APPLE_TEAM_ID (reference
the tauri-action@v0 step and the env variables APPLE_ID and APPLE_PASSWORD) so
the action receives APPLE_TEAM_ID from secrets for notarization.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
isInsideJoinParens,
getTableMatch,
getColumnMatch
} from '../../../../src/features/drizzle-runner/utils/lsp-patterns'
} from '../../../../../apps/desktop/src/features/drizzle-runner/utils/lsp-patterns'


describe('Drizzle LSP Patterns', () => {
describe('getDbName', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { seedDatabase } from '../../../../../../../apps/desktop/src/features/docker-manager/api/container-service'


// Mock the docker-client module
vi.mock('../../../../../../../apps/desktop/src/features/docker-manager/api/docker-client', () => ({
Expand All @@ -10,15 +10,36 @@ vi.mock('../../../../../../../apps/desktop/src/features/docker-manager/api/docke
}))

describe('seedDatabase', () => {
beforeEach(() => {
// We need to keep a reference to the dynamically imported module
let containerService: any;
let dockerClient: any;

beforeEach(async () => {
vi.resetModules() // Important: clear cache so isTauri is re-evaluated
vi.clearAllMocks()

// Mock Tauri environment to ensure we test the real implementation
Object.defineProperty(window, '__TAURI_INTERNALS__', {
value: {},
writable: true,
configurable: true
})

// Re-import modules after setting up environment
dockerClient = await import('../../../../../../../apps/desktop/src/features/docker-manager/api/docker-client')
containerService = await import('../../../../../../../apps/desktop/src/features/docker-manager/api/container-service')
})

afterEach(() => {
// Cleanup environment
// @ts-ignore
delete window['__TAURI_INTERNALS__']
})

it('should successfully copy, exec, and cleanup', async () => {
const { copyToContainer, execCommand } =
await import('../../../../../../../apps/desktop/src/features/docker-manager/api/docker-client')
const { copyToContainer, execCommand } = dockerClient

const result = await seedDatabase('container-123', '/path/to/seed.sql', {
const result = await containerService.seedDatabase('container-123', '/path/to/seed.sql', {
user: 'testuser',
database: 'testdb'
})
Expand Down Expand Up @@ -48,15 +69,15 @@ describe('seedDatabase', () => {
})

it('should handle exec failure', async () => {
const { execCommand } =
await import('../../../../../../../apps/desktop/src/features/docker-manager/api/docker-client')
const { execCommand } = dockerClient

vi.mocked(execCommand).mockResolvedValueOnce({
stdout: '',
stderr: 'psql error',
exitCode: 1
})

const result = await seedDatabase('container-123', 'file.sql', { user: 'u', database: 'd' })
const result = await containerService.seedDatabase('container-123', 'file.sql', { user: 'u', database: 'd' })

expect(result.success).toBe(false)
expect(result.error).toContain('psql error')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,20 @@ import {
checkDockerAvailability,
startContainer,
stopContainer,
removeContainer
removeContainer,
deps
} from '../../../../../../../apps/desktop/src/features/docker-manager/api/docker-client'

// Mock the Tauri shell plugin
const { mockExecute, mockCreate } = vi.hoisted(() => {
const execute = vi.fn(() => Promise.resolve({ stdout: '', stderr: '', code: 0 }))
const create = vi.fn(() => ({
execute: execute,
on: vi.fn(),
spawn: vi.fn().mockResolvedValue({
kill: vi.fn()
}),
stdout: { on: vi.fn() },
stderr: { on: vi.fn() }
}))
return { mockExecute: execute, mockCreate: create }
})

vi.mock('@tauri-apps/plugin-shell', () => ({
Command: {
create: (cmd: string, args: string[]) => mockCreate(cmd, args)
}
}))

// Mock Tauri API core directly to prevent "invoke" errors if leakage occurs
// Mock Tauri API core
vi.mock('@tauri-apps/api/core', () => ({
invoke: vi.fn().mockResolvedValue('')
}))

// Mock window to simulate Tauri environment
Object.defineProperty(window, 'Tauri', {
value: {},
writable: true
})

// Mock Tauri internals required by some real modules if they slip through
Object.defineProperty(window, '__TAURI_INTERNALS__', {
value: {
invoke: vi.fn()
Expand All @@ -48,9 +26,27 @@ Object.defineProperty(window, '__TAURI_INTERNALS__', {
})

describe('docker-client', () => {
let mockExecute: any
let mockCreate: any

beforeEach(() => {
vi.clearAllMocks()
mockExecute.mockResolvedValue({ stdout: '', stderr: '', code: 0 })
mockExecute = vi.fn().mockResolvedValue({ stdout: '', stderr: '', code: 0 })
mockCreate = vi.fn()

// Inject mock
deps.getCommand = async () => ({
create: (cmd: string, args: string[]) => {
mockCreate(cmd, args)
return {
execute: mockExecute,
on: vi.fn(),
spawn: vi.fn().mockResolvedValue({ kill: vi.fn() }),
stdout: { on: vi.fn() },
stderr: { on: vi.fn() }
}
}
} as any)
})

describe('checkDockerAvailability', () => {
Expand All @@ -65,9 +61,9 @@ describe('docker-client', () => {

expect(result).toEqual({ available: true, version: '20.10.21' })
expect(mockCreate).toHaveBeenCalledWith('docker', [
'version',
'info',
'--format',
'{{.Server.Version}}'
'{{.ServerVersion}}'
])
})

Expand All @@ -86,14 +82,13 @@ describe('docker-client', () => {

describe('listContainers', () => {
it('should list and parse containers correctly including Env', async () => {
// Mock 'ps' command
mockExecute.mockResolvedValueOnce({
stdout: '{"ID":"123","Names":"test-container","Image":"postgres:14","State":"running","Status":"Up 2 hours","Ports":"0.0.0.0:5432->5432/tcp","Labels":"","CreatedAt":"2023-01-01"}\n',
stdout:
'{"ID":"123","Names":"test-container","Image":"postgres:14","State":"running","Status":"Up 2 hours","Ports":"0.0.0.0:5432->5432/tcp","Labels":"","CreatedAt":"2023-01-01"}\n',
stderr: '',
code: 0
})

// Mock 'inspect' command
mockExecute.mockResolvedValueOnce({
stdout: JSON.stringify([
{
Expand Down
Loading
Loading