Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
32 changes: 8 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:

jobs:
test:
name: Run Backend Test Suite
name: Run Go Backend Test Suite
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
Expand All @@ -24,28 +24,12 @@ jobs:
with:
persist-credentials: false

- name: Setup Node.js 20
uses: actions/setup-node@v6
- name: Setup Go
uses: actions/setup-go@v5
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'
go-version: '1.25'
cache-dependency-path: backend-go/go.sum

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Setup Java 21 (Temurin)
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6

- name: Run Backend Tests
working-directory: ./backend
run: |
chmod +x gradlew
./gradlew test --no-daemon
- name: Run Go Backend Tests
working-directory: ./backend-go
run: go test ./... -v
156 changes: 69 additions & 87 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Tag name to release (e.g. v0.1.0-alpha)'
description: 'Tag name to release (e.g. v0.1.6)'
required: true
type: string

Expand All @@ -20,10 +20,10 @@ concurrency:

jobs:
# ===========================================
# STAGE 0: Tests & Version Sanity Check
# STAGE 0: Go Tests & Version Sanity Check
# ===========================================
verify:
name: Run Tests & Verify Version
name: Run Go Tests & Verify Version
runs-on: ubuntu-latest

steps:
Expand All @@ -33,47 +33,30 @@ jobs:
ref: ${{ inputs.tag || github.ref_name }}
persist-credentials: false

- name: Setup Node.js 20
uses: actions/setup-node@v6
- name: Setup Go
uses: actions/setup-go@v5
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'
go-version: '1.25'
cache-dependency-path: backend-go/go.sum

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Setup Java 21 (Temurin)
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
- name: Run Go Backend Tests
working-directory: ./backend-go
run: go test ./... -v

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6

- name: Run Backend Tests
working-directory: ./backend
run: |
chmod +x gradlew
./gradlew test --no-daemon

- name: Verify Tag Matches Gradle Version
working-directory: ./backend
- name: Verify Tag Matches Package Version
env:
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
run: |
TAG_VERSION="${RELEASE_TAG#v}"
GRADLE_VERSION=$(./gradlew properties -q | grep "^version:" | awk '{print $2}')
if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match Gradle version ($GRADLE_VERSION)"
PKG_VERSION=$(jq -r '.version' frontend/package.json)
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)"
exit 1
fi
echo "Tag version matches Gradle version: $TAG_VERSION"
echo "Tag version matches package.json version: $TAG_VERSION"

# ===========================================
# STAGE 1: Linux Packaging
# STAGE 1: Linux Packaging (.deb, .rpm)
# ===========================================
build-linux:
name: Package Linux Installers (.deb, .rpm)
Expand All @@ -94,41 +77,41 @@ jobs:
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Setup Java 21 (Temurin)
uses: actions/setup-java@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
distribution: 'temurin'
java-version: '21'
go-version: '1.25'
cache-dependency-path: backend-go/go.sum

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Linux Package Utilities (rpm, dpkg-dev)
- name: Install Linux Build Dependencies
run: |
sudo apt-get update
sudo apt-get install -y rpm dpkg-dev
sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev rpm dpkg-dev
Comment thread
MathCunha16 marked this conversation as resolved.

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Build Linux Package Installers (.deb, .rpm)
working-directory: ./backend
- name: Build Linux Installers via Tauri
working-directory: ./frontend
run: |
chmod +x gradlew
./gradlew packageDeb packageRpm --no-daemon
node scripts/build-all.js
npx tauri build --bundles deb,rpm

