mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
gha cleanups (#6275)
# Description of Changes Adds all pre commit PR checks under single file with job validator to have a true "required" run step "all-checks-passed" check Moves all GHAs into workflow helper function calls Note if: always() overrides the default skip-on-needs-failure, "Needs" is added to ensure the validation runs at end of all other tasks --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
This commit is contained in:
@@ -55,6 +55,25 @@ frontend: &frontend
|
|||||||
- scripts/type3_to_cff.py
|
- scripts/type3_to_cff.py
|
||||||
- scripts/update_type3_library.py
|
- scripts/update_type3_library.py
|
||||||
|
|
||||||
|
# Files that affect the Tauri desktop bundle. Gate the multi-OS Tauri build
|
||||||
|
# job on changes to any of these.
|
||||||
|
tauri: &tauri
|
||||||
|
- frontend/src-tauri/**
|
||||||
|
- frontend/src/desktop/**
|
||||||
|
- frontend/tsconfig.desktop.json
|
||||||
|
- frontend/package.json
|
||||||
|
- frontend/package-lock.json
|
||||||
|
- frontend/vite.config.ts
|
||||||
|
- .github/workflows/tauri-build.yml
|
||||||
|
|
||||||
|
# Files that affect the AI engine (Python tool models, fixers, tests). Gate
|
||||||
|
# the engine validation job on changes to engine sources or to the Java
|
||||||
|
# tool surfaces it generates models from.
|
||||||
|
engine: &engine
|
||||||
|
- engine/**
|
||||||
|
- app/(common|core|proprietary)/src/main/java/**
|
||||||
|
- .github/workflows/ai-engine.yml
|
||||||
|
|
||||||
licenses-frontend: &licenses-frontend
|
licenses-frontend: &licenses-frontend
|
||||||
- ".github/workflows/frontend-backend-licenses-update.yml"
|
- ".github/workflows/frontend-backend-licenses-update.yml"
|
||||||
- "frontend/package.json"
|
- "frontend/package.json"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
name: AI Engine CI
|
name: AI Engine CI
|
||||||
|
|
||||||
|
# Validates the Python AI engine: regenerates tool models, runs fixers,
|
||||||
|
# lint, type-check, and tests. Called from build.yml on PRs and merge_group;
|
||||||
|
# also runs directly on push to main as a post-merge safety net.
|
||||||
on:
|
on:
|
||||||
|
workflow_call:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
pull_request:
|
|
||||||
merge_group:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
name: Backend build, format check, and coverage
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml. Runs the full backend build matrix
|
||||||
|
# (JDK 21/25 × spring-security on/off), Spotless formatting check, JUnit, and
|
||||||
|
# posts Jacoco coverage to PRs.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
security-events: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
jdk-version: [21, 25]
|
||||||
|
spring-security: [true, false]
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Set up JDK ${{ matrix.jdk-version }}
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.jdk-version }}
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: Cache Gradle dependency artifacts
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.gradle/caches/modules-2/files-2.1
|
||||||
|
~/.gradle/caches/modules-2/metadata-2.*
|
||||||
|
key: gradle-deps-${{ runner.os }}-jdk-${{ matrix.jdk-version }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||||
|
with:
|
||||||
|
gradle-version: 9.3.1
|
||||||
|
cache-disabled: true
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Check Java formatting (Spotless)
|
||||||
|
if: matrix.jdk-version == 25 && matrix.spring-security == false
|
||||||
|
id: spotless-check
|
||||||
|
run: task backend:format:check
|
||||||
|
continue-on-error: true
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
|
||||||
|
- name: Comment on Java formatting failure
|
||||||
|
# Only post a comment on PRs. github-script's PR helpers need an
|
||||||
|
# issue/PR number, which doesn't exist on merge_group runs.
|
||||||
|
if: steps.spotless-check.outcome == 'failure' && github.event_name == 'pull_request'
|
||||||
|
continue-on-error: true
|
||||||
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const marker = '<!-- java-formatting-check -->';
|
||||||
|
const body = [
|
||||||
|
marker,
|
||||||
|
'### Java Formatting Check Failed',
|
||||||
|
'',
|
||||||
|
'Your code has formatting issues. Run the following command to fix them:',
|
||||||
|
'',
|
||||||
|
'```bash',
|
||||||
|
'task backend:format',
|
||||||
|
'```',
|
||||||
|
'',
|
||||||
|
'Then commit and push the changes.',
|
||||||
|
].join('\n');
|
||||||
|
const { data: comments } = await github.rest.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
});
|
||||||
|
const existing = comments.find(c => c.body.includes(marker));
|
||||||
|
if (existing) {
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: existing.id,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Fail if Java formatting issues found
|
||||||
|
if: steps.spotless-check.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
echo "============================================"
|
||||||
|
echo " Java Formatting Check Failed"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Your code has formatting issues."
|
||||||
|
echo "Run the following command to fix them:"
|
||||||
|
echo ""
|
||||||
|
echo " task backend:format"
|
||||||
|
echo ""
|
||||||
|
echo "Then commit and push the changes."
|
||||||
|
echo "============================================"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Build with Gradle and spring security ${{ matrix.spring-security }}
|
||||||
|
run: task backend:build:ci
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.spring-security }}
|
||||||
|
|
||||||
|
- name: Check Test Reports Exist
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
declare -a dirs=(
|
||||||
|
"app/core/build/reports/tests/"
|
||||||
|
"app/core/build/test-results/"
|
||||||
|
"app/common/build/reports/tests/"
|
||||||
|
"app/common/build/test-results/"
|
||||||
|
"app/proprietary/build/reports/tests/"
|
||||||
|
"app/proprietary/build/test-results/"
|
||||||
|
)
|
||||||
|
for dir in "${dirs[@]}"; do
|
||||||
|
if [ ! -d "$dir" ]; then
|
||||||
|
echo "Missing $dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload Test Reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: test-reports-jdk-${{ matrix.jdk-version }}-spring-security-${{ matrix.spring-security }}
|
||||||
|
path: |
|
||||||
|
app/**/build/reports/jacoco/test
|
||||||
|
app/**/build/reports/tests/
|
||||||
|
app/**/build/test-results/
|
||||||
|
app/**/build/reports/problems/
|
||||||
|
build/reports/problems/
|
||||||
|
retention-days: 3
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
- name: Add coverage to PR with spring security ${{ matrix.spring-security }} and JDK ${{ matrix.jdk-version }}
|
||||||
|
# The action only supports the pull_request event (it posts a PR comment),
|
||||||
|
# so skip it for merge_group runs and workflow_dispatch.
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
id: jacoco
|
||||||
|
uses: madrapps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 # v1.7.2
|
||||||
|
with:
|
||||||
|
paths: |
|
||||||
|
${{ github.workspace }}/**/build/reports/jacoco/test/jacocoTestReport.xml
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
min-coverage-overall: 10
|
||||||
|
min-coverage-changed-files: 0
|
||||||
|
comment-type: summary
|
||||||
@@ -6,7 +6,8 @@ name: Enterprise E2E (Playwright)
|
|||||||
# situations:
|
# situations:
|
||||||
#
|
#
|
||||||
# - PRs that touch proprietary / premium / SSO compose / enterprise tests
|
# - PRs that touch proprietary / premium / SSO compose / enterprise tests
|
||||||
# (path-filtered against .github/config/.files.yaml `proprietary`),
|
# (driven by build.yml via workflow_call, path-filtered against
|
||||||
|
# .github/config/.files.yaml `proprietary`),
|
||||||
# - every push to main (post-merge safety net),
|
# - every push to main (post-merge safety net),
|
||||||
# - on a nightly cron schedule (catches Keycloak image drift, license
|
# - on a nightly cron schedule (catches Keycloak image drift, license
|
||||||
# expiry, upstream proprietary changes),
|
# expiry, upstream proprietary changes),
|
||||||
@@ -15,49 +16,26 @@ name: Enterprise E2E (Playwright)
|
|||||||
# Auto-skipped when secrets.PREMIUM_KEY_ENTERPRISE is missing (forks, dependabot).
|
# Auto-skipped when secrets.PREMIUM_KEY_ENTERPRISE is missing (forks, dependabot).
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_call:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
pull_request:
|
|
||||||
branches: ["main"]
|
|
||||||
merge_group:
|
|
||||||
branches: ["main"]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 4 * * *"
|
- cron: "0 4 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
# No `concurrency:` block here on purpose. When this workflow is called via
|
||||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref_name || github.ref }}
|
# workflow_call from build.yml, ${{ github.workflow }}/event_name/pr_number
|
||||||
cancel-in-progress: true
|
# resolve to the *caller's* values, producing the same group key as build.yml
|
||||||
|
# and causing the workflow_call instantiation to self-cancel — the job
|
||||||
|
# silently fails to spawn while `${{ needs.X.result }}` still reports
|
||||||
|
# `failure`. Standalone runs (push-to-main, nightly cron, dispatch) don't
|
||||||
|
# overlap often enough to need explicit concurrency control.
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
files-changed:
|
|
||||||
name: detect what files changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 3
|
|
||||||
outputs:
|
|
||||||
proprietary: ${{ steps.changes.outputs.proprietary }}
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- name: Check for file changes
|
|
||||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
|
||||||
id: changes
|
|
||||||
with:
|
|
||||||
filters: .github/config/.files.yaml
|
|
||||||
|
|
||||||
playwright-e2e-enterprise:
|
playwright-e2e-enterprise:
|
||||||
# Run on PRs only if relevant files changed; always run on push-to-main,
|
|
||||||
# cron, and manual dispatch. Fork PRs without the secret will fail at
|
|
||||||
# the compose step (PREMIUM_KEY empty) — that's intentional, not silent.
|
|
||||||
if: github.event_name != 'pull_request' || needs.files-changed.outputs.proprietary == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
env:
|
env:
|
||||||
|
|||||||
+142
-671
@@ -1,5 +1,13 @@
|
|||||||
name: Build and Test Workflow
|
name: Build and Test Workflow
|
||||||
|
|
||||||
|
# Top-level PR / merge-queue gate. Detects which paths changed and dispatches
|
||||||
|
# to the dedicated reusable workflows under .github/workflows/. Each child
|
||||||
|
# workflow keeps its own setup/teardown so this file stays a routing layer.
|
||||||
|
#
|
||||||
|
# The final `all-checks-passed` job is the single status check that branch
|
||||||
|
# protection should require — it succeeds only if every required upstream
|
||||||
|
# job either succeeded or was legitimately skipped by its path filter.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
@@ -33,6 +41,9 @@ jobs:
|
|||||||
openapi: ${{ steps.changes.outputs.openapi }}
|
openapi: ${{ steps.changes.outputs.openapi }}
|
||||||
frontend: ${{ steps.changes.outputs.frontend }}
|
frontend: ${{ steps.changes.outputs.frontend }}
|
||||||
docker-base: ${{ steps.changes.outputs.docker-base }}
|
docker-base: ${{ steps.changes.outputs.docker-base }}
|
||||||
|
tauri: ${{ steps.changes.outputs.tauri }}
|
||||||
|
engine: ${{ steps.changes.outputs.engine }}
|
||||||
|
proprietary: ${{ steps.changes.outputs.proprietary }}
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
@@ -48,714 +59,174 @@ jobs:
|
|||||||
filters: .github/config/.files.yaml
|
filters: .github/config/.files.yaml
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
needs: [files-changed]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
strategy:
|
uses: ./.github/workflows/backend-build.yml
|
||||||
fail-fast: false
|
secrets: inherit
|
||||||
matrix:
|
|
||||||
jdk-version: [21, 25]
|
|
||||||
spring-security: [true, false]
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
- name: Set up JDK ${{ matrix.jdk-version }}
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
java-version: ${{ matrix.jdk-version }}
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- name: Cache Gradle dependency artifacts
|
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/wrapper
|
|
||||||
~/.gradle/caches/modules-2/files-2.1
|
|
||||||
~/.gradle/caches/modules-2/metadata-2.*
|
|
||||||
key: gradle-deps-${{ runner.os }}-jdk-${{ matrix.jdk-version }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
with:
|
|
||||||
gradle-version: 9.3.1
|
|
||||||
cache-disabled: true
|
|
||||||
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Check Java formatting (Spotless)
|
|
||||||
if: matrix.jdk-version == 25 && matrix.spring-security == false
|
|
||||||
id: spotless-check
|
|
||||||
run: task backend:format:check
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
||||||
|
|
||||||
- name: Comment on Java formatting failure
|
|
||||||
# Only post a comment on PRs. github-script's PR helpers need an
|
|
||||||
# issue/PR number, which doesn't exist on merge_group runs.
|
|
||||||
if: steps.spotless-check.outcome == 'failure' && github.event_name == 'pull_request'
|
|
||||||
continue-on-error: true
|
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const marker = '<!-- java-formatting-check -->';
|
|
||||||
const body = [
|
|
||||||
marker,
|
|
||||||
'### Java Formatting Check Failed',
|
|
||||||
'',
|
|
||||||
'Your code has formatting issues. Run the following command to fix them:',
|
|
||||||
'',
|
|
||||||
'```bash',
|
|
||||||
'task backend:format',
|
|
||||||
'```',
|
|
||||||
'',
|
|
||||||
'Then commit and push the changes.',
|
|
||||||
].join('\n');
|
|
||||||
const { data: comments } = await github.rest.issues.listComments({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
});
|
|
||||||
const existing = comments.find(c => c.body.includes(marker));
|
|
||||||
if (existing) {
|
|
||||||
await github.rest.issues.updateComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
comment_id: existing.id,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Fail if Java formatting issues found
|
|
||||||
if: steps.spotless-check.outcome == 'failure'
|
|
||||||
run: |
|
|
||||||
echo "============================================"
|
|
||||||
echo " Java Formatting Check Failed"
|
|
||||||
echo "============================================"
|
|
||||||
echo ""
|
|
||||||
echo "Your code has formatting issues."
|
|
||||||
echo "Run the following command to fix them:"
|
|
||||||
echo ""
|
|
||||||
echo " task backend:format"
|
|
||||||
echo ""
|
|
||||||
echo "Then commit and push the changes."
|
|
||||||
echo "============================================"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Build with Gradle and spring security ${{ matrix.spring-security }}
|
|
||||||
run: task backend:build:ci
|
|
||||||
env:
|
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
||||||
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.spring-security }}
|
|
||||||
|
|
||||||
- name: Check Test Reports Exist
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
declare -a dirs=(
|
|
||||||
"app/core/build/reports/tests/"
|
|
||||||
"app/core/build/test-results/"
|
|
||||||
"app/common/build/reports/tests/"
|
|
||||||
"app/common/build/test-results/"
|
|
||||||
"app/proprietary/build/reports/tests/"
|
|
||||||
"app/proprietary/build/test-results/"
|
|
||||||
)
|
|
||||||
for dir in "${dirs[@]}"; do
|
|
||||||
if [ ! -d "$dir" ]; then
|
|
||||||
echo "Missing $dir"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Upload Test Reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: test-reports-jdk-${{ matrix.jdk-version }}-spring-security-${{ matrix.spring-security }}
|
|
||||||
path: |
|
|
||||||
app/**/build/reports/jacoco/test
|
|
||||||
app/**/build/reports/tests/
|
|
||||||
app/**/build/test-results/
|
|
||||||
app/**/build/reports/problems/
|
|
||||||
build/reports/problems/
|
|
||||||
retention-days: 3
|
|
||||||
if-no-files-found: warn
|
|
||||||
|
|
||||||
- name: Add coverage to PR with spring security ${{ matrix.spring-security }} and JDK ${{ matrix.jdk-version }}
|
|
||||||
# The action only supports the pull_request event (it posts a PR comment),
|
|
||||||
# so skip it for merge_group runs and workflow_dispatch.
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
id: jacoco
|
|
||||||
uses: madrapps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 # v1.7.2
|
|
||||||
with:
|
|
||||||
paths: |
|
|
||||||
${{ github.workspace }}/**/build/reports/jacoco/test/jacocoTestReport.xml
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
min-coverage-overall: 10
|
|
||||||
min-coverage-changed-files: 0
|
|
||||||
comment-type: summary
|
|
||||||
|
|
||||||
check-generateOpenApiDocs:
|
check-generateOpenApiDocs:
|
||||||
if: needs.files-changed.outputs.openapi == 'true'
|
if: needs.files-changed.outputs.openapi == 'true'
|
||||||
needs: [files-changed]
|
needs: [files-changed]
|
||||||
runs-on: ubuntu-latest
|
permissions:
|
||||||
steps:
|
contents: read
|
||||||
- name: Harden Runner
|
uses: ./.github/workflows/check-openapi.yml
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
secrets: inherit
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
- name: Set up JDK 25
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
java-version: "25"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- name: Cache Gradle dependency artifacts
|
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/wrapper
|
|
||||||
~/.gradle/caches/modules-2/files-2.1
|
|
||||||
~/.gradle/caches/modules-2/metadata-2.*
|
|
||||||
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
with:
|
|
||||||
gradle-version: 9.3.1
|
|
||||||
cache-disabled: true
|
|
||||||
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Generate OpenAPI documentation
|
|
||||||
run: task backend:swagger
|
|
||||||
env:
|
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
||||||
DISABLE_ADDITIONAL_FEATURES: true
|
|
||||||
|
|
||||||
- name: Upload OpenAPI Documentation
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: openapi-docs
|
|
||||||
path: ./SwaggerDoc.json
|
|
||||||
|
|
||||||
frontend-validation:
|
frontend-validation:
|
||||||
if: needs.files-changed.outputs.frontend == 'true'
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
needs: files-changed
|
needs: [files-changed]
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
uses: ./.github/workflows/frontend-validation.yml
|
||||||
- name: Harden Runner
|
secrets: inherit
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
cache: "npm"
|
|
||||||
cache-dependency-path: frontend/package-lock.json
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Quality-check frontend
|
|
||||||
id: frontend-check
|
|
||||||
run: task frontend:check:all
|
|
||||||
continue-on-error: true
|
|
||||||
- name: Comment on frontend check failure
|
|
||||||
# Only post a comment on PRs. github-script's PR helpers need an
|
|
||||||
# issue/PR number, which doesn't exist on merge_group runs.
|
|
||||||
if: steps.frontend-check.outcome == 'failure' && github.event_name == 'pull_request'
|
|
||||||
continue-on-error: true
|
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const marker = '<!-- frontend-check -->';
|
|
||||||
const body = [
|
|
||||||
marker,
|
|
||||||
'### Frontend Check Failed',
|
|
||||||
'',
|
|
||||||
'There are issues with your frontend code that will need to be fixed before they can be merged in.',
|
|
||||||
'',
|
|
||||||
'Run `task frontend:fix` to auto-fix what can be fixed automatically, then run `task frontend:check:all` to see what still needs fixing manually.',
|
|
||||||
].join('\n');
|
|
||||||
const { data: comments } = await github.rest.issues.listComments({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
});
|
|
||||||
const existing = comments.find(c => c.body.includes(marker));
|
|
||||||
if (existing) {
|
|
||||||
await github.rest.issues.updateComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
comment_id: existing.id,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
- name: Fail if frontend check failed
|
|
||||||
if: steps.frontend-check.outcome == 'failure'
|
|
||||||
run: |
|
|
||||||
echo "============================================"
|
|
||||||
echo " Frontend Check Failed"
|
|
||||||
echo "============================================"
|
|
||||||
echo ""
|
|
||||||
echo "There are issues with your frontend code that"
|
|
||||||
echo "will need to be fixed before they can be merged in."
|
|
||||||
echo ""
|
|
||||||
echo "Run 'task frontend:fix' to auto-fix what can be"
|
|
||||||
echo "fixed automatically, then run 'task frontend:check:all'"
|
|
||||||
echo "to see what still needs fixing manually."
|
|
||||||
echo "============================================"
|
|
||||||
exit 1
|
|
||||||
- name: Upload frontend build artifacts
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: frontend-build
|
|
||||||
path: frontend/dist/
|
|
||||||
retention-days: 3
|
|
||||||
|
|
||||||
playwright-e2e:
|
playwright-e2e:
|
||||||
# Backend-free stubbed suite — fast, no Spring Boot required.
|
|
||||||
if: needs.files-changed.outputs.frontend == 'true'
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
needs: files-changed
|
needs: [files-changed]
|
||||||
runs-on: ubuntu-latest
|
permissions:
|
||||||
steps:
|
contents: read
|
||||||
- name: Harden Runner
|
uses: ./.github/workflows/e2e-stubbed.yml
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
secrets: inherit
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
cache: "npm"
|
|
||||||
cache-dependency-path: frontend/package-lock.json
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Install Playwright (chromium only)
|
|
||||||
run: task frontend:test:e2e:install -- chromium
|
|
||||||
- name: Run stubbed E2E tests (chromium)
|
|
||||||
run: task frontend:test:e2e -- --project=stubbed --workers=3
|
|
||||||
- name: Upload Playwright report
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: playwright-report-stubbed-${{ github.run_id }}
|
|
||||||
path: frontend/playwright-report/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
playwright-e2e-live:
|
playwright-e2e-live:
|
||||||
# Live-backend suite — boots Spring Boot and runs auth + real tool round-trips.
|
|
||||||
if: needs.files-changed.outputs.frontend == 'true'
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
needs: files-changed
|
needs: [files-changed]
|
||||||
runs-on: ubuntu-latest
|
permissions:
|
||||||
timeout-minutes: 30
|
contents: read
|
||||||
steps:
|
uses: ./.github/workflows/e2e-live.yml
|
||||||
- name: Harden Runner
|
secrets: inherit
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
|
||||||
with:
|
playwright-e2e-enterprise:
|
||||||
egress-policy: audit
|
if: needs.files-changed.outputs.proprietary == 'true'
|
||||||
- name: Checkout repository
|
needs: [files-changed]
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
permissions:
|
||||||
- name: Set up JDK 25
|
contents: read
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: ./.github/workflows/build-enterprise.yml
|
||||||
with:
|
secrets: inherit
|
||||||
java-version: "25"
|
|
||||||
distribution: "temurin"
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
cache: "npm"
|
|
||||||
cache-dependency-path: frontend/package-lock.json
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Install Playwright (chromium only)
|
|
||||||
run: task frontend:test:e2e:install -- chromium
|
|
||||||
- name: Start Spring Boot backend (background)
|
|
||||||
env:
|
|
||||||
# Suppress the analytics opt-in modal that fires on first admin login when
|
|
||||||
# enableAnalytics is null (see Onboarding.tsx). The modal renders a Mantine
|
|
||||||
# overlay that intercepts pointer events on every tool page until dismissed,
|
|
||||||
# which causes every "click run button" assertion in the live suite to fail.
|
|
||||||
SYSTEM_ENABLEANALYTICS: "false"
|
|
||||||
# NOTE: SECURITY_INITIALLOGIN_USERNAME/PASSWORD are intentionally NOT set.
|
|
||||||
# The live-setup project's bootstrap spec performs the real first-login
|
|
||||||
# flow against the backend's default admin/stirling user, exercising the
|
|
||||||
# forced-password-change UI and leaving the DB at admin/adminadmin for
|
|
||||||
# the rest of the live suite. This is both real coverage of the first-
|
|
||||||
# login flow and a stronger seed than env-var-driven user creation.
|
|
||||||
run: |
|
|
||||||
nohup ./gradlew :stirling-pdf:bootRun > /tmp/backend.log 2>&1 &
|
|
||||||
echo $! > /tmp/backend.pid
|
|
||||||
- name: Wait for backend to become ready
|
|
||||||
run: |
|
|
||||||
start=$SECONDS
|
|
||||||
# 300 iterations × 2s = 10 minute ceiling
|
|
||||||
for i in $(seq 1 300); do
|
|
||||||
if curl -fsS http://localhost:8080/api/v1/info/status >/dev/null 2>&1; then
|
|
||||||
echo "Backend up after $((SECONDS - start))s"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "Backend did not become ready in $((SECONDS - start))s"
|
|
||||||
tail -200 /tmp/backend.log || true
|
|
||||||
exit 1
|
|
||||||
- name: Run live E2E tests (chromium)
|
|
||||||
id: live-tests
|
|
||||||
run: task frontend:test:e2e -- --project=live
|
|
||||||
- name: Print backend log on failure
|
|
||||||
if: failure() && steps.live-tests.conclusion == 'failure'
|
|
||||||
run: |
|
|
||||||
echo "::group::Spring Boot backend log (last 500 lines)"
|
|
||||||
tail -500 /tmp/backend.log || echo "no backend log found"
|
|
||||||
echo "::endgroup::"
|
|
||||||
- name: Stop backend
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ -f /tmp/backend.pid ]; then
|
|
||||||
kill "$(cat /tmp/backend.pid)" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
- name: Upload backend log
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: backend-log-live-${{ github.run_id }}
|
|
||||||
path: /tmp/backend.log
|
|
||||||
retention-days: 7
|
|
||||||
- name: Upload Playwright report
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: playwright-report-live-${{ github.run_id }}
|
|
||||||
path: frontend/playwright-report/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
check-licence:
|
check-licence:
|
||||||
if: needs.files-changed.outputs.build == 'true'
|
if: needs.files-changed.outputs.build == 'true'
|
||||||
needs: [files-changed, build]
|
needs: [files-changed, build]
|
||||||
runs-on: ubuntu-latest
|
permissions:
|
||||||
steps:
|
contents: read
|
||||||
- name: Harden Runner
|
uses: ./.github/workflows/check-licence.yml
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
secrets: inherit
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
- name: Set up JDK 25
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
java-version: "25"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- name: Cache Gradle dependency artifacts
|
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/wrapper
|
|
||||||
~/.gradle/caches/modules-2/files-2.1
|
|
||||||
~/.gradle/caches/modules-2/metadata-2.*
|
|
||||||
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
with:
|
|
||||||
gradle-version: 9.3.1
|
|
||||||
cache-disabled: true
|
|
||||||
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Check licenses for compatibility
|
|
||||||
run: task backend:licenses:check
|
|
||||||
env:
|
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
||||||
|
|
||||||
- name: FAILED - check the licenses for compatibility
|
|
||||||
if: failure()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: dependencies-without-allowed-license.json
|
|
||||||
path: build/reports/dependency-license/dependencies-without-allowed-license.json
|
|
||||||
retention-days: 3
|
|
||||||
|
|
||||||
docker-compose-tests:
|
docker-compose-tests:
|
||||||
if: needs.files-changed.outputs.project == 'true'
|
if: needs.files-changed.outputs.project == 'true'
|
||||||
needs: files-changed
|
needs: [files-changed]
|
||||||
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
|
|
||||||
# (github.event_name == 'pull_request' &&
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'licenses') == false &&
|
|
||||||
# (
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Front End') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Java') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Back End') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Security') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'API') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Docker') ||
|
|
||||||
# contains(github.event.pull_request.labels.*.name, 'Test')
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: read
|
contents: read
|
||||||
checks: write
|
checks: write
|
||||||
|
uses: ./.github/workflows/docker-compose-tests.yml
|
||||||
steps:
|
secrets: inherit
|
||||||
- name: Harden Runner
|
with:
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
docker-base-changed: ${{ needs.files-changed.outputs.docker-base }}
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
- name: Set up JDK 25
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
java-version: "25"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- name: Cache Gradle dependency artifacts
|
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/wrapper
|
|
||||||
~/.gradle/caches/modules-2/files-2.1
|
|
||||||
~/.gradle/caches/modules-2/metadata-2.*
|
|
||||||
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
with:
|
|
||||||
gradle-version: 9.3.1
|
|
||||||
cache-disabled: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
|
||||||
|
|
||||||
# Expose ACTIONS_RUNTIME_TOKEN / ACTIONS_RESULTS_URL for docker buildx type=gha cache backend.
|
|
||||||
- name: Expose GitHub runtime for Buildx cache
|
|
||||||
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
|
|
||||||
|
|
||||||
- name: Install Docker Compose
|
|
||||||
run: |
|
|
||||||
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
cache: "pip" # caching pip dependencies
|
|
||||||
cache-dependency-path: ./testing/cucumber/requirements.txt
|
|
||||||
|
|
||||||
- name: Pip requirements
|
|
||||||
run: |
|
|
||||||
pip install --require-hashes --only-binary=:all: -r ./testing/cucumber/requirements.txt
|
|
||||||
|
|
||||||
- name: Run Docker Compose Tests
|
|
||||||
run: |
|
|
||||||
chmod +x ./testing/test_webpages.sh
|
|
||||||
chmod +x ./testing/test.sh
|
|
||||||
chmod +x ./testing/test_disabledEndpoints.sh
|
|
||||||
./testing/test.sh
|
|
||||||
env:
|
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
||||||
DOCKER_BASE_CHANGED: ${{ needs.files-changed.outputs.docker-base }}
|
|
||||||
|
|
||||||
- name: Upload Cucumber Report
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: cucumber-report
|
|
||||||
path: testing/cucumber/report.html
|
|
||||||
retention-days: 7
|
|
||||||
if-no-files-found: warn
|
|
||||||
|
|
||||||
- name: Upload Test Reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: docker-compose-test-reports
|
|
||||||
path: testing/reports/
|
|
||||||
retention-days: 7
|
|
||||||
if-no-files-found: warn
|
|
||||||
|
|
||||||
- name: Cucumber Test Report
|
|
||||||
if: always()
|
|
||||||
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
|
|
||||||
with:
|
|
||||||
name: Cucumber Tests
|
|
||||||
path: testing/cucumber/junit/*.xml
|
|
||||||
reporter: java-junit
|
|
||||||
fail-on-error: false
|
|
||||||
|
|
||||||
test-build-docker-images:
|
test-build-docker-images:
|
||||||
if: github.event_name == 'pull_request' && needs.files-changed.outputs.project == 'true'
|
if: github.event_name == 'pull_request' && needs.files-changed.outputs.project == 'true'
|
||||||
needs: [files-changed, build, check-generateOpenApiDocs, check-licence]
|
needs: [files-changed, build, check-generateOpenApiDocs, check-licence]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: read
|
||||||
|
uses: ./.github/workflows/test-build-docker.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
docker-base-changed: ${{ needs.files-changed.outputs.docker-base }}
|
||||||
|
|
||||||
|
tauri-build:
|
||||||
|
if: needs.files-changed.outputs.tauri == 'true'
|
||||||
|
needs: [files-changed]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
uses: ./.github/workflows/tauri-build.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
ai-engine:
|
||||||
|
if: needs.files-changed.outputs.engine == 'true'
|
||||||
|
needs: [files-changed]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
uses: ./.github/workflows/ai-engine.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
pre-commit:
|
||||||
|
needs: [files-changed]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
uses: ./.github/workflows/pre_commit.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
dependency-review:
|
||||||
|
needs: [files-changed]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
uses: ./.github/workflows/dependency-review.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Single status check that branch protection should mark as required.
|
||||||
|
# Succeeds when every upstream job is either `success` or `skipped` (path-
|
||||||
|
# gated jobs that didn't apply this run). Any `failure` or `cancelled`
|
||||||
|
# result fails the gate. `if: always()` ensures the gate evaluates even
|
||||||
|
# when an upstream job fails.
|
||||||
|
all-checks-passed:
|
||||||
|
name: All checks passed
|
||||||
|
if: always()
|
||||||
|
needs:
|
||||||
|
- files-changed
|
||||||
|
- build
|
||||||
|
- check-generateOpenApiDocs
|
||||||
|
- frontend-validation
|
||||||
|
- playwright-e2e
|
||||||
|
- playwright-e2e-live
|
||||||
|
- playwright-e2e-enterprise
|
||||||
|
- check-licence
|
||||||
|
- docker-compose-tests
|
||||||
|
- test-build-docker-images
|
||||||
|
- tauri-build
|
||||||
|
- ai-engine
|
||||||
|
- pre-commit
|
||||||
|
- dependency-review
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- docker-rev: docker/embedded/Dockerfile
|
|
||||||
artifact-suffix: Dockerfile
|
|
||||||
cache-scope: stirling-pdf-latest
|
|
||||||
- docker-rev: docker/embedded/Dockerfile.ultra-lite
|
|
||||||
artifact-suffix: Dockerfile.ultra-lite
|
|
||||||
cache-scope: stirling-pdf-ultra-lite
|
|
||||||
- docker-rev: docker/embedded/Dockerfile.fat
|
|
||||||
artifact-suffix: Dockerfile.fat
|
|
||||||
cache-scope: stirling-pdf-fat
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Verify every required job passed (or was legitimately skipped)
|
||||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ github.token }}
|
|
||||||
|
|
||||||
- name: Convert repository owner to lowercase
|
|
||||||
id: repoowner
|
|
||||||
run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Free disk space on runner
|
|
||||||
run: |
|
|
||||||
echo "Disk space before cleanup:" && df -h
|
|
||||||
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost
|
|
||||||
docker system prune -af || true
|
|
||||||
echo "Disk space after cleanup:" && df -h
|
|
||||||
|
|
||||||
- name: Set up JDK 25
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
java-version: "25"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- name: Cache Gradle dependency artifacts
|
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/wrapper
|
|
||||||
~/.gradle/caches/modules-2/files-2.1
|
|
||||||
~/.gradle/caches/modules-2/metadata-2.*
|
|
||||||
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
with:
|
|
||||||
gradle-version: 9.3.1
|
|
||||||
cache-disabled: true
|
|
||||||
|
|
||||||
- name: Install Task
|
|
||||||
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
|
||||||
- name: Build application
|
|
||||||
run: task backend:build
|
|
||||||
env:
|
env:
|
||||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
RESULTS: |
|
||||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
files-changed=${{ needs.files-changed.result }}
|
||||||
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
build=${{ needs.build.result }}
|
||||||
DISABLE_ADDITIONAL_FEATURES: true
|
check-generateOpenApiDocs=${{ needs.check-generateOpenApiDocs.result }}
|
||||||
STIRLING_PDF_DESKTOP_UI: false
|
frontend-validation=${{ needs.frontend-validation.result }}
|
||||||
|
playwright-e2e=${{ needs.playwright-e2e.result }}
|
||||||
- name: Set up QEMU
|
playwright-e2e-live=${{ needs.playwright-e2e-live.result }}
|
||||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
playwright-e2e-enterprise=${{ needs.playwright-e2e-enterprise.result }}
|
||||||
|
check-licence=${{ needs.check-licence.result }}
|
||||||
- name: Set up Docker Buildx
|
docker-compose-tests=${{ needs.docker-compose-tests.result }}
|
||||||
id: buildx
|
test-build-docker-images=${{ needs.test-build-docker-images.result }}
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
tauri-build=${{ needs.tauri-build.result }}
|
||||||
|
ai-engine=${{ needs.ai-engine.result }}
|
||||||
- name: Build base image locally (PR base change only)
|
pre-commit=${{ needs.pre-commit.result }}
|
||||||
if: github.event_name == 'pull_request' && needs.files-changed.outputs.docker-base == 'true'
|
dependency-review=${{ needs.dependency-review.result }}
|
||||||
run: |
|
run: |
|
||||||
docker build -t stirling-pdf-base:pr-test -f docker/base/Dockerfile docker/base
|
ok=true
|
||||||
|
while IFS='=' read -r name result; do
|
||||||
- name: Set base image and platform for this build
|
[ -z "$name" ] && continue
|
||||||
id: build-params
|
case "$result" in
|
||||||
run: |
|
success|skipped) printf ' %-30s %s\n' "$name" "$result" ;;
|
||||||
if [ "${{ github.event_name }}" == "pull_request" ] && [ "${{ needs.files-changed.outputs.docker-base }}" == "true" ]; then
|
*) printf '✗ %-30s %s\n' "$name" "$result"; ok=false ;;
|
||||||
echo "base_image=stirling-pdf-base:pr-test" >> $GITHUB_OUTPUT
|
esac
|
||||||
echo "platforms=linux/amd64" >> $GITHUB_OUTPUT
|
done <<< "$RESULTS"
|
||||||
else
|
if [ "$ok" != "true" ]; then
|
||||||
echo "base_image=stirlingtools/stirling-pdf-base:latest" >> $GITHUB_OUTPUT
|
echo ""
|
||||||
echo "platforms=linux/amd64,linux/arm64/v8" >> $GITHUB_OUTPUT
|
echo "One or more required checks failed or were cancelled."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo ""
|
||||||
- name: Build ${{ matrix.docker-rev }}
|
echo "All required checks passed."
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
|
||||||
with:
|
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
|
||||||
context: .
|
|
||||||
file: ./${{ matrix.docker-rev }}
|
|
||||||
push: false
|
|
||||||
cache-from: type=gha,scope=${{ matrix.cache-scope }}
|
|
||||||
cache-to: type=gha,mode=max,scope=${{ matrix.cache-scope }}
|
|
||||||
platforms: ${{ steps.build-params.outputs.platforms }}
|
|
||||||
build-args: |
|
|
||||||
BASE_IMAGE=${{ steps.build-params.outputs.base_image }}
|
|
||||||
provenance: true
|
|
||||||
sbom: true
|
|
||||||
|
|
||||||
- name: Upload Reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
||||||
with:
|
|
||||||
name: reports-docker-${{ matrix.artifact-suffix }}
|
|
||||||
path: |
|
|
||||||
build/reports/tests/
|
|
||||||
build/test-results/
|
|
||||||
build/reports/problems/
|
|
||||||
retention-days: 3
|
|
||||||
if-no-files-found: warn
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: License compatibility check
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml when build.gradle / module gradle
|
||||||
|
# files change. Verifies all transitive dependencies use a permitted license.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-licence:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: "25"
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: Cache Gradle dependency artifacts
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.gradle/caches/modules-2/files-2.1
|
||||||
|
~/.gradle/caches/modules-2/metadata-2.*
|
||||||
|
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||||
|
with:
|
||||||
|
gradle-version: 9.3.1
|
||||||
|
cache-disabled: true
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Check licenses for compatibility
|
||||||
|
run: task backend:licenses:check
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
|
||||||
|
- name: FAILED - check the licenses for compatibility
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: dependencies-without-allowed-license.json
|
||||||
|
path: build/reports/dependency-license/dependencies-without-allowed-license.json
|
||||||
|
retention-days: 3
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Generate OpenAPI documentation
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml when backend Java sources change.
|
||||||
|
# Generates SwaggerDoc.json so we know the doc still builds against the
|
||||||
|
# current code.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-generate-openapi-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: "25"
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: Cache Gradle dependency artifacts
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.gradle/caches/modules-2/files-2.1
|
||||||
|
~/.gradle/caches/modules-2/metadata-2.*
|
||||||
|
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||||
|
with:
|
||||||
|
gradle-version: 9.3.1
|
||||||
|
cache-disabled: true
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Generate OpenAPI documentation
|
||||||
|
run: task backend:swagger
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
DISABLE_ADDITIONAL_FEATURES: true
|
||||||
|
|
||||||
|
- name: Upload OpenAPI Documentation
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: openapi-docs
|
||||||
|
path: ./SwaggerDoc.json
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
# Dependency Review Action
|
name: Dependency Review
|
||||||
#
|
|
||||||
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
# Reusable workflow called from build.yml. Scans dependency manifest files
|
||||||
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
# changing in a PR for known-vulnerable packages, using the rules in
|
||||||
# Once installed, if the workflow run is marked as required,
|
# .github/config/dependency-review-config.yml.
|
||||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
|
||||||
#
|
|
||||||
# Source repository: https://github.com/actions/dependency-review-action
|
|
||||||
name: "Dependency Review"
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
workflow_call:
|
||||||
merge_group:
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
name: Docker Compose Cucumber tests
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml when project / docker / testing
|
||||||
|
# sources change. Boots the docker-compose stack and runs the cucumber
|
||||||
|
# scenarios in testing/cucumber.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
docker-base-changed:
|
||||||
|
description: "Whether the docker base image changed (forwarded from files-changed)."
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-compose-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
contents: read
|
||||||
|
checks: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: "25"
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: Cache Gradle dependency artifacts
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.gradle/caches/modules-2/files-2.1
|
||||||
|
~/.gradle/caches/modules-2/metadata-2.*
|
||||||
|
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||||
|
with:
|
||||||
|
gradle-version: 9.3.1
|
||||||
|
cache-disabled: true
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||||
|
|
||||||
|
# Expose ACTIONS_RUNTIME_TOKEN / ACTIONS_RESULTS_URL for docker buildx type=gha cache backend.
|
||||||
|
- name: Expose GitHub runtime for Buildx cache
|
||||||
|
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
|
||||||
|
|
||||||
|
- name: Install Docker Compose
|
||||||
|
run: |
|
||||||
|
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
cache: "pip" # caching pip dependencies
|
||||||
|
cache-dependency-path: ./testing/cucumber/requirements.txt
|
||||||
|
|
||||||
|
- name: Pip requirements
|
||||||
|
run: |
|
||||||
|
pip install --require-hashes --only-binary=:all: -r ./testing/cucumber/requirements.txt
|
||||||
|
|
||||||
|
- name: Run Docker Compose Tests
|
||||||
|
run: |
|
||||||
|
chmod +x ./testing/test_webpages.sh
|
||||||
|
chmod +x ./testing/test.sh
|
||||||
|
chmod +x ./testing/test_disabledEndpoints.sh
|
||||||
|
./testing/test.sh
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
DOCKER_BASE_CHANGED: ${{ inputs.docker-base-changed }}
|
||||||
|
|
||||||
|
- name: Upload Cucumber Report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: cucumber-report
|
||||||
|
path: testing/cucumber/report.html
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
- name: Upload Test Reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: docker-compose-test-reports
|
||||||
|
path: testing/reports/
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
- name: Cucumber Test Report
|
||||||
|
if: always()
|
||||||
|
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
|
||||||
|
with:
|
||||||
|
name: Cucumber Tests
|
||||||
|
path: testing/cucumber/junit/*.xml
|
||||||
|
reporter: java-junit
|
||||||
|
fail-on-error: false
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
name: Playwright E2E (live backend)
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml. Live-backend Playwright suite —
|
||||||
|
# boots Spring Boot and runs auth + real tool round-trips against the live
|
||||||
|
# server.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
playwright-e2e-live:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: "25"
|
||||||
|
distribution: "temurin"
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "npm"
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Install Playwright (chromium only)
|
||||||
|
run: task frontend:test:e2e:install -- chromium
|
||||||
|
- name: Start Spring Boot backend (background)
|
||||||
|
env:
|
||||||
|
# Suppress the analytics opt-in modal that fires on first admin login when
|
||||||
|
# enableAnalytics is null (see Onboarding.tsx). The modal renders a Mantine
|
||||||
|
# overlay that intercepts pointer events on every tool page until dismissed,
|
||||||
|
# which causes every "click run button" assertion in the live suite to fail.
|
||||||
|
SYSTEM_ENABLEANALYTICS: "false"
|
||||||
|
# NOTE: SECURITY_INITIALLOGIN_USERNAME/PASSWORD are intentionally NOT set.
|
||||||
|
# The live-setup project's bootstrap spec performs the real first-login
|
||||||
|
# flow against the backend's default admin/stirling user, exercising the
|
||||||
|
# forced-password-change UI and leaving the DB at admin/adminadmin for
|
||||||
|
# the rest of the live suite. This is both real coverage of the first-
|
||||||
|
# login flow and a stronger seed than env-var-driven user creation.
|
||||||
|
run: |
|
||||||
|
nohup ./gradlew :stirling-pdf:bootRun > /tmp/backend.log 2>&1 &
|
||||||
|
echo $! > /tmp/backend.pid
|
||||||
|
- name: Wait for backend to become ready
|
||||||
|
run: |
|
||||||
|
start=$SECONDS
|
||||||
|
# 300 iterations × 2s = 10 minute ceiling
|
||||||
|
for i in $(seq 1 300); do
|
||||||
|
if curl -fsS http://localhost:8080/api/v1/info/status >/dev/null 2>&1; then
|
||||||
|
echo "Backend up after $((SECONDS - start))s"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "Backend did not become ready in $((SECONDS - start))s"
|
||||||
|
tail -200 /tmp/backend.log || true
|
||||||
|
exit 1
|
||||||
|
- name: Run live E2E tests (chromium)
|
||||||
|
id: live-tests
|
||||||
|
run: task frontend:test:e2e -- --project=live
|
||||||
|
- name: Print backend log on failure
|
||||||
|
if: failure() && steps.live-tests.conclusion == 'failure'
|
||||||
|
run: |
|
||||||
|
echo "::group::Spring Boot backend log (last 500 lines)"
|
||||||
|
tail -500 /tmp/backend.log || echo "no backend log found"
|
||||||
|
echo "::endgroup::"
|
||||||
|
- name: Stop backend
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
if [ -f /tmp/backend.pid ]; then
|
||||||
|
kill "$(cat /tmp/backend.pid)" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- name: Upload backend log
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: backend-log-live-${{ github.run_id }}
|
||||||
|
path: /tmp/backend.log
|
||||||
|
retention-days: 7
|
||||||
|
- name: Upload Playwright report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: playwright-report-live-${{ github.run_id }}
|
||||||
|
path: frontend/playwright-report/
|
||||||
|
retention-days: 7
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
name: Playwright E2E (stubbed)
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml. Backend-free Playwright suite —
|
||||||
|
# fast, no Spring Boot required. Runs against the `stubbed` project which
|
||||||
|
# mocks API responses in the browser.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
playwright-e2e:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "npm"
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Install Playwright (chromium only)
|
||||||
|
run: task frontend:test:e2e:install -- chromium
|
||||||
|
- name: Run stubbed E2E tests (chromium)
|
||||||
|
run: task frontend:test:e2e -- --project=stubbed --workers=3
|
||||||
|
- name: Upload Playwright report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: playwright-report-stubbed-${{ github.run_id }}
|
||||||
|
path: frontend/playwright-report/
|
||||||
|
retention-days: 7
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
name: Frontend lint, type-check, and build
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml when frontend / testing sources
|
||||||
|
# change. Runs the consolidated `task frontend:check:all` (lint, types,
|
||||||
|
# unit tests, build) and uploads the dist artifact for downstream jobs.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
frontend-validation:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "npm"
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Quality-check frontend
|
||||||
|
id: frontend-check
|
||||||
|
run: task frontend:check:all
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Comment on frontend check failure
|
||||||
|
# Only post a comment on PRs. github-script's PR helpers need an
|
||||||
|
# issue/PR number, which doesn't exist on merge_group runs.
|
||||||
|
if: steps.frontend-check.outcome == 'failure' && github.event_name == 'pull_request'
|
||||||
|
continue-on-error: true
|
||||||
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const marker = '<!-- frontend-check -->';
|
||||||
|
const body = [
|
||||||
|
marker,
|
||||||
|
'### Frontend Check Failed',
|
||||||
|
'',
|
||||||
|
'There are issues with your frontend code that will need to be fixed before they can be merged in.',
|
||||||
|
'',
|
||||||
|
'Run `task frontend:fix` to auto-fix what can be fixed automatically, then run `task frontend:check:all` to see what still needs fixing manually.',
|
||||||
|
].join('\n');
|
||||||
|
const { data: comments } = await github.rest.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
});
|
||||||
|
const existing = comments.find(c => c.body.includes(marker));
|
||||||
|
if (existing) {
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: existing.id,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
- name: Fail if frontend check failed
|
||||||
|
if: steps.frontend-check.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
echo "============================================"
|
||||||
|
echo " Frontend Check Failed"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "There are issues with your frontend code that"
|
||||||
|
echo "will need to be fixed before they can be merged in."
|
||||||
|
echo ""
|
||||||
|
echo "Run 'task frontend:fix' to auto-fix what can be"
|
||||||
|
echo "fixed automatically, then run 'task frontend:check:all'"
|
||||||
|
echo "to see what still needs fixing manually."
|
||||||
|
echo "============================================"
|
||||||
|
exit 1
|
||||||
|
- name: Upload frontend build artifacts
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: frontend-build
|
||||||
|
path: frontend/dist/
|
||||||
|
retention-days: 3
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
name: Pre-commit
|
name: Pre-commit
|
||||||
|
|
||||||
|
# Runs `pre-commit run` for ruff / codespell / gitleaks / EOF / trailing-ws.
|
||||||
|
# Called from build.yml on PRs and merge_group; also runnable on demand via
|
||||||
|
# workflow_dispatch for manual local-equivalent linting.
|
||||||
on:
|
on:
|
||||||
|
workflow_call:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
merge_group:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
name: Build Tauri Applications
|
name: Build Tauri Applications
|
||||||
|
|
||||||
|
# Multi-OS Tauri desktop bundle build matrix (Windows / macOS arm+intel /
|
||||||
|
# Linux). Called from build.yml on PRs that touch desktop sources (gated
|
||||||
|
# via the `tauri` filter in .github/config/.files.yaml). Also runnable
|
||||||
|
# on demand via workflow_dispatch with a per-platform selector.
|
||||||
on:
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
platform:
|
||||||
|
description: "Platform to build (windows, macos, linux, or all)."
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "all"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
platform:
|
platform:
|
||||||
@@ -13,17 +24,6 @@ on:
|
|||||||
- windows
|
- windows
|
||||||
- macos
|
- macos
|
||||||
- linux
|
- linux
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
types: [opened, reopened, synchronize, ready_for_review]
|
|
||||||
paths:
|
|
||||||
- "frontend/src-tauri/**"
|
|
||||||
- "frontend/src/desktop/**"
|
|
||||||
- "frontend/tsconfig.desktop.json"
|
|
||||||
- "frontend/package.json"
|
|
||||||
- "frontend/package-lock.json"
|
|
||||||
- "frontend/vite.config.ts"
|
|
||||||
- ".github/workflows/tauri-build.yml"
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -45,20 +45,15 @@ jobs:
|
|||||||
id: set-matrix
|
id: set-matrix
|
||||||
env:
|
env:
|
||||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
PLATFORM: ${{ inputs.platform }}
|
||||||
run: |
|
run: |
|
||||||
WINDOWS='{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}'
|
WINDOWS='{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}'
|
||||||
MACOS_ARM='{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"}'
|
MACOS_ARM='{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"}'
|
||||||
MACOS_INTEL='{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}'
|
MACOS_INTEL='{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}'
|
||||||
LINUX='{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}'
|
LINUX='{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}'
|
||||||
|
|
||||||
# Resolve requested platform (non-dispatch events always build all)
|
# Resolve requested platform — populated by either workflow_dispatch
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
# or workflow_call inputs; both paths default to "all".
|
||||||
PLATFORM="${{ github.event.inputs.platform }}"
|
|
||||||
else
|
|
||||||
PLATFORM="all"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build candidate list
|
|
||||||
case "$PLATFORM" in
|
case "$PLATFORM" in
|
||||||
windows) ENTRIES=("$WINDOWS") ;;
|
windows) ENTRIES=("$WINDOWS") ;;
|
||||||
macos) ENTRIES=("$MACOS_ARM" "$MACOS_INTEL") ;;
|
macos) ENTRIES=("$MACOS_ARM" "$MACOS_INTEL") ;;
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
name: Build Docker images (PR test)
|
||||||
|
|
||||||
|
# Reusable workflow called from build.yml on PRs to verify the three
|
||||||
|
# embedded Dockerfiles (default, ultra-lite, fat) still build cleanly,
|
||||||
|
# optionally against a freshly-built base image when the PR touches the
|
||||||
|
# base Dockerfile.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
docker-base-changed:
|
||||||
|
description: "Whether the docker base image changed (forwarded from files-changed)."
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# TODO: extract a pre-matrix `prepare` job that runs once and produces
|
||||||
|
# shared artifacts for the three matrix entries below to consume:
|
||||||
|
# 1. `task backend:build` — currently runs 3× in parallel with
|
||||||
|
# identical env (DISABLE_ADDITIONAL_FEATURES=true,
|
||||||
|
# STIRLING_PDF_DESKTOP_UI=false). Build once, upload the JAR as an
|
||||||
|
# artifact, matrix entries download.
|
||||||
|
# 2. The base-image `docker build` (gated on docker-base-changed) —
|
||||||
|
# currently runs 3× in parallel against the same Dockerfile and
|
||||||
|
# context. Build once, `docker save` to an artifact, matrix entries
|
||||||
|
# `docker load` before the embedded build.
|
||||||
|
# Saves ~2 full backend builds + 2 base-image builds per PR that touches
|
||||||
|
# docker. May also be reusable from backend-build.yml's jdk-25 +
|
||||||
|
# spring-security=true matrix entry if `task backend:build` and
|
||||||
|
# `task backend:build:ci` produce equivalent JARs (verify before wiring).
|
||||||
|
test-build-docker-images:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- docker-rev: docker/embedded/Dockerfile
|
||||||
|
artifact-suffix: Dockerfile
|
||||||
|
cache-scope: stirling-pdf-latest
|
||||||
|
- docker-rev: docker/embedded/Dockerfile.ultra-lite
|
||||||
|
artifact-suffix: Dockerfile.ultra-lite
|
||||||
|
cache-scope: stirling-pdf-ultra-lite
|
||||||
|
- docker-rev: docker/embedded/Dockerfile.fat
|
||||||
|
artifact-suffix: Dockerfile.fat
|
||||||
|
cache-scope: stirling-pdf-fat
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ github.token }}
|
||||||
|
|
||||||
|
- name: Convert repository owner to lowercase
|
||||||
|
id: repoowner
|
||||||
|
run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Free disk space on runner
|
||||||
|
run: |
|
||||||
|
echo "Disk space before cleanup:" && df -h
|
||||||
|
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost
|
||||||
|
docker system prune -af || true
|
||||||
|
echo "Disk space after cleanup:" && df -h
|
||||||
|
|
||||||
|
- name: Set up JDK 25
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: "25"
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: Cache Gradle dependency artifacts
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
~/.gradle/caches/modules-2/files-2.1
|
||||||
|
~/.gradle/caches/modules-2/metadata-2.*
|
||||||
|
key: gradle-deps-${{ runner.os }}-jdk-25-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }}
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||||
|
with:
|
||||||
|
gradle-version: 9.3.1
|
||||||
|
cache-disabled: true
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
|
||||||
|
- name: Build application
|
||||||
|
run: task backend:build
|
||||||
|
env:
|
||||||
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||||
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
||||||
|
DISABLE_ADDITIONAL_FEATURES: true
|
||||||
|
STIRLING_PDF_DESKTOP_UI: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||||
|
|
||||||
|
- name: Build base image locally (PR base change only)
|
||||||
|
if: github.event_name == 'pull_request' && inputs.docker-base-changed == 'true'
|
||||||
|
run: |
|
||||||
|
docker build -t stirling-pdf-base:pr-test -f docker/base/Dockerfile docker/base
|
||||||
|
|
||||||
|
- name: Set base image and platform for this build
|
||||||
|
id: build-params
|
||||||
|
# Pass workflow inputs through env vars rather than expanding `${{ }}`
|
||||||
|
# directly into the shell — defense-in-depth against template injection
|
||||||
|
# if any upstream provider of these values ever becomes less trusted.
|
||||||
|
# GITHUB_EVENT_NAME is already provided by the runner.
|
||||||
|
env:
|
||||||
|
DOCKER_BASE_CHANGED: ${{ inputs.docker-base-changed }}
|
||||||
|
run: |
|
||||||
|
if [ "$GITHUB_EVENT_NAME" = "pull_request" ] && [ "$DOCKER_BASE_CHANGED" = "true" ]; then
|
||||||
|
echo "base_image=stirling-pdf-base:pr-test" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "platforms=linux/amd64" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "base_image=stirlingtools/stirling-pdf-base:latest" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "platforms=linux/amd64,linux/arm64/v8" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.docker-rev }}
|
||||||
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./${{ matrix.docker-rev }}
|
||||||
|
push: false
|
||||||
|
cache-from: type=gha,scope=${{ matrix.cache-scope }}
|
||||||
|
cache-to: type=gha,mode=max,scope=${{ matrix.cache-scope }}
|
||||||
|
platforms: ${{ steps.build-params.outputs.platforms }}
|
||||||
|
build-args: |
|
||||||
|
BASE_IMAGE=${{ steps.build-params.outputs.base_image }}
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
|
|
||||||
|
- name: Upload Reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: reports-docker-${{ matrix.artifact-suffix }}
|
||||||
|
path: |
|
||||||
|
build/reports/tests/
|
||||||
|
build/test-results/
|
||||||
|
build/reports/problems/
|
||||||
|
retention-days: 3
|
||||||
|
if-no-files-found: warn
|
||||||
Reference in New Issue
Block a user