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
35 changes: 35 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
changelog:
exclude:
labels:
- skip-changelog
authors:
- dependabot[bot]

categories:
- title: "🚀 Features"
labels:
- enhancement
- feature

- title: "🐛 Fixes"
labels:
- bug
- fix

- title: "🔒 Security"
labels:
- security

- title: "📚 Documentation"
labels:
- documentation

- title: "🧰 Maintenance"
labels:
- dependencies
- chore
- ci

- title: "Other Changes"
labels:
- "*"
76 changes: 71 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 20
env:
HOSTNAME: ai-jobpilot-ci
APP_AUTO_OPEN_BROWSER: "false"
APP_BROWSER_HEADLESS: "true"
APP_CACHE_DIR: /tmp/ai-jobpilot-ci/cache
Expand All @@ -25,10 +26,10 @@ jobs:
SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-ci/getjobs-ci.db
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "21"
Expand All @@ -40,7 +41,35 @@ jobs:
chmod +x ./gradlew

- name: Run backend tests
run: ./gradlew test --no-daemon
run: ./gradlew test --no-daemon --stacktrace

- name: Print failed JUnit reports
if: failure()
shell: bash
run: |
found_failure=false
for report in build/test-results/test/*.xml; do
if grep -Eq 'failures="[1-9]|errors="[1-9]' "$report"; then
found_failure=true
echo "::group::$report"
cat "$report"
echo "::endgroup::"
fi
done
if [[ "$found_failure" == "false" ]]; then
echo "No failed JUnit XML report was found."
fi

- name: Upload backend test reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: backend-test-reports
path: |
build/reports/tests/test
build/test-results/test
if-no-files-found: ignore
retention-days: 7

- name: Build backend
run: ./gradlew build --no-daemon
Expand All @@ -51,15 +80,15 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.20.0

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "20"
cache: pnpm
Expand All @@ -76,3 +105,40 @@ jobs:
- name: Build frontend
working-directory: front
run: pnpm build

chrome-extension:
name: Chrome Extension
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "20"

- name: Validate manifest and referenced files
run: node scripts/validate-chrome-extension.mjs

docker-config:
name: Docker Configuration
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Validate Docker Compose
run: docker compose config --quiet

- name: Verify required Dockerfile stages
shell: bash
run: |
for stage in backend-dev frontend-dev frontend-build backend-build runtime; do
grep -Eq "^[[:space:]]*FROM[[:space:]].*[[:space:]]AS[[:space:]]${stage}([[:space:]]|$)" Dockerfile || {
echo "Missing Dockerfile stage: ${stage}"
exit 1
}
done
71 changes: 71 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: CodeQL

on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: "23 3 * * 1"
workflow_dispatch:

permissions:
contents: read
security-events: write
packages: read
actions: read

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- language: java-kotlin
build-mode: manual
- language: javascript-typescript
build-mode: none

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

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: security-extended

- name: Setup Java
if: matrix.language == 'java-kotlin'
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "21"
cache: gradle

- name: Build Java for CodeQL
if: matrix.language == 'java-kotlin'
env:
APP_AUTO_OPEN_BROWSER: "false"
APP_BROWSER_HEADLESS: "true"
APP_CACHE_DIR: /tmp/ai-jobpilot-codeql/cache
APP_DATA_DIR: /tmp/ai-jobpilot-codeql/data
APP_OUTPUT_DIR: /tmp/ai-jobpilot-codeql/output
LOGGING_FILE_NAME: /tmp/ai-jobpilot-codeql/logs/get-jobs.log
SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-codeql/getjobs-codeql.db
run: |
mkdir -p "$APP_CACHE_DIR" "$APP_DATA_DIR" "$APP_OUTPUT_DIR" "$(dirname "$LOGGING_FILE_NAME")"
chmod +x ./gradlew
./gradlew clean classes --no-daemon

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"
144 changes: 144 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Release

on:
push:
tags:
- "v*.*.*"
pull_request:
paths:
- ".github/workflows/release.yml"
- "scripts/validate-chrome-extension.mjs"
workflow_dispatch:
inputs:
version:
description: "Version label used for preview artifacts, for example v1.3.1-rc.1"
required: true
type: string

permissions:
contents: write

jobs:
build-and-package:
name: Build and package release artifacts
runs-on: ubuntu-latest
timeout-minutes: 40
env:
HOSTNAME: ai-jobpilot-release
APP_AUTO_OPEN_BROWSER: "false"
APP_BROWSER_HEADLESS: "true"
APP_CACHE_DIR: /tmp/ai-jobpilot-release/cache
APP_DATA_DIR: /tmp/ai-jobpilot-release/data
APP_OUTPUT_DIR: /tmp/ai-jobpilot-release/output
LOGGING_FILE_NAME: /tmp/ai-jobpilot-release/logs/get-jobs.log
SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-release/getjobs-release.db

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

- name: Resolve version
shell: bash
run: |
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
VERSION="${{ inputs.version }}"
elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
VERSION="v0.0.0-pr${{ github.event.pull_request.number }}"
else
VERSION="$GITHUB_REF_NAME"
fi

if [[ ! "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "Invalid version: $VERSION"
exit 1
fi

echo "RELEASE_VERSION=$VERSION" >> "$GITHUB_ENV"

- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "21"
cache: gradle

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.20.0

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "20"
cache: pnpm
cache-dependency-path: front/pnpm-lock.yaml

- name: Install frontend dependencies
working-directory: front
run: pnpm install --frozen-lockfile

- name: Validate frontend
working-directory: front
run: |
pnpm lint
pnpm build

- name: Validate Chrome extension
run: node scripts/validate-chrome-extension.mjs

- name: Build backend JAR with frontend assets
shell: bash
run: |
mkdir -p "$APP_CACHE_DIR" "$APP_DATA_DIR" "$APP_OUTPUT_DIR" "$(dirname "$LOGGING_FILE_NAME")"
rm -rf src/main/resources/dist
mkdir -p src/main/resources/dist
cp -a front/out/. src/main/resources/dist/
chmod +x ./gradlew
./gradlew clean test bootJar --no-daemon

- name: Assemble release artifacts
shell: bash
run: |
mkdir -p dist

JAR_PATH="$(find build/libs -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1)"
if [[ -z "$JAR_PATH" ]]; then
echo "No bootable JAR found in build/libs"
exit 1
fi

cp "$JAR_PATH" "dist/AI-JobPilot-${RELEASE_VERSION}.jar"
(cd chrome-extension && zip -qr "../dist/AI-JobPilot-${RELEASE_VERSION}-chrome-extension.zip" .)
(cd front/out && zip -qr "../../dist/AI-JobPilot-${RELEASE_VERSION}-frontend-static.zip" .)
git archive --format=zip --output="dist/AI-JobPilot-${RELEASE_VERSION}-source.zip" HEAD

(
cd dist
sha256sum AI-JobPilot-* > SHA256SUMS.txt
)

- name: Upload preview artifacts
uses: actions/upload-artifact@v4
with:
name: AI-JobPilot-${{ env.RELEASE_VERSION }}
path: dist/*
if-no-files-found: error
retention-days: 14

- name: Publish GitHub Release for tag
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
EXTRA_ARGS=()
if [[ "$RELEASE_VERSION" == *-* ]]; then
EXTRA_ARGS+=(--prerelease)
fi

gh release create "$GITHUB_REF_NAME" dist/* \
--verify-tag \
--title "AI JobPilot ${GITHUB_REF_NAME}" \
--generate-notes \
"${EXTRA_ARGS[@]}"
Loading
Loading