- name: Upload Linux Installer Artifacts
uses: actions/upload-artifact@v4
with:
name: linux-installers
path: |
backend/build/jpackage/**/*.deb
backend/build/jpackage/**/*.rpm
frontend/src-tauri/target/release/bundle/deb/*.deb
frontend/src-tauri/target/release/bundle/rpm/*.rpm
if-no-files-found: error

# ===========================================
# STAGE 2: Windows Packaging
# STAGE 2: Windows Packaging (.msi)
# ===========================================
build-windows:
name: Package Windows Installer (.msi)
Expand All @@ -149,35 +132,34 @@ jobs:
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Setup Java 21 (Temurin)
uses: actions/setup-java@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
distribution: 'temurin'
java-version: '21'
go-version: '1.25'
cache-dependency-path: backend-go/go.sum

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Wix Toolset 3.x (Chocolatey)
run: choco install wixtoolset -y
- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Build Windows Package Installer (.msi)
working-directory: ./backend
run: .\gradlew.bat packageMsi --no-daemon
- name: Build Windows Installer via Tauri
working-directory: ./frontend
run: |
node scripts/build-all.js
npx tauri build --bundles msi

- name: Upload Windows Installer Artifact
uses: actions/upload-artifact@v4
with:
name: windows-installer
path: backend/build/jpackage/**/*.msi
path: frontend/src-tauri/target/release/bundle/msi/*.msi
if-no-files-found: error

# ===========================================
# STAGE 3: macOS Packaging
# STAGE 3: macOS Packaging (.dmg)
# ===========================================
build-macos:
name: Package macOS Installer (.dmg)
Expand All @@ -198,30 +180,30 @@ jobs:
cache: 'npm'
cache-dependency-path: 'frontend/package-lock.json'

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
cache-dependency-path: backend-go/go.sum

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Frontend Dependencies
working-directory: ./frontend
run: npm ci

- name: Setup Java 21 (Temurin)
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6

- name: Build macOS Package Installer (.dmg)
working-directory: ./backend
- name: Build macOS Installer via Tauri
working-directory: ./frontend
run: |
chmod +x gradlew
./gradlew packageDmg --no-daemon
node scripts/build-all.js
npx tauri build --bundles dmg

- name: Upload macOS Installer Artifact
uses: actions/upload-artifact@v4
with:
name: macos-installer
path: backend/build/jpackage/**/*.dmg
path: frontend/src-tauri/target/release/bundle/dmg/*.dmg
if-no-files-found: error

# ===========================================
Expand Down Expand Up @@ -254,10 +236,10 @@ jobs:
with:
tag_name: ${{ inputs.tag || github.ref_name }}
files: |
release-assets/**/*.deb
release-assets/**/*.rpm
release-assets/**/*.msi
release-assets/**/*.dmg
release-assets/*.deb
release-assets/*.rpm
release-assets/*.msi
release-assets/*.dmg
generate_release_notes: true
draft: false
prerelease: false
Expand Down
42 changes: 40 additions & 2 deletions backend-go/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,52 @@ import (
"devaulty-backend/internal/adapter/in/web"
"devaulty-backend/internal/adapter/in/web/handler"
"devaulty-backend/internal/usecase"
"devaulty-backend/migrations"
"fmt"
"log"
"net"
"os"
"path/filepath"
"runtime/debug"

"devaulty-backend/internal/adapter/out/persistence"
"devaulty-backend/internal/adapter/out/security"

"github.com/jmoiron/sqlx"
)

// resolveDataDir returns the absolute path to the application data directory.
// In production, Tauri sets DEVAULTY_DATA_DIR to the OS user config path.
// In development, it falls back to a relative "data" directory.
func resolveDataDir() string {
if dir := os.Getenv("DEVAULTY_DATA_DIR"); dir != "" {
return dir
}
return "data"
}

func main() {
if os.Getenv("APP_ENV") != "dev" {
debug.SetGCPercent(20)
}
log.Println("Starting Devaulty API")
db, err := persistence.InitDB("data/devaulty.db", "migrations")

dataDir := resolveDataDir()
if err := os.MkdirAll(dataDir, 0700); err != nil {
log.Fatalf("Critical error while creating data directory %s: %v", dataDir, err)
}

dbPath := filepath.Join(dataDir, "devaulty.db")

// Use embedded migrations in production for a self-contained binary.
// In development (APP_ENV=dev), use file-based migrations for hot-reload convenience.
var db *sqlx.DB
var err error
if os.Getenv("APP_ENV") == "dev" {
db, err = persistence.InitDB(dbPath, "migrations")
} else {
db, err = persistence.InitDBWithFS(dbPath, migrations.FS)
}
if err != nil {
log.Fatalf("Critical error while initializing Devaulty database: %v", err)
}
Expand Down Expand Up @@ -101,7 +134,12 @@ func main() {
}

port := listener.Addr().(*net.TCPAddr).Port
log.Printf("DEVAULTY_API_BASE_URL=%d", port) // Exposes for TAURI

// Structured session handshake line for Tauri IPC.
// Tauri reads stdout via a private pipe and parses this exact format
// to extract the dynamic port and security token.
fmt.Printf("[DEVAULTY_SESSION] PORT=%d TOKEN=%s\n", port, devaultyInternalToken)
os.Stdout.Sync()
Comment thread
MathCunha16 marked this conversation as resolved.
Outdated

log.Printf("Starting server on port %d...", port)
if err := r.RunListener(listener); err != nil {
Expand Down
6 changes: 5 additions & 1 deletion backend-go/internal/adapter/in/web/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ type Handlers struct {
}

func SetupRouter(h *Handlers, apiToken string) *gin.Engine {
r := gin.Default()
if os.Getenv("APP_ENV") != "dev" {
gin.SetMode(gin.ReleaseMode)
}
r := gin.New()
r.Use(gin.Recovery())
r.Use(middleware.CORSMiddleware())

registerDocsRoutes(r)
Expand Down
Loading