Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
23 changes: 23 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
changelog:
categories:
- title: "Features"
labels:
- "feature"
- "enhancement"
- title: "Bug Fixes"
labels:
- "fix"
- "bug"
- title: "Performance Improvements"
labels:
- "performance"
- "perf"
- title: "Documentation"
labels:
- "documentation"
- "docs"
- title: "Maintenance & Infrastructure"
labels:
- "chore"
- "ci"
- "dependencies"
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI Backend Test Suite

on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
- ready_for_review

jobs:
test:
name: Run Backend Test Suite
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v6
Comment thread
MathCunha16 marked this conversation as resolved.

- name: Setup Node.js 20
uses: actions/setup-node@v6
with:
node-version: '20'
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
with:
distribution: 'temurin'
java-version: '21'

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

- name: Run Backend Tests
working-directory: ./backend
run: |
chmod +x gradlew
./gradlew test --no-daemon
246 changes: 246 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
name: Release CI/CD Pipeline

on:
push:
tags:
- 'v*'
workflow_dispatch:
Comment thread
MathCunha16 marked this conversation as resolved.

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

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

steps:
- name: Checkout Repository
uses: actions/checkout@v6

- name: Setup Node.js 20
uses: actions/setup-node@v6
with:
node-version: '20'
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
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Gradle
uses: 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
if: github.event_name == 'push'
working-directory: ./backend
run: |
TAG_VERSION="${GITHUB_REF_NAME#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)"
exit 1
fi
echo "Tag version matches Gradle version: $TAG_VERSION"

# ===========================================
# STAGE 1: Linux Packaging
# ===========================================
build-linux:
name: Package Linux Installers (.deb, .rpm)
needs: verify
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v6

- name: Setup Node.js 20
uses: actions/setup-node@v6
with:
node-version: '20'
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
with:
distribution: 'temurin'
java-version: '21'

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

- name: Install Linux Package Utilities (rpm, dpkg-dev)
run: |
sudo apt-get update
sudo apt-get install -y rpm dpkg-dev

- name: Build Linux Package Installers (.deb, .rpm)
working-directory: ./backend
run: |
chmod +x gradlew
./gradlew packageDeb packageRpm --no-daemon

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

# ===========================================
# STAGE 2: Windows Packaging
# ===========================================
build-windows:
name: Package Windows Installer (.msi)
needs: verify
runs-on: windows-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v6

- name: Setup Node.js 20
uses: actions/setup-node@v6
with:
node-version: '20'
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
with:
distribution: 'temurin'
java-version: '21'

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

- name: Install Wix Toolset 3.x (Chocolatey)
run: choco install wix --version 3.11.2 -y

- name: Build Windows Package Installer (.msi)
working-directory: ./backend
run: .\gradlew.bat packageMsi --no-daemon

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

# ===========================================
# STAGE 3: macOS Packaging
# ===========================================
build-macos:
name: Package macOS Installer (.dmg)
needs: verify
runs-on: macos-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v6

- name: Setup Node.js 20
uses: actions/setup-node@v6
with:
node-version: '20'
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
with:
distribution: 'temurin'
java-version: '21'

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

- name: Build macOS Package Installer (.dmg)
working-directory: ./backend
run: |
chmod +x gradlew
./gradlew packageDmg --no-daemon

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

# ===========================================
# STAGE 4: Publish on GitHub Releases
# ===========================================
publish-release:
name: Publish GitHub Release Assets
needs:
- build-linux
- build-windows
- build-macos
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download All Build Artifacts
uses: actions/download-artifact@v4
with:
path: release-assets
merge-multiple: true

- name: Display Collected Release Assets
run: |
echo "=== Executables Collected for Release ==="
ls -la release-assets/

- name: Publish GitHub Release (softprops/action-gh-release)
uses: softprops/action-gh-release@v2
with:
files: |
release-assets/*.deb
release-assets/*.rpm
release-assets/*.msi
release-assets/*.dmg
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 changes: 17 additions & 4 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,23 @@ val mapStructVersion = "1.6.3"
val swaggerOpenAPIVersion = "3.0.3"
val bouncyCastleVersion = "1.84"

val yamlVersion = file("src/main/resources/application.yaml")
.readLines()
.firstOrNull { it.trim().startsWith("version:") }
?.substringAfter("version:")
?.trim()
?.removeSurrounding("\"")
?.removeSurrounding("'")
?: "0.1.0-alpha"

group = "com.devaulty"
version = "0.1.0-alpha"
version = yamlVersion

// Clean version for Linux/Windows (e.g. "0.1.0-alpha" -> "0.1.0")
val packageVersion = yamlVersion.replace(Regex("(?i)-.*$"), "")

val packageVersion = "0.1.0"
val macPackageVersion = "1.0.0"
// Numeric 3-part version for macOS (e.g. "0.1.0")
val macPackageVersion = if (packageVersion.split(".").size >= 3) packageVersion else "$packageVersion.0"

java {
toolchain {
Expand All @@ -36,6 +48,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-liquibase")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-webmvc")
implementation("org.springframework.boot:spring-boot-starter-webclient")
implementation("org.springframework.security:spring-security-crypto")
implementation("org.mapstruct:mapstruct:${mapStructVersion}")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${swaggerOpenAPIVersion}")
Expand Down Expand Up @@ -81,7 +94,7 @@ val buildFrontend by tasks.registering(Exec::class) {

workingDir = file("../frontend")

val isWindows = org.gradle.internal.os.OperatingSystem.current().isWindows()
val isWindows = org.gradle.internal.os.OperatingSystem.current().isWindows
if (isWindows) {
commandLine("cmd", "/c", "npm run build")
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public ResponseEntity<ApiErrorResponse> handleCryptoException(CryptoException ex
return buildResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());
}

@ExceptionHandler(UpdateNotAvailableException.class)
public ResponseEntity<ApiErrorResponse> handleUpdateNotAvailableException(UpdateNotAvailableException exception) {
return buildResponse(HttpStatus.BAD_REQUEST, exception.getMessage());
}

@ExceptionHandler(InvalidMasterPasswordException.class)
public ResponseEntity<ApiErrorResponse> handleInvalidMasterPasswordException(InvalidMasterPasswordException exception) {
return buildResponse(HttpStatus.UNAUTHORIZED, exception.getMessage());
Expand Down
Loading
Loading