From 702f4e5c2c4090fa8b0e1b324e55c7d196a2aa53 Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:16:57 +0100 Subject: [PATCH] Add Taskfile for unified dev workflow across all components (#6080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Add Taskfile for unified dev workflow ### Summary - Introduces [Taskfile](https://taskfile.dev/) as the single CLI entry point for all development workflows across backend, frontend, engine, Docker, and desktop - ~80 tasks organized into 6 namespaces: `backend:`, `frontend:`, `engine:`, `docker:`, `desktop:`, plus root-level composites - All CI workflows migrated to use Task - Deletes `engine/Makefile` and `scripts/build-tauri-jlink.{sh,bat}` — replaced by Task equivalents - Removes redundant npm scripts (`dev`, `build`, `prep`, `lint`, `test`, `typecheck:all`) from `package.json` - Smart dependency caching: `sources`/`status`/`generates` fingerprinting, CI-aware `npm ci` vs `npm install`, `run: once` for parallel dep deduplication ### What this does NOT do - Does not replace Gradle, npm, or Docker — Taskfile is a thin orchestration wrapper - Does not change application code or behavior ### Install ``` npm install -g @go-task/cli # or: brew install go-task, winget install Task.Task ``` ### Quick start ``` task --list # discover all tasks task install # install all deps task dev # start backend + frontend task dev:all # also start AI engine task test # run all tests task check # quick quality gate (local dev) task check:all # full CI quality gate ``` ### Test plan - [ ] Install `task` CLI and run `task --list` — verify all tasks display - [ ] Run `task install` — verify frontend + engine deps install - [ ] Run `task dev` — verify backend + frontend start, Ctrl+C exits cleanly - [ ] Run `task frontend:check` — verify typecheck + lint + test pass - [ ] Run `task desktop:dev` — verify jlink builds are cached on second run - [ ] Verify CI passes on all workflows --------- Co-authored-by: James Brunton --- .github/config/.files.yaml | 3 +- .github/pull_request_template.md | 5 +- .../workflows/PR-Demo-Comment-with-react.yml | 4 +- .github/workflows/ai-engine.yml | 20 +- .github/workflows/build.yml | 105 +++-- .../frontend-backend-licenses-update.yml | 15 +- .github/workflows/multiOSReleases.yml | 7 +- .github/workflows/nightly.yml | 12 +- .github/workflows/push-docker.yml | 2 + .github/workflows/swagger.yml | 2 + .github/workflows/tauri-build.yml | 78 +-- .github/workflows/testdriver.yml | 4 +- .gitignore | 3 + .taskfiles/backend.yml | 111 +++++ .taskfiles/desktop.yml | 105 +++++ .taskfiles/docker.yml | 57 +++ .taskfiles/engine.yml | 124 +++++ .taskfiles/frontend.yml | 313 +++++++++++++ AGENTS.md | 74 ++- CONTRIBUTING.md | 18 +- DeveloperGuide.md | 70 ++- README.md | 2 +- Taskfile.yml | 128 +++++ app/core/build.gradle | 5 +- devGuide/DeveloperGuide.md | 443 +----------------- devGuide/README.md | 3 +- docker/README.md | 17 + docker/embedded/Dockerfile | 5 + docker/embedded/Dockerfile.fat | 5 + docker/embedded/Dockerfile.ultra-lite | 5 + docker/frontend/Dockerfile | 2 +- engine/AGENTS.md | 16 +- engine/Makefile | 82 ---- frontend/README.md | 166 ++----- frontend/package.json | 52 -- frontend/playwright.config.ts | 2 +- frontend/src-tauri/tauri.conf.json | 4 +- scripts/build-tauri-jlink.bat | 176 ------- scripts/build-tauri-jlink.sh | 229 --------- 39 files changed, 1172 insertions(+), 1302 deletions(-) create mode 100644 .taskfiles/backend.yml create mode 100644 .taskfiles/desktop.yml create mode 100644 .taskfiles/docker.yml create mode 100644 .taskfiles/engine.yml create mode 100644 .taskfiles/frontend.yml create mode 100644 Taskfile.yml delete mode 100644 engine/Makefile delete mode 100644 scripts/build-tauri-jlink.bat delete mode 100755 scripts/build-tauri-jlink.sh diff --git a/.github/config/.files.yaml b/.github/config/.files.yaml index 7d73e47137..48d03bfc73 100644 --- a/.github/config/.files.yaml +++ b/.github/config/.files.yaml @@ -46,8 +46,7 @@ frontend: &frontend - testing/** - docker/** - scripts/translations/*.py - - scripts/build-tauri-jlink.bat - - scripts/build-tauri-jlink.sh + - .taskfiles/desktop.yml - scripts/convert_cff_to_ttf.py - scripts/harvest_type3_fonts.py - scripts/ignore_translation.toml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d1287c0117..d9eb6dbe10 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Closes #(issue_number) ### 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/devGuide/DeveloperGuide.md) (if applicable) +- [ ] 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 @@ -37,4 +37,5 @@ Closes #(issue_number) ### Testing (if applicable) -- [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. +- [ ] 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. diff --git a/.github/workflows/PR-Demo-Comment-with-react.yml b/.github/workflows/PR-Demo-Comment-with-react.yml index 2fc00080af..41449b8cee 100644 --- a/.github/workflows/PR-Demo-Comment-with-react.yml +++ b/.github/workflows/PR-Demo-Comment-with-react.yml @@ -161,6 +161,8 @@ jobs: with: gradle-version: 9.3.1 + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Run Gradle Command run: | if [ "${{ needs.check-comment.outputs.disable_security }}" == "true" ]; then @@ -168,7 +170,7 @@ jobs: else export DISABLE_ADDITIONAL_FEATURES=false fi - ./gradlew build + task backend:build env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} diff --git a/.github/workflows/ai-engine.yml b/.github/workflows/ai-engine.yml index ed0569c312..c0cd927841 100644 --- a/.github/workflows/ai-engine.yml +++ b/.github/workflows/ai-engine.yml @@ -11,10 +11,6 @@ jobs: permissions: contents: read pull-requests: write - defaults: - run: - working-directory: engine - steps: - name: Checkout code uses: actions/checkout@v4 @@ -24,12 +20,12 @@ jobs: with: enable-cache: true - - name: Install dependencies - run: make install + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Run fixers # Ignore errors here because we're going to add comments for them in the following steps before actually failing - run: make fix || true + run: task engine:fix || true - name: Check for fixer changes id: fixer_changes @@ -60,7 +56,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: "The Python code in your PR has formatting/linting issues. Consider running `make fix` locally or setting up your editor's Ruff integration to auto-format and lint your files as you go, or commit the suggested changes on this PR.", + body: "The Python code in your PR has formatting/linting issues. Consider running `task engine:fix` locally or setting up your editor's Ruff integration to auto-format and lint your files as you go, or commit the suggested changes on this PR.", }); - name: Verify fixer changes are committed @@ -68,16 +64,16 @@ jobs: run: | if ! git diff --exit-code; then echo "Fixes are out of date." - echo "Apply the reviewdog suggestions or run 'make fix' from engine/ and commit the updated files." + echo "Apply the reviewdog suggestions or run 'task engine:fix' from the repo root and commit the updated files." git --no-pager diff --stat exit 1 fi - name: Run linting - run: make lint + run: task engine:lint - name: Run type checking - run: make typecheck + run: task engine:typecheck - name: Run tests - run: make test + run: task engine:test diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 643ef13fd0..78efa46cc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,10 +85,12 @@ jobs: 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: ./gradlew spotlessCheck + run: task backend:format:check continue-on-error: true env: MAVEN_USER: ${{ secrets.MAVEN_USER }} @@ -97,6 +99,7 @@ jobs: - name: Comment on Java formatting failure if: steps.spotless-check.outcome == 'failure' + continue-on-error: true uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | @@ -108,7 +111,7 @@ jobs: 'Your code has formatting issues. Run the following command to fix them:', '', '```bash', - './gradlew spotlessApply', + 'task backend:format', '```', '', 'Then commit and push the changes.', @@ -137,10 +140,22 @@ jobs: - name: Fail if Java formatting issues found if: steps.spotless-check.outcome == 'failure' - run: exit 1 + 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: ./gradlew build -PnoSpotless + run: task backend:build:ci env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} @@ -224,8 +239,10 @@ jobs: 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: ./gradlew :stirling-pdf:generateOpenApiDocs + run: task backend:swagger env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} @@ -258,29 +275,26 @@ jobs: node-version: "22" cache: "npm" cache-dependency-path: frontend/package-lock.json - - name: Install frontend dependencies - run: cd frontend && npm ci - - name: Check TypeScript formatting (Prettier) - id: prettier-check - run: cd frontend && npm run format:check + - 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 + if: steps.frontend-check.outcome == 'failure' continue-on-error: true - - name: Comment on TypeScript formatting failure - if: steps.prettier-check.outcome == 'failure' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | - const marker = ''; + const marker = ''; const body = [ marker, - '### TypeScript Formatting Check Failed', + '### Frontend Check Failed', '', - 'Your code has formatting issues. Run the following command to fix them:', + 'There are issues with your frontend code that will need to be fixed before they can be merged in.', '', - '```bash', - 'cd frontend && npm run fix', - '```', - '', - 'Then commit and push the changes.', + '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, @@ -303,17 +317,21 @@ jobs: body, }); } - - name: Fail if TypeScript formatting issues found - if: steps.prettier-check.outcome == 'failure' - run: exit 1 - - name: Type-check frontend - run: cd frontend && npm run prep && npm run typecheck:all - - name: Lint frontend - run: cd frontend && npm run lint - - name: Build frontend - run: cd frontend && npm run build - - name: Run frontend tests - run: cd frontend && npm run test -- --run + - 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: @@ -338,14 +356,12 @@ jobs: node-version: "22" cache: "npm" cache-dependency-path: frontend/package-lock.json - - name: Install frontend dependencies - run: cd frontend && npm ci - - name: Generate icons - run: cd frontend && node scripts/generate-icons.js + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Install Playwright (chromium only) - run: cd frontend && npx playwright install chromium --with-deps + run: task frontend:test:e2e:install -- chromium - name: Run E2E tests (chromium) - run: cd frontend && npx playwright test --project=chromium + run: task frontend:test:e2e -- --project=chromium - name: Upload Playwright report if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 @@ -388,13 +404,10 @@ jobs: gradle-version: 9.3.1 cache-disabled: true - - name: check the licenses for compatibility - # NOTE: --no-parallel is intentional here. Running the checkLicense task in parallel with other - # Gradle tasks has been observed to cause intermittent failures with the dependency license - # checking plugin on this Gradle version. Disabling parallel execution trades some build speed - # for more reliable, deterministic license checks. If upgrading Gradle or the plugin, consider - # re-evaluating whether this flag is still required before removing it. - run: ./gradlew checkLicense --no-parallel + - 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 }} @@ -589,8 +602,10 @@ jobs: gradle-version: 9.3.1 cache-disabled: true + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Build application - run: ./gradlew build + run: task backend:build env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} diff --git a/.github/workflows/frontend-backend-licenses-update.yml b/.github/workflows/frontend-backend-licenses-update.yml index a7e10dd4fe..be7c0f1148 100644 --- a/.github/workflows/frontend-backend-licenses-update.yml +++ b/.github/workflows/frontend-backend-licenses-update.yml @@ -89,12 +89,13 @@ jobs: NPM_CONFIG_IGNORE_SCRIPTS: "true" run: npm ci --ignore-scripts --audit=false --fund=false + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Generate frontend license report (internal PR) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false - working-directory: frontend env: PR_IS_FORK: "false" - run: npm run generate-licenses + run: task frontend:licenses:generate - name: Generate frontend license report (fork PRs, pinned) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true @@ -341,15 +342,11 @@ jobs: with: gradle-version: 9.3.1 + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Check licenses and generate report id: license-check - run: | - # NOTE: --no-parallel is intentional here. Running the license-checking tasks in parallel has - # previously caused intermittent concurrency issues in CI (e.g. flaky failures in the license - # plugin/Gradle when multiple projects are evaluated concurrently). Disabling parallelism trades - # some build speed for more reliable license reports. If the underlying issues are resolved in - # future Gradle or plugin versions, this flag can be reconsidered. - ./gradlew checkLicense generateLicenseReport --no-parallel || echo "LICENSE_CHECK_FAILED=true" >> $GITHUB_ENV + run: task backend:licenses:generate || echo "LICENSE_CHECK_FAILED=true" >> $GITHUB_ENV env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index b1ee6c8c88..91a57cc297 100644 --- a/.github/workflows/multiOSReleases.yml +++ b/.github/workflows/multiOSReleases.yml @@ -63,11 +63,11 @@ jobs: with: gradle-version: 9.3.1 + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Get version number id: versionNumber run: | - echo "Running gradlew printVersion..." - ./gradlew printVersion --quiet VERSION=$(./gradlew printVersion --quiet | tail -1) echo "Extracted version: $VERSION" echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT @@ -295,8 +295,7 @@ jobs: DISABLE_ADDITIONAL_FEATURES: true - name: Install frontend dependencies - working-directory: ./frontend - run: npm ci + run: task frontend:install # DigiCert KeyLocker Setup (Cloud HSM) - name: Setup DigiCert KeyLocker diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b3a13e233d..ce7b000475 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,17 +32,13 @@ jobs: cache: "npm" cache-dependency-path: frontend/package-lock.json - - name: Install frontend dependencies - run: cd frontend && npm ci - - - name: Generate icons - run: cd frontend && node scripts/generate-icons.js - + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Install all Playwright browsers - run: cd frontend && npx playwright install --with-deps + run: task frontend:test:e2e:install - name: Run E2E tests (all browsers) - run: cd frontend && npx playwright test + run: task frontend:test:e2e - name: Upload Playwright report if: always() diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 11151557ea..244c044bd8 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -64,6 +64,8 @@ jobs: id: buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Get version number id: versionNumber run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/swagger.yml b/.github/workflows/swagger.yml index 78195183ad..2b34ab9ca6 100644 --- a/.github/workflows/swagger.yml +++ b/.github/workflows/swagger.yml @@ -56,6 +56,8 @@ jobs: SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} SWAGGERHUB_USER: "Frooodle" + - name: Install Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 - name: Get version number id: versionNumber run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index d7ca66b2e0..6924ecf489 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -128,86 +128,16 @@ jobs: with: gradle-version: 9.3.1 - - name: Build Java backend with JLink - working-directory: ./ - shell: bash - run: | - chmod +x ./gradlew - echo "🔧 Building Stirling-PDF JAR..." - # STIRLING_PDF_DESKTOP_UI=false ./gradlew bootJar --no-daemon - ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube - - # Find the built JAR - STIRLING_JAR=$(ls app/core/build/libs/stirling-pdf-*.jar | head -n 1) - echo "✅ Built JAR: $STIRLING_JAR" - - # Create Tauri directories - mkdir -p ./frontend/src-tauri/libs - mkdir -p ./frontend/src-tauri/runtime - - # Copy JAR to Tauri libs - cp "$STIRLING_JAR" ./frontend/src-tauri/libs/ - echo "✅ JAR copied to Tauri libs" - - # Analyze JAR dependencies for jlink modules - echo "🔍 Analyzing JAR dependencies..." - if command -v jdeps &> /dev/null; then - DETECTED_MODULES=$(jdeps --print-module-deps --ignore-missing-deps "$STIRLING_JAR" 2>/dev/null || echo "") - if [ -n "$DETECTED_MODULES" ]; then - echo "📋 jdeps detected modules: $DETECTED_MODULES" - MODULES="$DETECTED_MODULES,java.compiler,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" - else - echo "⚠️ jdeps analysis failed, using predefined modules" - MODULES="java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" - fi - else - echo "⚠️ jdeps not available, using predefined modules" - MODULES="java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" - fi - - # Create custom JRE with jlink (always rebuild) - echo "🔧 Creating custom JRE with jlink..." - echo "📋 Using modules: $MODULES" - - # Remove any existing JRE - rm -rf ./frontend/src-tauri/runtime/jre - - # Create the custom JRE - jlink \ - --add-modules "$MODULES" \ - --strip-debug \ - --compress=2 \ - --no-header-files \ - --no-man-pages \ - --output ./frontend/src-tauri/runtime/jre - - if [ ! -d "./frontend/src-tauri/runtime/jre" ]; then - echo "❌ Failed to create JLink runtime" - exit 1 - fi - - # Test the bundled runtime - if [ -f "./frontend/src-tauri/runtime/jre/bin/java" ]; then - RUNTIME_VERSION=$(./frontend/src-tauri/runtime/jre/bin/java --version 2>&1 | head -n 1) - echo "✅ Custom JRE created successfully: $RUNTIME_VERSION" - else - echo "❌ Custom JRE executable not found" - exit 1 - fi - - # Calculate runtime size - RUNTIME_SIZE=$(du -sh ./frontend/src-tauri/runtime/jre | cut -f1) - echo "📊 Custom JRE size: $RUNTIME_SIZE" + - name: Setup Task + uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 + - name: Prepare desktop build + run: task desktop:prepare env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} DISABLE_ADDITIONAL_FEATURES: true - - name: Install frontend dependencies - working-directory: ./frontend - run: npm ci - # DigiCert KeyLocker Setup (Cloud HSM) - name: Setup DigiCert KeyLocker id: digicert-setup diff --git a/.github/workflows/testdriver.yml b/.github/workflows/testdriver.yml index 5d2466013b..4d61ce5df0 100644 --- a/.github/workflows/testdriver.yml +++ b/.github/workflows/testdriver.yml @@ -167,9 +167,9 @@ jobs: with: key: ${{secrets.TESTDRIVER_API_KEY}} prerun: | + choco install go-task -y + task frontend:build cd frontend - npm install - npm run build npm install dashcam-chrome --save Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=$(pwd)/node_modules/dashcam-chrome/build", "http://${{ secrets.NEW_VPS_HOST }}:1337" Start-Sleep -Seconds 20 diff --git a/.gitignore b/.gitignore index 880715ff97..1275a0991d 100644 --- a/.gitignore +++ b/.gitignore @@ -219,6 +219,9 @@ id_ed25519.pub .ssh/ *ssh +# Taskfile checksum cache +.task/ + # cache .cache .ruff_cache diff --git a/.taskfiles/backend.yml b/.taskfiles/backend.yml new file mode 100644 index 0000000000..3308e7d8b4 --- /dev/null +++ b/.taskfiles/backend.yml @@ -0,0 +1,111 @@ +version: '3' + +tasks: + dev: + desc: "Start backend dev server" + ignore_error: true + cmds: + - cmd: cmd /c gradlew.bat :stirling-pdf:bootRun + platforms: [windows] + - cmd: ./gradlew :stirling-pdf:bootRun + platforms: [linux, darwin] + + build: + desc: "Full backend build" + cmds: + - cmd: cmd /c gradlew.bat clean build + platforms: [windows] + - cmd: ./gradlew clean build + platforms: [linux, darwin] + + build:fast: + desc: "Build without tests" + cmds: + - cmd: cmd /c gradlew.bat clean build -x test + platforms: [windows] + - cmd: ./gradlew clean build -x test + platforms: [linux, darwin] + + build:ci: + desc: "Build for CI (formatting checked separately)" + cmds: + - cmd: cmd /c gradlew.bat build -PnoSpotless + platforms: [windows] + - cmd: ./gradlew build -PnoSpotless + platforms: [linux, darwin] + + test: + desc: "Run backend tests" + cmds: + - cmd: cmd /c gradlew.bat test + platforms: [windows] + - cmd: ./gradlew test + platforms: [linux, darwin] + + format: + desc: "Auto-fix code formatting" + cmds: + - cmd: cmd /c gradlew.bat spotlessApply + platforms: [windows] + - cmd: ./gradlew spotlessApply + platforms: [linux, darwin] + + format:check: + desc: "Check code formatting" + cmds: + - cmd: cmd /c gradlew.bat spotlessCheck + platforms: [windows] + - cmd: ./gradlew spotlessCheck + platforms: [linux, darwin] + + fix: + desc: "Auto-fix backend" + cmds: + - task: format + + swagger: + desc: "Generate OpenAPI docs" + cmds: + - cmd: cmd /c gradlew.bat :stirling-pdf:generateOpenApiDocs + platforms: [windows] + - cmd: ./gradlew :stirling-pdf:generateOpenApiDocs + platforms: [linux, darwin] + + check: + desc: "Backend quality gate" + cmds: + - task: format:check + - task: test + + version: + desc: "Print project version" + silent: true + cmds: + - cmd: cmd /c gradlew.bat printVersion --quiet | tail -1 + platforms: [windows] + - cmd: ./gradlew printVersion --quiet | tail -1 + platforms: [linux, darwin] + + licenses:check: + desc: "Check dependency licenses" + cmds: + - cmd: cmd /c gradlew.bat checkLicense --no-parallel + platforms: [windows] + - cmd: ./gradlew checkLicense --no-parallel + platforms: [linux, darwin] + + licenses:generate: + desc: "Check and generate dependency license report" + cmds: + - cmd: cmd /c gradlew.bat checkLicense generateLicenseReport --no-parallel + platforms: [windows] + - cmd: ./gradlew checkLicense generateLicenseReport --no-parallel + platforms: [linux, darwin] + + clean: + desc: "Clean build artifacts" + cmds: + - cmd: cmd /c gradlew.bat clean + platforms: [windows] + - cmd: ./gradlew clean + platforms: [linux, darwin] diff --git a/.taskfiles/desktop.yml b/.taskfiles/desktop.yml new file mode 100644 index 0000000000..0bf938e806 --- /dev/null +++ b/.taskfiles/desktop.yml @@ -0,0 +1,105 @@ +version: '3' + +vars: + JLINK_MODULES: "java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" + +tasks: + prepare: + desc: "Prepare desktop build dependencies" + deps: [jlink, ":frontend:prepare:desktop", provisioner] + + provisioner: + desc: "Build installer provisioner" + platforms: [windows] + cmds: + - node scripts/build-provisioner.mjs + + dev: + desc: "Start Tauri desktop dev mode" + deps: [prepare] + ignore_error: true + cmds: + - npx tauri dev --no-watch + + build: + desc: "Build Tauri desktop app (production)" + deps: [prepare] + cmds: + - npx tauri build + + build:dev: + desc: "Build Tauri desktop app (dev, no bundling)" + deps: [prepare] + cmds: + - npx tauri build --no-bundle + + build:dev:mac: + desc: "Build Tauri desktop .app bundle (macOS)" + deps: [prepare] + cmds: + - npx tauri build --bundles app + + build:dev:windows: + desc: "Build Tauri desktop NSIS installer (Windows)" + deps: [prepare] + cmds: + - npx tauri build --bundles nsis + + build:dev:linux: + desc: "Build Tauri desktop AppImage (Linux)" + deps: [prepare] + cmds: + - npx tauri build --bundles appimage + + clean: + desc: "Clean Tauri/Cargo build artifacts" + cmds: + - task: jlink:clean + - cd src-tauri && cargo clean + - rm -rf dist build + + # ============================================================ + # JLink — Build bundled Java runtime for Tauri + # ============================================================ + + jlink: + desc: "Build backend JAR and create JLink runtime for Tauri" + deps: [jlink:jar, jlink:runtime] + + jlink:jar: + desc: "Build backend JAR for Tauri bundling" + run: once + dir: .. + env: + DISABLE_ADDITIONAL_FEATURES: "true" + cmds: + - cmd: cmd /c gradlew.bat bootJar --no-daemon + platforms: [windows] + - cmd: ./gradlew bootJar --no-daemon + platforms: [linux, darwin] + - mkdir -p frontend/src-tauri/libs + - cp app/core/build/libs/stirling-pdf-*.jar frontend/src-tauri/libs/ + status: + - test -f frontend/src-tauri/libs/stirling-pdf-*.jar + + jlink:runtime: + desc: "Create custom JRE with jlink" + deps: [jlink:jar] + cmds: + - rm -rf src-tauri/runtime/jre + - mkdir -p src-tauri/runtime + - >- + jlink + --add-modules {{.JLINK_MODULES}} + --strip-debug + --compress=2 + --no-header-files + --no-man-pages + --output src-tauri/runtime/jre + status: + - test -d src-tauri/runtime/jre + + jlink:clean: + desc: "Remove JLink runtime and bundled JARs" + cmds: + - rm -rf src-tauri/libs src-tauri/runtime diff --git a/.taskfiles/docker.yml b/.taskfiles/docker.yml new file mode 100644 index 0000000000..eca54f4a85 --- /dev/null +++ b/.taskfiles/docker.yml @@ -0,0 +1,57 @@ +version: '3' + +vars: + COMPOSE_DIR: docker/compose + EMBEDDED_DIR: docker/embedded + +tasks: + build: + desc: "Build standard Docker image" + cmds: + - docker build -t stirling-pdf -f {{.EMBEDDED_DIR}}/Dockerfile . + + build:fat: + desc: "Build fat Docker image (all features)" + cmds: + - docker build -t stirling-pdf-fat -f {{.EMBEDDED_DIR}}/Dockerfile.fat . + + build:ultra-lite: + desc: "Build ultra-lite Docker image" + cmds: + - docker build -t stirling-pdf-ultra-lite -f {{.EMBEDDED_DIR}}/Dockerfile.ultra-lite . + + build:frontend: + desc: "Build frontend-only Docker image" + cmds: + - docker build -t stirling-pdf-frontend -f docker/frontend/Dockerfile . + + build:engine: + desc: "Build engine Docker image" + dir: engine + cmds: + - docker build -t stirling-pdf-engine . + + up: + desc: "Start standard docker compose stack" + cmds: + - docker compose -f {{.COMPOSE_DIR}}/docker-compose.yml up -d + + up:fat: + desc: "Start fat docker compose stack" + cmds: + - docker compose -f {{.COMPOSE_DIR}}/docker-compose.fat.yml up -d + + up:ultra-lite: + desc: "Start ultra-lite docker compose stack" + cmds: + - docker compose -f {{.COMPOSE_DIR}}/docker-compose.ultra-lite.yml up -d + + down: + desc: "Stop all running docker compose stacks" + cmds: + - docker compose -f {{.COMPOSE_DIR}}/docker-compose.yml down + + logs: + desc: "Tail docker compose logs" + cmds: + - docker compose -f {{.COMPOSE_DIR}}/docker-compose.yml logs -f diff --git a/.taskfiles/engine.yml b/.taskfiles/engine.yml new file mode 100644 index 0000000000..c23ed8d7cf --- /dev/null +++ b/.taskfiles/engine.yml @@ -0,0 +1,124 @@ +version: '3' + +tasks: + install: + desc: "Install engine dependencies" + run: once + cmds: + - uv python install 3.13.8 + - uv sync + sources: + - uv.lock + - pyproject.toml + status: + - test -d .venv + + prep: + desc: "Set up engine .env from template" + deps: [install] + cmds: + - uv run scripts/setup_env.py + sources: + - scripts/setup_env.py + - config/.env.example + generates: + - .env + + run: + desc: "Run engine server" + deps: [prep] + ignore_error: true + dir: src + env: + PYTHONUNBUFFERED: "1" + cmds: + - uv run uvicorn stirling.api.app:app --host 0.0.0.0 --port 5001 + + dev: + desc: "Start engine dev server with hot reload" + deps: [prep] + ignore_error: true + dir: src + env: + PYTHONUNBUFFERED: "1" + cmds: + - uv run uvicorn stirling.api.app:app --host 0.0.0.0 --port 5001 --reload + + lint: + desc: "Run linting" + deps: [install] + cmds: + - uv run ruff check . + + lint:fix: + desc: "Auto-fix lint issues" + deps: [install] + cmds: + - uv run ruff check . --fix + + format: + desc: "Auto-fix code formatting" + deps: [install] + cmds: + - uv run ruff format . + + format:check: + desc: "Check code formatting" + deps: [install] + cmds: + - uv run ruff format . --diff + + typecheck: + desc: "Run type checking" + deps: [install] + cmds: + - uv run pyright . --warnings + + test: + desc: "Run tests" + deps: [prep] + cmds: + - uv run pytest tests + + fix: + desc: "Auto-fix lint + format" + cmds: + - task: lint:fix + - task: format + + check: + desc: "Full engine quality gate" + cmds: + - task: typecheck + - task: lint + - task: format:check + - task: test + + tool-models: + desc: "Generate tool_models.py from frontend TypeScript defs" + deps: [install] + cmds: + - uv run python scripts/generate_tool_models.py --output src/stirling/models/tool_models.py + - task: fix + + clean: + desc: "Clean build artifacts" + cmds: + - task: '{{if eq .OS "Windows_NT"}}clean-windows{{else}}clean-unix{{end}}' + + clean-unix: + internal: true + desc: "Clean build artifacts" + cmds: + - rm -rf .venv data logs output + + # On Windows, use PowerShell as bash failed to delete some dependencies + clean-windows: + internal: true + desc: "Clean build artifacts" + ignore_error: true + cmds: + - powershell rm -Recurse -Force -ErrorAction SilentlyContinue .venv + - powershell rm -Recurse -Force -ErrorAction SilentlyContinue data + - powershell rm -Recurse -Force -ErrorAction SilentlyContinue logs + - powershell rm -Recurse -Force -ErrorAction SilentlyContinue output diff --git a/.taskfiles/frontend.yml b/.taskfiles/frontend.yml new file mode 100644 index 0000000000..4156a34f1c --- /dev/null +++ b/.taskfiles/frontend.yml @@ -0,0 +1,313 @@ +version: '3' + +tasks: + install: + desc: "Install dependencies" + run: once + cmds: + - '{{ if eq .CI "true" }}npm ci{{ else }}npm install{{ end }}' + sources: + - package-lock.json + - package.json + status: + - test -d node_modules + env: + CI: '{{ .CI | default "false" }}' + + prepare:env: + desc: "Generate .env from example if missing" + run: once + deps: [install] + cmds: + - npx tsx scripts/setup-env.ts + sources: + - scripts/setup-env.ts + - config/.env.example + generates: + - .env + + prepare:env:saas: + desc: "Generate .env and .env.saas from examples if missing" + run: once + deps: [install] + cmds: + - npx tsx scripts/setup-env.ts --saas + sources: + - scripts/setup-env.ts + - config/.env.example + - config/.env.saas.example + generates: + - .env + - .env.saas + + prepare:env:desktop: + desc: "Generate .env and .env.desktop from examples if missing" + run: once + deps: [install] + cmds: + - npx tsx scripts/setup-env.ts --desktop + sources: + - scripts/setup-env.ts + - config/.env.example + - config/.env.desktop.example + generates: + - .env + - .env.desktop + + prepare:icons: + desc: "Generate icon bundle from source references" + run: once + deps: [install] + cmds: + - node scripts/generate-icons.js + + prepare: + desc: "Set up dev environment" + run: once + deps: [prepare:env, prepare:icons] + + prepare:saas: + desc: "Prepare for SaaS mode" + run: once + deps: [prepare:env:saas, prepare:icons] + + prepare:desktop: + desc: "Prepare for desktop mode" + run: once + deps: [prepare:env:desktop, prepare:icons] + + # ============================================================ + # Development + # ============================================================ + + dev: + desc: "Start frontend dev server" + deps: [prepare] + ignore_error: true + cmds: + - npx vite + + dev:core: + desc: "Start frontend dev server in core mode" + deps: [prepare] + ignore_error: true + cmds: + - npx vite --mode core + + dev:proprietary: + desc: "Start frontend dev server in proprietary mode" + deps: [prepare] + ignore_error: true + cmds: + - npx vite --mode proprietary + + dev:saas: + desc: "Start frontend dev server in SaaS mode" + deps: [prepare:saas] + ignore_error: true + cmds: + - npx vite --mode saas + + dev:desktop: + desc: "Start frontend dev server in desktop mode" + deps: [prepare:desktop] + ignore_error: true + cmds: + - npx vite --mode desktop + + dev:prototypes: + desc: "Start frontend dev server in prototypes mode" + deps: [prepare] + ignore_error: true + cmds: + - npx vite --mode prototypes + + # ============================================================ + # Build + # ============================================================ + + build: + desc: "Production build (default mode)" + deps: [prepare] + cmds: + - npx vite build + + build:core: + desc: "Build for core mode" + deps: [prepare] + cmds: + - npx vite build --mode core + + build:proprietary: + desc: "Build for proprietary mode" + deps: [prepare] + cmds: + - npx vite build --mode proprietary + + build:saas: + desc: "Build for SaaS mode" + deps: [prepare:saas] + cmds: + - npx vite build --mode saas + + build:desktop: + desc: "Build for desktop mode" + deps: [prepare:desktop] + cmds: + - npx vite build --mode desktop + + build:prototypes: + desc: "Build for prototypes mode" + deps: [prepare] + cmds: + - npx vite build --mode prototypes + + # ============================================================ + # Code quality + # ============================================================ + + lint: + desc: "Run linting" + deps: [install] + cmds: + - npx eslint --max-warnings=0 + - npx dpdm src --circular --no-warning --no-tree --exit-code circular:1 + + lint:fix: + desc: "Auto-fix lint issues" + deps: [install] + cmds: + - npx eslint --fix + + format: + desc: "Auto-fix code formatting" + deps: [install] + cmds: + - npx prettier --write . + + format:check: + desc: "Check code formatting" + deps: [install] + cmds: + - npx prettier --check . + + fix: + desc: "Auto-fix lint and format" + cmds: + - task: format + - task: lint:fix + + typecheck: + desc: "Typecheck default build of the app" + cmds: + - task: typecheck:proprietary + + typecheck:core: + desc: "Typecheck core build variant" + deps: [prepare] + cmds: + - npx tsc --noEmit --project src/core/tsconfig.json + + typecheck:proprietary: + desc: "Typecheck proprietary build variant" + deps: [prepare] + cmds: + - npx tsc --noEmit --project src/proprietary/tsconfig.json + + typecheck:saas: + desc: "Typecheck SaaS build variant" + deps: [prepare:saas] + cmds: + - npx tsc --noEmit --project src/saas/tsconfig.json + + typecheck:desktop: + desc: "Typecheck desktop build variant" + deps: [prepare:desktop] + cmds: + - npx tsc --noEmit --project src/desktop/tsconfig.json + + typecheck:scripts: + desc: "Typecheck scripts" + deps: [prepare] + cmds: + - npx tsc --noEmit --project scripts/tsconfig.json + + typecheck:prototypes: + desc: "Typecheck prototypes build variant" + deps: [prepare] + cmds: + - npx tsc --noEmit --project src/prototypes/tsconfig.json + + typecheck:all: + desc: "Typecheck all build variants" + cmds: + - task: typecheck:core + - task: typecheck:proprietary + - task: typecheck:saas + - task: typecheck:desktop + - task: typecheck:scripts + + # ============================================================ + # Quality Gate + # ============================================================ + + check: + desc: "Quick quality gate for local development" + cmds: + - task: typecheck + - task: lint + - task: format:check + - task: test + + check:all: + desc: "Full CI quality gate" + cmds: + - task: typecheck:all + - task: lint + - task: format:check + - task: build + - task: test + + # ============================================================ + # Test + # ============================================================ + + test: + desc: "Run tests" + deps: [install] + cmds: + - npx vitest run + + test:watch: + desc: "Run tests in watch mode" + deps: [install] + cmds: + - npx vitest --watch + + test:coverage: + desc: "Run tests with coverage" + deps: [install] + cmds: + - npx vitest --coverage + + test:e2e: + desc: "Run E2E tests" + deps: [prepare] + cmds: + - npx playwright test {{.CLI_ARGS}} + + test:e2e:install: + desc: "Install E2E test browsers" + deps: [install] + cmds: + - npx playwright install {{.CLI_ARGS}} --with-deps + + # ============================================================ + # Code Generation + # ============================================================ + + licenses:generate: + desc: "Generate frontend license report" + deps: [install] + cmds: + - node scripts/generate-licenses.js diff --git a/AGENTS.md b/AGENTS.md index 0e46552a21..b97f114dc1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,18 +2,40 @@ This file provides guidance to AI Agents when working with code in this repository. +## Taskfile (Recommended) + +This project uses [Task](https://taskfile.dev/) as a unified command runner. All build, dev, test, lint, and docker commands can be run from the repo root via `task `. Run `task --list` to see all available commands. + +### Quick Reference +- `task install` — install all dependencies +- `task dev` — start backend + frontend concurrently +- `task dev:all` — start backend + frontend + engine concurrently +- `task build` — build all components +- `task test` — run all tests (backend + frontend + engine) +- `task lint` — run all linters +- `task format` — auto-fix formatting across all components +- `task check` — full quality gate (lint + typecheck + test) +- `task clean` — clean all build artifacts +- `task docker:build` — build standard Docker image +- `task docker:up` — start Docker compose stack + ## Common Development Commands ### Build and Test -- **Build project**: `./gradlew clean build` -- **Run locally**: `./gradlew bootRun` -- **Full test suite**: `./test.sh` (builds all Docker variants and runs comprehensive tests) -- **Code formatting**: `./gradlew spotlessApply` (runs automatically before compilation) +- **Build project**: `task build` +- **Run backend locally**: `task backend:dev` +- **Run all tests**: `task test` (or individually: `task backend:test`, `task frontend:test`, `task engine:test`) +- **Docker integration tests**: `./test.sh` (builds all Docker variants and runs comprehensive tests) +- **Code formatting**: `task format` (or `task backend:format` for Java only) +- **Full quality gate**: `task check` (runs lint + typecheck + test across all components) ### Docker Development -- **Build ultra-lite**: `docker build -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .` -- **Build standard**: `docker build -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .` -- **Build fat version**: `docker build -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .` +- **Build standard**: `task docker:build` (or `docker build -t stirling-pdf -f docker/embedded/Dockerfile .`) +- **Build fat version**: `task docker:build:fat` +- **Build ultra-lite**: `task docker:build:ultra-lite` +- **Start compose stack**: `task docker:up` (or `task docker:up:fat`, `task docker:up:ultra-lite`) +- **Stop compose stack**: `task docker:down` +- **View logs**: `task docker:logs` - **Example compose files**: Located in `exampleYmlFiles/` directory ### Security Mode Development @@ -23,20 +45,22 @@ Set `DOCKER_ENABLE_SECURITY=true` environment variable to enable security featur Development for the AI engine happens in the `engine/` folder. The frontend calls the Python via Java as a proxy. - Follow the engine-specific guidance in [engine/AGENTS.md](engine/AGENTS.md) for Python architecture, code style, and AI usage. -- Use Makefile commands for Python work: - - From `engine/`: `make check` to lint, type-check, test, etc. and `make fix` to fix easily fixable linting and formatting issues. -- The project structure is defined in `engine/pyproject.toml`. Any new dependencies should be listed there, followed by running `make install`. +- Use Task commands from the repo root: + - `task engine:check` — lint, type-check, test + - `task engine:fix` — auto-fix linting and formatting + - `task engine:install` — install dependencies +- The project structure is defined in `engine/pyproject.toml`. Any new dependencies should be listed there, followed by running `task engine:install`. ### Frontend Development -- **Frontend dev server**: `cd frontend && npm run dev` (requires backend on localhost:8080) +- **Frontend dev server**: `task frontend:dev` — requires backend on localhost:8080 - **Tech Stack**: Vite + React + TypeScript + Mantine UI + TailwindCSS - **Proxy Configuration**: Vite proxies `/api/*` calls to backend (localhost:8080) - **Build Process**: DO NOT run build scripts manually - builds are handled by CI/CD pipelines -- **Package Installation**: DO NOT run npm install commands - package management handled separately +- **Package Installation**: `task frontend:install` - **Deployment Options**: - - **Desktop App**: `npm run tauri-build` (native desktop application) - - **Web Server**: `npm run build` then serve dist/ folder - - **Development**: `npm run tauri-dev` for desktop dev mode + - **Desktop App**: `task desktop:build` + - **Web Server**: `task frontend:build` then serve dist/ folder + - **Development**: `task desktop:dev` for desktop dev mode #### Environment Variables - All `VITE_*` variables must be declared in the appropriate example file: @@ -44,8 +68,8 @@ Development for the AI engine happens in the `engine/` folder. The frontend call - `frontend/config/.env.saas.example` — SaaS-only vars - `frontend/config/.env.desktop.example` — desktop (Tauri)-only vars - Never use `|| 'hardcoded-fallback'` inline — put defaults in the example files -- `npm run prep` / `prep:saas` / `prep:desktop` auto-create the env files from examples on first run, and error if any required keys are missing -- These prep scripts run automatically at the start of all `dev*`, `build*`, and `tauri*` commands +- `task frontend:prep` / `prep:saas` / `prep:desktop` auto-create the env files from examples on first run, and error if any required keys are missing +- Prep runs automatically as a dependency of all `dev*`, `build*`, and `desktop*` tasks - See `frontend/README.md#environment-variables` for full documentation #### Import Paths - CRITICAL @@ -299,15 +323,17 @@ The frontend is organized with a clear separation of concerns: ## Development Workflow -1. **Local Development**: - - Backend: `./gradlew bootRun` (runs on localhost:8080) - - Frontend: `cd frontend && npm run dev` (runs on localhost:5173, proxies to backend) -2. **Docker Testing**: Use `./test.sh` before submitting PRs -3. **Code Style**: Spotless enforces Google Java Format automatically -4. **Translations**: +1. **Local Development** (using Taskfile): + - Backend + frontend: `task dev` + - All services (including AI engine): `task dev:all` + - Or individually: `task backend:dev` (localhost:8080), `task frontend:dev` (localhost:5173), `task engine:dev` (localhost:5001) +2. **Quality Gate**: Run `task check` before submitting PRs +3. **Docker Testing**: Use `./test.sh` for full Docker integration tests +4. **Code Style**: Spotless enforces Google Java Format automatically (`task backend:format`) +5. **Translations**: - Backend: Use helper scripts in `/scripts` for multi-language updates - Frontend: Update JSON files in `frontend/public/locales/` or use conversion script -5. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html` +6. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html` ## Frontend Architecture Status diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9828c4096..79eb43f645 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,19 @@ Before you start working on an issue, please comment on (or create) the issue an Once you have been assigned an issue, you can start working on it. When you are ready to submit your changes, open a pull request. For a detailed pull request tutorial, see [this guide](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github). +## Development Quick Start + +This project uses [Task](https://taskfile.dev/) as a unified command runner. After cloning: + +1. Install the `task` CLI: https://taskfile.dev/installation/ +2. Run `task install` to install all dependencies +3. Run `task dev` to start backend + frontend +4. Run `task check` before submitting a PR + +Run `task --list` to see all available commands. + +## Pull Request Guidelines + Please make sure your Pull Request adheres to the following guidelines: - Use the PR template provided. @@ -39,9 +52,10 @@ If, at any point in time, you have a question, please feel free to ask in the sa ## Developer Documentation -For technical guides, setup instructions, and development resources, please see our [Developer Documentation](devGuide/) which includes: +For technical guides, setup instructions, and development resources: -- [Developer Guide](devGuide/DeveloperGuide.md) - Main setup and architecture guide +- [Developer Guide](DeveloperGuide.md) - Main setup and architecture guide +- [Taskfile.yml](Taskfile.yml) - Unified task runner for all build/dev/test/lint commands - [Exception Handling Guide](devGuide/EXCEPTION_HANDLING_GUIDE.md) - Error handling patterns and i18n - [Translation Guide](devGuide/HowToAddNewLanguage.md) - Adding new languages - And more in the [devGuide folder](devGuide/) diff --git a/DeveloperGuide.md b/DeveloperGuide.md index 735c1348f9..fdfab20c5e 100644 --- a/DeveloperGuide.md +++ b/DeveloperGuide.md @@ -42,11 +42,13 @@ This guide focuses on developing for Stirling 2.0, including both the React fron ### Prerequisites +- [Task](https://taskfile.dev/installation/) — unified command runner (recommended) - Docker - Git - Java JDK 21 or later (JDK 25 recommended) - Node.js 18+ and npm (required for frontend development) - Gradle 7.0 or later (Included within the repo) +- [uv](https://docs.astral.sh/uv/) — Python package manager (required for engine development) - Rust and Cargo (required for Tauri desktop app development) - Tauri CLI (install with `cargo install tauri-cli`) @@ -82,13 +84,29 @@ For local testing, you should generally be testing the full 'Security' version o 5. **Frontend Setup (Required for Stirling 2.0)** Navigate to the frontend directory and install dependencies using npm. +### Verify Setup + +Run `task install` to install all project dependencies (frontend npm packages, engine Python packages). Gradle manages its own dependencies automatically. Then run `task check` to verify everything builds and passes. + ## 4. Stirling 2.0 Development Workflow +### Using Taskfile (Recommended) + +The fastest way to start developing: + +1. **Start developing**: `task dev` (runs backend + frontend concurrently — Ctrl+C to stop) +2. **Or start services individually** in separate terminals: + - `task backend:dev` — Spring Boot on localhost:8080 + - `task frontend:dev` — Vite on localhost:5173 + - `task engine:dev` — FastAPI on localhost:5001 + +Run `task --list` to see all available commands. + ### Frontend Development (React) The frontend is a React SPA that runs independently during development: -1. **Start the backend**: Run the Spring Boot application (serves API endpoints on localhost:8080) -2. **Start the frontend dev server**: Navigate to the frontend directory and run the development server (serves UI on localhost:5173) +1. **Start the backend**: `task backend:dev` (serves API endpoints on localhost:8080) +2. **Start the frontend dev server**: `task frontend:dev` (serves UI on localhost:5173) 3. **Development flow**: The Vite dev server automatically proxies API calls to the backend ### File Storage Architecture @@ -99,7 +117,10 @@ Stirling 2.0 uses client-side file storage: ### Tauri Desktop App Development Stirling-PDF can be packaged as a cross-platform desktop application using Tauri with PDF file association support and bundled JRE. -See [the frontend README](frontend/README.md#tauri) for build instructions. + +Using Taskfile: `task desktop:dev` (development) or `task desktop:build` (production build). + +See [the frontend README](frontend/README.md#tauri) for detailed build instructions. ## 5. Project Structure @@ -222,6 +243,20 @@ docker-compose -f exampleYmlFiles/docker-compose-latest-security.yml up ### Building Docker Images +#### Using Taskfile (Recommended) + +```bash +task docker:build # standard image +task docker:build:fat # fat image (all features) +task docker:build:ultra-lite # ultra-lite image +task docker:up # start standard compose stack +task docker:up:fat # start fat compose stack +task docker:down # stop all stacks +task docker:logs # tail logs +``` + +#### Manual Docker Builds + Stirling-PDF uses different Docker images for various configurations. The build process is controlled by environment variables and uses specific Dockerfile variants. Here's how to build the Docker images: 1. Set the security environment variable: @@ -230,10 +265,10 @@ Stirling-PDF uses different Docker images for various configurations. The build export DISABLE_ADDITIONAL_FEATURES=true # or false for to enable login and security features for builds ``` -2. Build the project with Gradle: +2. Build the project: ```bash - ./gradlew clean build + task backend:build ``` 3. Build the Docker images: @@ -261,9 +296,18 @@ Note: The `--no-cache` and `--pull` flags ensure that the build process uses the ## 7. Testing +### Quick Testing with Taskfile + +Run all unit/integration tests across all components: + +```bash +task test # run all tests (backend + frontend + engine) +task check # full quality gate: lint + typecheck + test +``` + ### Comprehensive Testing Script -Stirling-PDF provides a `test.sh` script in the root directory. This script builds all versions of Stirling-PDF, checks that each version works, and runs Cucumber tests. It's recommended to run this script before submitting a final pull request. +Stirling-PDF also provides a `test.sh` script in the root directory for Docker integration tests. This script builds all versions of Stirling-PDF, checks that each version works, and runs Cucumber tests. It's recommended to run this script before submitting a final pull request. To run the test script: @@ -289,10 +333,11 @@ Note: The `test.sh` script will run automatically when you raise a PR. However, For React frontend development: -1. Start the backend: Run the Spring Boot application to serve API endpoints on localhost:8080 -2. Start the frontend dev server: Navigate to the frontend directory and run the development server on localhost:5173 +1. Start the backend: `task backend:dev` (serves API endpoints on localhost:8080) +2. Start the frontend dev server: `task frontend:dev` (serves UI on localhost:5173) 3. The Vite dev server automatically proxies API calls to the backend -4. Test React components, UI interactions, and IndexedDB file operations using browser developer tools +4. Run frontend tests: `task frontend:test` (or `task frontend:test:watch` for watch mode) +5. Test React components, UI interactions, and IndexedDB file operations using browser developer tools ### Local Testing (Java and UI Components) @@ -308,7 +353,7 @@ To run Stirling-PDF locally: 1. Compile and run the project using built-in IDE methods or by running: ```bash - ./gradlew bootRun + task backend:dev ``` 2. Access the application at `http://localhost:8080` in your web browser. @@ -329,10 +374,11 @@ Important notes: 2. Create a new branch for your feature or bug fix. 3. Make your changes and commit them with clear, descriptive messages and ensure any documentation is updated related to your changes. 4. Test your changes thoroughly in the Docker environment. -5. Run the `test.sh` script to ensure all versions build correctly and pass the Cucumber tests: +5. Run the quality gate and integration tests: ```bash - ./test.sh + task check # lint + typecheck + test across all components + ./test.sh # Docker integration tests (builds all variants + Cucumber) ``` 6. Push your changes to your fork. diff --git a/README.md b/README.md index d9e0d5a418..9329b20eed 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ For full installation options (including desktop and Kubernetes), see our [Docum We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. -For development setup, see the [Developer Guide](DeveloperGuide.md). +This project uses [Task](https://taskfile.dev/) as a unified command runner for all build, dev, and test commands. Run `task install` to get started, or see the [Developer Guide](DeveloperGuide.md) for full details. For adding translations, see the [Translation Guide](devGuide/HowToAddNewLanguage.md). diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000000..d6138a7ad4 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,128 @@ +version: '3' + +output: prefixed + +includes: + backend: + taskfile: .taskfiles/backend.yml + dir: . + frontend: + taskfile: .taskfiles/frontend.yml + dir: frontend + engine: + taskfile: .taskfiles/engine.yml + dir: engine + docker: + taskfile: .taskfiles/docker.yml + dir: . + desktop: + taskfile: .taskfiles/desktop.yml + dir: frontend + +tasks: + # ============================================================ + # Setup & Prerequisites + # ============================================================ + + install: + desc: "Install all project dependencies" + cmds: + - task: frontend:install + - task: engine:install + + # ============================================================ + # Development + # ============================================================ + + dev: + desc: "Start backend + frontend concurrently" + deps: + - backend:dev + - frontend:dev + + dev:all: + desc: "Start backend + frontend + engine concurrently" + deps: + - backend:dev + - frontend:dev + - engine:dev + + # ============================================================ + # Build + # ============================================================ + + build: + desc: "Build all components" + cmds: + - task: backend:build + - task: frontend:build + + # ============================================================ + # Test + # ============================================================ + + test: + desc: "Run ALL tests (backend + frontend + engine)" + cmds: + - task: backend:test + - task: frontend:test + - task: engine:test + + # ============================================================ + # Lint & Format + # ============================================================ + + lint: + desc: "Run all linters" + cmds: + - task: frontend:lint + - task: engine:lint + + fix: + desc: "Auto-fix all components" + cmds: + - task: backend:fix + - task: frontend:fix + - task: engine:fix + + format: + desc: "Auto-fix formatting across all components" + cmds: + - task: backend:format + - task: frontend:format + - task: engine:format + + format:check: + desc: "Check formatting across all components" + cmds: + - task: backend:format:check + - task: frontend:format:check + - task: engine:format:check + + # ============================================================ + # Quality Gate + # ============================================================ + + check: + desc: "Quick quality gate for local development" + cmds: + - task: backend:check + - task: frontend:check + - task: engine:check + + check:all: + desc: "Full CI quality gate" + cmds: + - task: backend:check + - task: frontend:check:all + - task: engine:check + + # ============================================================ + # Clean + # ============================================================ + + clean: + desc: "Clean all build artifacts" + cmds: + - task: backend:clean + - task: engine:clean diff --git a/app/core/build.gradle b/app/core/build.gradle index d3e7fc4993..277792314e 100644 --- a/app/core/build.gradle +++ b/app/core/build.gradle @@ -243,9 +243,8 @@ tasks.register('npmBuild', Exec) { enabled = buildWithFrontend group = 'frontend' description = 'Build frontend application' - workingDir frontendDir - commandLine = Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c', 'npm', 'run', 'build'] : ['npm', 'run', 'build'] - dependsOn npmInstall + workingDir file('../..') + commandLine = ['task', 'frontend:build'] inputs.dir(new File(frontendDir, 'src')) inputs.dir(new File(frontendDir, 'public')) inputs.file(new File(frontendDir, 'package.json')) diff --git a/devGuide/DeveloperGuide.md b/devGuide/DeveloperGuide.md index 2a16adf979..964992927e 100644 --- a/devGuide/DeveloperGuide.md +++ b/devGuide/DeveloperGuide.md @@ -1,442 +1,5 @@ -# Stirling-PDF Developer Guide +# Developer Guide -## 1. Introduction +This guide has moved to the repository root for easier discovery. -Stirling-PDF is a robust, locally hosted, web-based PDF manipulation tool. This guide focuses on Docker-based development and testing, which is the recommended approach for working with the full version of Stirling-PDF. - -## 2. Project Overview - -Stirling-PDF is built using: - -- Spring Boot -- PDFBox -- LibreOffice -- qpdf -- Calibre (`ebook-convert` CLI) for eBook conversions -- HTML, CSS, JavaScript -- Docker -- PDF.js -- PDF-LIB.js -- Lombok - -## 3. Development Environment Setup - -### Prerequisites - -- Docker -- Git -- Java JDK 21 or later (JDK 25 recommended) -- Gradle 7.0 or later (Included within the repo) - -### Setup Steps - -1. Clone the repository: - - ```bash - git clone https://github.com/Stirling-Tools/Stirling-PDF.git - cd Stirling-PDF - ``` - -2. Install Docker and JDK 21 (or JDK 25 recommended) if not already installed. - -3. Install a recommended Java IDE such as Eclipse, IntelliJ, or VSCode - 1. Only VSCode - 1. Open VS Code. - 2. When prompted, install the recommended extensions. - 3. Alternatively, open the command palette (`Ctrl + Shift + P` or `Cmd + Shift + P` on macOS) and run: - - ```sh - Extensions: Show Recommended Extensions - ``` - - 4. Install the required extensions from the list. - -4. Lombok Setup -Stirling-PDF uses Lombok to reduce boilerplate code. Some IDEs, like Eclipse, don't support Lombok out of the box. To set up Lombok in your development environment: -Visit the [Lombok website](https://projectlombok.org/setup/) for installation instructions specific to your IDE. - -5. Install Calibre CLI (optional but required for eBook conversions) - Ensure the `ebook-convert` binary from Calibre is available on your PATH when working on the - eBook to PDF feature. The Calibre tool group is automatically disabled when the binary is - missing, so having it installed locally allows you to exercise the full workflow. - -6. Add environment variable -For local testing, you should generally be testing the full 'Security' version of Stirling PDF. To do this, you must add the environment flag DISABLE_ADDITIONAL_FEATURES=false to your system and/or IDE build/run step. - -## 4. Project Structure - -```bash -Stirling-PDF/ -├── .github/ # GitHub-specific files (workflows, issue templates) -├── configs/ # Configuration files used by stirling at runtime (generated at runtime) -├── cucumber/ # Cucumber test files -│ ├── features/ -├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files) -├── docs/ # Documentation files -├── exampleYmlFiles/ # Example YAML configuration files -├── images/ # Image assets -├── pipeline/ # Pipeline-related files (generated at runtime) -├── scripts/ # Utility scripts -├── src/ # Source code -│ ├── main/ -│ │ ├── java/ -│ │ │ └── stirling/ -│ │ │ └── software/ -│ │ │ └── SPDF/ -│ │ │ ├── config/ -│ │ │ ├── controller/ -│ │ │ ├── model/ -│ │ │ ├── repository/ -│ │ │ ├── service/ -│ │ │ └── utils/ -│ │ └── resources/ -│ │ ├── static/ -│ │ │ ├── css/ -│ │ │ ├── js/ -│ │ │ └── pdfjs/ -│ └── test/ -│ └── java/ -│ └── stirling/ -│ └── software/ -│ └── SPDF/ -├── build.gradle # Gradle build configuration -├── Dockerfile # Main Dockerfile -├── Dockerfile.ultra-lite # Dockerfile for ultra-lite version -├── Dockerfile.fat # Dockerfile for fat version -├── docker-compose.yml # Docker Compose configuration -└── test.sh # Test script to deploy all docker versions and run cuke tests -``` - -## 5. Docker-based Development - -Stirling-PDF offers several Docker versions: - -- Full: All features included -- Ultra-Lite: Basic PDF operations only -- Fat: Includes additional libraries and fonts predownloaded - -### Example Docker Compose Files - -Stirling-PDF provides several example Docker Compose files in the `exampleYmlFiles` directory, such as: - -- `docker-compose-latest.yml`: Latest version without login and security features -- `docker-compose-latest-security.yml`: Latest version with login and security features enabled -- `docker-compose-latest-fat-security.yml`: Fat version with login and security features enabled - -These files provide pre-configured setups for different scenarios. For example, here's a snippet from `docker-compose-latest-security.yml`: - -```yaml -services: - stirling-pdf: - container_name: Stirling-PDF-Security - image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest - deploy: - resources: - limits: - memory: 4G - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8080$${SYSTEM_ROOTURIPATH:-''}/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"] - interval: 5s - timeout: 10s - retries: 16 - ports: - - "8080:8080" - volumes: - - ./stirling/latest/data:/usr/share/tessdata:rw - - ./stirling/latest/config:/configs:rw - - ./stirling/latest/logs:/logs:rw - environment: - DISABLE_ADDITIONAL_FEATURES: "false" - SECURITY_ENABLELOGIN: "true" - PUID: 1002 - PGID: 1002 - UMASK: "022" - SYSTEM_DEFAULTLOCALE: en-US - UI_APPNAME: Stirling-PDF - UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security - UI_APPNAMENAVBAR: Stirling-PDF Latest - SYSTEM_MAXFILESIZE: "100" - METRICS_ENABLED: "true" - SYSTEM_GOOGLEVISIBILITY: "true" - SHOW_SURVEY: "true" - restart: on-failure:5 -``` - -To use these example files, copy the desired file to your project root and rename it to `docker-compose.yml`, or specify the file explicitly when running Docker Compose: - -```bash -docker-compose -f exampleYmlFiles/docker-compose-latest-security.yml up -``` - -### Building Docker Images - -Stirling-PDF uses different Docker images for various configurations. The build process is controlled by environment variables and uses specific Dockerfile variants. Here's how to build the Docker images: - -1. Set the security environment variable: - - ```bash - export DISABLE_ADDITIONAL_FEATURES=true # or false for to enable login and security features for builds - ``` - -2. Build the project with Gradle: - - ```bash - ./gradlew clean build - ``` - -3. Build the Docker images: - - For the latest version: - - ```bash - docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile . - ``` - - For the ultra-lite version: - - ```bash - docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite . - ``` - - For the fat version (with login and security features enabled): - - ```bash - export DISABLE_ADDITIONAL_FEATURES=false - docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat . - ``` - -Note: The `--no-cache` and `--pull` flags ensure that the build process uses the latest base images and doesn't use cached layers, which is useful for testing and ensuring reproducible builds. however to improve build times these can often be removed depending on your usecase - -## 6. Testing - -### Comprehensive Testing Script - -Stirling-PDF provides a `test.sh` script in the root directory. This script builds all versions of Stirling-PDF, checks that each version works, and runs Cucumber tests. It's recommended to run this script before submitting a final pull request. - -To run the test script: - -```bash -./test.sh -``` - -This script performs the following actions: - -1. Builds all Docker images (full, ultra-lite, fat). -2. Runs each version to ensure it starts correctly. -3. Executes Cucumber tests against the main version and ensures feature compatibility. In the event these tests fail, your PR will not be merged. - -Note: The `test.sh` script will run automatically when you raise a PR. However, it's recommended to run it locally first to save resources and catch any issues early. - -### Full Testing with Docker - -1. Build and run the Docker container per the above instructions: - -2. Access the application at `http://localhost:8080` and manually test all features developed. - -### Local Testing (Java and UI Components) - -For quick iterations and development of Java backend, JavaScript, and UI components, you can run and test Stirling-PDF locally without Docker. This approach allows you to work on and verify changes to: - -- Java backend logic -- RESTful API endpoints -- JavaScript functionality -- User interface components and styling - -To run Stirling-PDF locally: - -1. Compile and run the project using built-in IDE methods or by running: - - ```bash - ./gradlew bootRun - ``` - -2. Access the application at `http://localhost:8080` in your web browser. - -3. Manually test the features you're working on through the UI. - -4. For API changes, use tools like Postman or curl to test endpoints directly. - -Important notes: - -- Local testing doesn't include features that depend on external tools like qpdf, LibreOffice, or Python scripts. -- There are currently no automated unit tests. All testing is done manually through the UI or API calls. (You are welcome to add JUnits!) -- Always verify your changes in the full Docker environment before submitting pull requests, as some integrations and features will only work in the complete setup. - -## 7. Contributing - -1. Fork the repository on GitHub. -2. Create a new branch for your feature or bug fix. -3. Make your changes and commit them with clear, descriptive messages and ensure any documentation is updated related to your changes. -4. Test your changes thoroughly in the Docker environment. -5. Run the `test.sh` script to ensure all versions build correctly and pass the Cucumber tests: - - ```bash - ./test.sh - ``` - -6. Push your changes to your fork. -7. Submit a pull request to the main repository. -8. See additional [contributing guidelines](../CONTRIBUTING.md). - -When you raise a PR: - -- The `test.sh` script will run automatically against your PR. -- The PR checks will verify versioning and dependency updates. -- Documentation will be automatically updated for dependency changes. -- Security issues will be checked using Snyk and PixeeBot. - -Address any issues that arise from these checks before finalizing your pull request. - -## 8. API Documentation - -API documentation is available at `/swagger-ui/index.html` when running the application. You can also view the latest API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/). - -## 9. Customization - -Stirling-PDF can be customized through environment variables or a `settings.yml` file. Key customization options include: - -- Application name and branding -- Security settings -- UI customization -- Endpoint management -- Maximum DPI for PDF to image conversion (`system.maxDPI`) - -When using Docker, pass environment variables using the `-e` flag or in your `docker-compose.yml` file. - -Example: - -```bash -docker run -p 8080:8080 -e APP_NAME="My PDF Tool" stirling-pdf:full -``` - -Refer to the main README for a full list of customization options. - -## 10. Language Translations - -For managing language translations that affect multiple files, Stirling-PDF provides a helper script: - -```bash -/scripts/replace_translation_line.sh -``` - -This script helps you make consistent replacements across language files. - -When contributing translations: - -1. Use the helper script for multi-file changes. -2. Ensure all language files are updated consistently. -3. The PR checks will verify consistency in language file updates. - -Remember to test your changes thoroughly to ensure they don't break any existing functionality. - -## Code examples - -### Adding a New Feature to the Backend (API) - -1. **Create a New Controller:** - - Create a new Java class in the `app/core/src/main/java/stirling/software/SPDF/controller/api` directory. - - Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint. - - Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`. - - ```java - package stirling.software.SPDF.controller.api; - - import org.springframework.web.bind.annotation.GetMapping; - import org.springframework.web.bind.annotation.RequestMapping; - import org.springframework.web.bind.annotation.RestController; - import io.swagger.v3.oas.annotations.Operation; - import io.swagger.v3.oas.annotations.tags.Tag; - - @RestController - @RequestMapping("/api/v1/new-feature") - @Tag(name = "General", description = "General APIs") - public class NewFeatureController { - - @GetMapping - @Operation(summary = "New Feature", description = "This is a new feature endpoint.") - public String newFeature() { - return "NewFeatureResponse"; - } - } - ``` - -2. **Define the Service Layer:** (Not required but often useful) - - Create a new service class in the `app/core/src/main/java/stirling/software/SPDF/service` directory. - - Implement the business logic for the new feature. - - ```java - package stirling.software.SPDF.service; - - import org.springframework.stereotype.Service; - - @Service - public class NewFeatureService { - - public String getNewFeatureData() { - // Implement business logic here - return "New Feature Data"; - } - } - ``` - -2b. **Integrate the Service with the Controller:** - -- Autowire the service class in the controller and use it to handle the API request. - - ```java - package stirling.software.SPDF.controller.api; - - import org.springframework.beans.factory.annotation.Autowired; - import org.springframework.web.bind.annotation.GetMapping; - import org.springframework.web.bind.annotation.RequestMapping; - import org.springframework.web.bind.annotation.RestController; - import stirling.software.SPDF.service.NewFeatureService; - import io.swagger.v3.oas.annotations.Operation; - import io.swagger.v3.oas.annotations.tags.Tag; - - @RestController - @RequestMapping("/api/v1/new-feature") - @Tag(name = "General", description = "General APIs") - public class NewFeatureController { - - @Autowired - private NewFeatureService newFeatureService; - - @GetMapping - @Operation(summary = "New Feature", description = "This is a new feature endpoint.") - public String newFeature() { - return newFeatureService.getNewFeatureData(); - } - } - ``` - -## Adding New Translations to Existing Language Files in Stirling-PDF - -When adding a new feature or modifying existing ones in Stirling-PDF, you'll need to add new translation entries to the existing language files. Here's a step-by-step guide: - -### 1. Locate Existing Language Files - -Find the existing `messages.properties` files in the `app/core/src/main/resources` directory. You'll see files like: - -- `messages.properties` (default, usually English) -- `messages_en_GB.properties` -- `messages_fr_FR.properties` -- `messages_de_DE.properties` -- etc. - -### 2. Add New Translation Entries - -Open each of these files and add your new translation entries. For example, if you're adding a new feature called "PDF Splitter", -Use descriptive, hierarchical keys (e.g., `feature.element.description`) -you might add: - -```properties -pdfSplitter.title=PDF Splitter -pdfSplitter.description=Split your PDF into multiple documents -pdfSplitter.button.split=Split PDF -pdfSplitter.input.pages=Enter page numbers to split -``` - -Add these entries to the default GB language file and any others you wish, translating the values as appropriate for each language. - -Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization. +**See [DeveloperGuide.md](../DeveloperGuide.md) for the current developer guide.** diff --git a/devGuide/README.md b/devGuide/README.md index f58f6e5d19..5c8f60577b 100644 --- a/devGuide/README.md +++ b/devGuide/README.md @@ -5,7 +5,8 @@ This directory contains all development-related documentation for Stirling PDF. ## 📚 Documentation Index ### Core Development -- **[DeveloperGuide.md](./DeveloperGuide.md)** - Main developer setup and architecture guide +- **[DeveloperGuide.md](../DeveloperGuide.md)** - Main developer setup and architecture guide (in repo root) +- **[Taskfile.yml](../Taskfile.yml)** - Unified task runner for all build/dev/test/lint commands - **[EXCEPTION_HANDLING_GUIDE.md](./EXCEPTION_HANDLING_GUIDE.md)** - Exception handling patterns and i18n best practices - **[HowToAddNewLanguage.md](./HowToAddNewLanguage.md)** - Internationalization and translation guide diff --git a/docker/README.md b/docker/README.md index 99b86b53ea..d0a1535fff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -2,6 +2,23 @@ This directory contains the organized Docker configurations for the split frontend/backend architecture. +## Using Taskfile (Recommended) + +All Docker commands can be run from the project root using [Task](https://taskfile.dev/): + +```bash +task docker:build # Build standard image +task docker:build:fat # Build fat image (all features) +task docker:build:ultra-lite # Build ultra-lite image +task docker:build:frontend # Build frontend-only image +task docker:build:engine # Build engine image +task docker:up # Start standard compose stack +task docker:up:fat # Start fat compose stack +task docker:up:ultra-lite # Start ultra-lite compose stack +task docker:down # Stop all running stacks +task docker:logs # Tail compose logs +``` + ## Directory Structure ``` diff --git a/docker/embedded/Dockerfile b/docker/embedded/Dockerfile index a0f435c15c..b9da6f7a22 100644 --- a/docker/embedded/Dockerfile +++ b/docker/embedded/Dockerfile @@ -7,11 +7,16 @@ ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION} # Stage 1: Build the Java application and frontend FROM gradle:9.3.1-jdk25 AS app-build +ARG TASK_VERSION=3.49.1 RUN apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates \ && update-ca-certificates \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ + && ARCH=$(dpkg --print-architecture) \ + && curl -fsSL "https://github.com/go-task/task/releases/download/v${TASK_VERSION}/task_${TASK_VERSION}_linux_${ARCH}.deb" -o /tmp/task.deb \ + && dpkg -i /tmp/task.deb \ + && rm /tmp/task.deb \ && rm -rf /var/lib/apt/lists/* # JDK 25+: --add-exports is no longer accepted via JAVA_TOOL_OPTIONS; use JDK_JAVA_OPTIONS instead diff --git a/docker/embedded/Dockerfile.fat b/docker/embedded/Dockerfile.fat index 6c739e61ea..81c10943f6 100644 --- a/docker/embedded/Dockerfile.fat +++ b/docker/embedded/Dockerfile.fat @@ -8,11 +8,16 @@ ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION} # Stage 1: Build the Java application and frontend FROM gradle:9.3.1-jdk25 AS app-build +ARG TASK_VERSION=3.49.1 RUN apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates \ && update-ca-certificates \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ + && ARCH=$(dpkg --print-architecture) \ + && curl -fsSL "https://github.com/go-task/task/releases/download/v${TASK_VERSION}/task_${TASK_VERSION}_linux_${ARCH}.deb" -o /tmp/task.deb \ + && dpkg -i /tmp/task.deb \ + && rm /tmp/task.deb \ && rm -rf /var/lib/apt/lists/* # JDK 25+: --add-exports is no longer accepted via JAVA_TOOL_OPTIONS; use JDK_JAVA_OPTIONS instead diff --git a/docker/embedded/Dockerfile.ultra-lite b/docker/embedded/Dockerfile.ultra-lite index fb2e4bae56..b748dbab82 100644 --- a/docker/embedded/Dockerfile.ultra-lite +++ b/docker/embedded/Dockerfile.ultra-lite @@ -5,12 +5,17 @@ FROM gradle:9.3.1-jdk25 AS build # Install Node.js and npm for frontend build +ARG TASK_VERSION=3.49.1 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ && npm --version \ && node --version \ + && ARCH=$(dpkg --print-architecture) \ + && curl -fsSL "https://github.com/go-task/task/releases/download/v${TASK_VERSION}/task_${TASK_VERSION}_linux_${ARCH}.deb" -o /tmp/task.deb \ + && dpkg -i /tmp/task.deb \ + && rm /tmp/task.deb \ && rm -rf /var/lib/apt/lists/* WORKDIR /app diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index af80bee067..5fef0ea3e4 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -13,7 +13,7 @@ RUN npm ci COPY frontend . # Build the application -RUN npm run build +RUN npx vite build # Production stage FROM nginx:alpine@sha256:b0f7830b6bfaa1258f45d94c240ab668ced1b3651c8a222aefe6683447c7bf55 diff --git a/engine/AGENTS.md b/engine/AGENTS.md index 87fa13db26..6fcef4c249 100644 --- a/engine/AGENTS.md +++ b/engine/AGENTS.md @@ -4,9 +4,23 @@ This file is for AI agents working in `engine/`. The engine is a Python reasoning service for Stirling. It plans and interprets work, but it does not own durable state, and it does not execute Stirling PDF operations directly. Keep the service narrow: typed contracts in, typed contracts out, with AI only where it adds reasoning value. +## Commands + +All engine commands can be run from the repository root using Task: + +- `task engine:check` — run all checks (typecheck + lint + format-check + test) +- `task engine:fix` — auto-fix lint + formatting +- `task engine:install` — install Python dependencies via uv +- `task engine:dev` — start FastAPI with hot reload (localhost:5001) +- `task engine:test` — run pytest +- `task engine:lint` — run ruff linting +- `task engine:typecheck` — run pyright +- `task engine:format` — format code with ruff +- `task engine:tool-models` — generate tool_models.py from frontend TypeScript defs + ## Code Style -- Keep `make check` passing. +- Keep `task engine:check` passing. - Use modern Python when it improves clarity. - Prefer explicit names to cleverness. - Avoid nested functions and nested classes unless the language construct requires them. diff --git a/engine/Makefile b/engine/Makefile deleted file mode 100644 index 71d637694e..0000000000 --- a/engine/Makefile +++ /dev/null @@ -1,82 +0,0 @@ -.PHONY: help install prep check fix lint lint-fix lint-fix-unsafe format format-check typecheck test run run-dev clean tool-models docker-build docker-run - -REPO_ROOT_DIR = .. -ROOT_DIR = . -VENV_DIR = $(ROOT_DIR)/.venv -DEPS_STAMP := $(VENV_DIR)/.deps-installed -TOOL_MODELS := $(CURDIR)/src/stirling/models/tool_models.py -FRONTEND_DIR := $(REPO_ROOT_DIR)/frontend -FRONTEND_TSX := $(FRONTEND_DIR)/node_modules/.bin/tsx - -$(DEPS_STAMP): $(ROOT_DIR)/uv.lock $(ROOT_DIR)/pyproject.toml - uv python install 3.13.8 - uv sync - touch $(DEPS_STAMP) - -help: - @echo "Engine commands:" - @echo " make install - Install production dependencies" - @echo " make prep - Set up .env file from .env.example" - @echo " make lint - Run linting checks" - @echo " make format - Format code" - @echo " make format-check - Show whether code is correctly formatted" - @echo " make typecheck - Run type checking" - @echo " make test - Run tests" - @echo " make run - Run the FastAPI backend with uvicorn" - @echo " make run-dev - Run the FastAPI backend with reload" - @echo " make tool-models - Generate src/stirling/models/tool_models.py from frontend TypeScript tool defs" - @echo " make clean - Clean up generated files" - @echo " make docker-build - Build Docker image" - @echo " make docker-run - Run Docker container" - -install: $(DEPS_STAMP) - -prep: install - uv run scripts/setup_env.py - -check: typecheck lint format-check test - -fix: lint-fix format - -lint: install - uv run ruff check . - -lint-fix: install - uv run ruff check . --fix - -lint-fix-unsafe: install - uv run ruff check . --fix --unsafe-fixes - -format: install - uv run ruff format . - -format-check: install - uv run ruff format . --diff - -typecheck: install - uv run pyright . --warnings - -test: prep - uv run pytest tests - -run: prep - cd src && PYTHONUNBUFFERED=1 uv run uvicorn stirling.api.app:app --host 0.0.0.0 --port 5001 - -run-dev: prep - cd src && PYTHONUNBUFFERED=1 uv run uvicorn stirling.api.app:app --host 0.0.0.0 --port 5001 --reload - -frontend-deps: - if [ ! -x "$(FRONTEND_TSX)" ]; then cd $(FRONTEND_DIR) && npm install; fi - -tool-models: install frontend-deps - uv run python scripts/generate_tool_models.py --output $(TOOL_MODELS) - $(MAKE) fix - -clean: - rm -rf $(VENV_DIR) data logs output - -docker-build: - docker build -t stirling-pdf-engine . - -docker-run: - docker run -p 5001:5001 stirling-pdf-engine diff --git a/frontend/README.md b/frontend/README.md index e3bd1887b0..a8802b24ef 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,145 +1,79 @@ # Frontend + +All frontend commands are run from the repository root using [Task](https://taskfile.dev/): + +- `task frontend:dev` — start Vite dev server (localhost:5173) +- `task frontend:build` — production build +- `task frontend:test` — run tests +- `task frontend:test:watch` — run tests in watch mode +- `task frontend:lint` — run ESLint + cycle detection +- `task frontend:typecheck` — run TypeScript type checking +- `task frontend:check` — run typecheck + lint + test +- `task frontend:install` — install npm dependencies + +For desktop app development, see the [Tauri](#tauri) section below. + ## Environment Variables -The frontend requires environment variables to be set before running. `npm run dev` will create a `.env` file for you automatically on first run using the defaults from `config/.env.example` - for most development work this is all you need. +The frontend requires environment variables to be set before running. `task frontend:dev` will create a `.env` file for you automatically on first run using the defaults from `config/.env.example` - for most development work this is all you need. If you need to configure specific services (Google Drive, Supabase, Stripe, PostHog), edit your local `.env` file. The values in `config/.env.example` show what each variable does and provides sensible defaults where applicable. -For desktop (Tauri) development, `npm run tauri-dev` will additionally create a `.env.desktop` file from `config/.env.desktop.example`. +For desktop (Tauri) development, `task desktop:dev` will additionally create a `.env.desktop` file from `config/.env.desktop.example`. ## Docker Setup For Docker deployments and configuration, see the [Docker README](../docker/README.md). -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. - -The page will reload when you make changes.\ -You may also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can't go back!** - -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. - -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) - -### Analyzing the Bundle Size - -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) - -### Making a Progressive Web App - -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) - -### Advanced Configuration - -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) - -### Deployment - -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) - -### `npm run build` fails to minify - -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) - - ## Tauri -In order to run Tauri, you first have to build the Java backend for Tauri to use. -**macOS/Linux:** - -From the root of the repo, run: - -```bash -./gradlew clean build -./scripts/build-tauri-jlink.sh -``` - -**Windows** - -From the root of the repo, run: - -```batch -gradlew clean build -scripts\build-tauri-jlink.bat -``` - -### Testing the Bundled Runtime - -Before building the full Tauri app, you can test the bundled runtime: - -**macOS/Linux:** -```bash -./frontend/src-tauri/runtime/launch-stirling.sh -``` - -**Windows:** -```cmd -frontend\src-tauri\runtime\launch-stirling.bat -``` - -This will start Stirling-PDF using the bundled JRE, accessible at http://localhost:8080 +All desktop tasks are available via [Task](https://taskfile.dev). From the root of the repo: ### Dev -To run Tauri in development. Use the command in the `frontend` folder: ```bash -npm run tauri-dev +task desktop:dev ``` -This will run the gradle runboot command and the tauri dev command concurrently, starting the app once both are stable. - -> [!NOTE] -> -> Desktop builds require additional environment variables. See [Environment Variables](#environment-variables) -> above - `npm run tauri-dev` will set these up automatically from `config/.env.desktop.example` on first run. +This ensures the JLink runtime and backend JAR exist (skipping if already built), then starts Tauri in dev mode. ### Build -To build a deployment of the Tauri app. Use this command in the `frontend` folder: ```bash -npm run tauri-build +task desktop:build ``` -This will bundle the backend and frontend into one executable for each target. Targets can be set within the `tauri.conf.json` file. +This does a full clean rebuild of the backend JAR and JLink runtime, then builds the Tauri app for production. + +Platform-specific dev builds are also available: + +```bash +task desktop:build:dev # No bundling +task desktop:build:dev:mac # macOS .app bundle +task desktop:build:dev:windows # Windows NSIS installer +task desktop:build:dev:linux # Linux AppImage +``` + +### JLink Tasks + +You can also run JLink steps individually: + +```bash +task desktop:jlink # Build JAR + create JLink runtime +task desktop:jlink:jar # Build backend JAR only +task desktop:jlink:runtime # Create JLink custom JRE only +task desktop:jlink:clean # Remove JLink artifacts +``` + +### Clean + +```bash +task desktop:clean +``` + +Removes all desktop build artifacts including JLink runtime, bundled JARs, Cargo build, and dist/build directories. > [!NOTE] > > Desktop builds require additional environment variables. See [Environment Variables](#environment-variables) -> above - `npm run tauri-build` will set these up automatically from `config/.env.desktop.example` on first run. +> above - `task desktop:dev` will set these up automatically from `config/.env.desktop.example` on first run. diff --git a/frontend/package.json b/frontend/package.json index 167fd7181e..511164b53f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -79,58 +79,6 @@ "web-vitals": "^5.1.0" }, "scripts": { - "prep": "tsx scripts/setup-env.ts && npm run generate-icons", - "prep:saas": "tsx scripts/setup-env.ts --saas && npm run generate-icons", - "prep:desktop": "tsx scripts/setup-env.ts --desktop && npm run generate-icons", - "prep:desktop-build": "node scripts/build-provisioner.mjs && npm run prep:desktop", - "dev": "npm run prep && vite", - "dev:core": "npm run prep && vite --mode core", - "dev:proprietary": "npm run prep && vite --mode proprietary", - "dev:saas": "npm run prep:saas && vite --mode saas", - "dev:desktop": "npm run prep:desktop && vite --mode desktop", - "dev:prototypes": "npm run prep && vite --mode prototypes", - "fix": "npm run format && npm run lint:fix", - "format": "prettier --write .", - "format:check": "prettier --check .", - "lint": "npm run lint:eslint && npm run lint:cycles", - "lint:eslint": "eslint --max-warnings=0", - "lint:fix": "eslint --fix", - "lint:cycles": "dpdm src --circular --no-warning --no-tree --exit-code circular:1", - "build": "npm run prep && vite build", - "build:core": "npm run prep && vite build --mode core", - "build:proprietary": "npm run prep && vite build --mode proprietary", - "build:saas": "npm run prep:saas && vite build --mode saas", - "build:desktop": "npm run prep:desktop && vite build --mode desktop", - "build:prototypes": "npm run prep && vite build --mode prototypes", - "preview": "vite preview", - "tauri-dev": "npm run prep:desktop && tauri dev --no-watch", - "tauri-build": "npm run prep:desktop-build && tauri build", - "_tauri-build-dev": "npm run prep:desktop && tauri build", - "tauri-build-dev": "npm run _tauri-build-dev -- --no-bundle", - "tauri-build-dev-mac": "npm run _tauri-build-dev -- --bundles app", - "tauri-build-dev-windows": "npm run _tauri-build-dev -- --bundles nsis", - "tauri-build-dev-linux": "npm run _tauri-build-dev -- --bundles appimage", - "tauri-clean": "cd src-tauri && cargo clean && cd .. && rm -rf dist build", - "typecheck": "npm run typecheck:proprietary", - "typecheck:core": "tsc --noEmit --project src/core/tsconfig.json", - "typecheck:proprietary": "tsc --noEmit --project src/proprietary/tsconfig.json", - "typecheck:saas": "tsc --noEmit --project src/saas/tsconfig.json", - "typecheck:desktop": "tsc --noEmit --project src/desktop/tsconfig.json", - "typecheck:prototypes": "tsc --noEmit --project src/prototypes/tsconfig.json", - "typecheck:scripts": "tsc --noEmit --project scripts/tsconfig.json", - "typecheck:all": "npm run typecheck:core && npm run typecheck:proprietary && npm run typecheck:saas && npm run typecheck:desktop && npm run typecheck:prototypes && npm run typecheck:scripts", - "check": "npm run typecheck && npm run lint && npm run test:run", - "generate-licenses": "node scripts/generate-licenses.js", - "generate-icons": "node scripts/generate-icons.js", - "generate-icons:verbose": "node scripts/generate-icons.js --verbose", - "generate-sample-pdf": "node scripts/sample-pdf/generate.mjs", - "test": "vitest", - "test:run": "vitest run", - "test:watch": "vitest --watch", - "test:coverage": "vitest --coverage", - "test:e2e": "playwright test", - "test:e2e:ui": "playwright test --ui", - "test:e2e:install": "playwright install", "update:minor": "npm outdated || npm update --before=$(date -v-7d +%Y-%m-%d) && (npm audit fix --before=$(date -v-7d +%Y-%m-%d) || true) && npm test", "update:major": "npx npm-check-updates -u && npm install", "update:interactive": "npx npm-check-updates -i", diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index deb6a63977..58590adebf 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -68,7 +68,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { - command: "npm run dev", + command: "npx vite", url: "http://localhost:5173", reuseExistingServer: !process.env.CI, }, diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index 536d0d382d..1f333a0413 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -6,8 +6,8 @@ "build": { "frontendDist": "../dist", "devUrl": "http://localhost:5173", - "beforeDevCommand": "npm run dev -- --mode desktop", - "beforeBuildCommand": "node scripts/build-provisioner.mjs && npm run build -- --mode desktop" + "beforeDevCommand": "npx vite --mode desktop", + "beforeBuildCommand": "npx vite build --mode desktop" }, "app": { "windows": [ diff --git a/scripts/build-tauri-jlink.bat b/scripts/build-tauri-jlink.bat deleted file mode 100644 index be9cd00d72..0000000000 --- a/scripts/build-tauri-jlink.bat +++ /dev/null @@ -1,176 +0,0 @@ -@echo off -REM Build script for Tauri with JLink runtime bundling -REM This script creates a self-contained Java runtime for Stirling-PDF - -echo 🔧 Building Stirling-PDF with JLink runtime for Tauri... - -echo ▶ Checking Java environment... -java -version >nul 2>&1 -if errorlevel 1 ( - echo ❌ Java is not installed or not in PATH - exit /b 1 -) - -jlink --version >nul 2>&1 -if errorlevel 1 ( - echo ❌ jlink is not available. Please ensure you have a JDK ^(not just JRE^) installed. - exit /b 1 -) - -echo ▶ Checking Java version... -set "JAVA_VERSION_STRING=" -for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( - set "JAVA_VERSION_STRING=%%g" -) -if not defined JAVA_VERSION_STRING ( - echo ❌ Unable to capture Java version string from "java -version" - exit /b 1 -) -set "JAVA_VERSION_STRING=%JAVA_VERSION_STRING:"=%" -set "JAVA_MAJOR_VERSION=" -set "JAVA_MINOR_VERSION=0" -set "JAVA_EFFECTIVE_MAJOR=" -for /f "tokens=1,2 delims=." %%a in ("%JAVA_VERSION_STRING%") do ( - set "JAVA_MAJOR_VERSION=%%a" - set "JAVA_MINOR_VERSION=%%b" - if "%%a"=="1" ( - set "JAVA_EFFECTIVE_MAJOR=%%b" - ) else ( - set "JAVA_EFFECTIVE_MAJOR=%%a" - ) -) -if not defined JAVA_MAJOR_VERSION ( - echo ❌ Unable to determine Java major version from "%JAVA_VERSION_STRING%" - exit /b 1 -) -if not defined JAVA_EFFECTIVE_MAJOR ( - echo ❌ Unable to determine an effective Java major version from "%JAVA_VERSION_STRING%" - exit /b 1 -) -for /f "tokens=1 delims=.-" %%c in ("%JAVA_EFFECTIVE_MAJOR%") do set "JAVA_EFFECTIVE_MAJOR=%%c" -set /a "JAVA_EFFECTIVE_MAJOR_NUM=%JAVA_EFFECTIVE_MAJOR%" >nul 2>&1 -if errorlevel 1 ( - echo ❌ Java major version "%JAVA_EFFECTIVE_MAJOR%" could not be parsed as an integer. Detected string: "%JAVA_VERSION_STRING%" - exit /b 1 -) -set "JAVA_EFFECTIVE_MAJOR=%JAVA_EFFECTIVE_MAJOR_NUM%" -if %JAVA_EFFECTIVE_MAJOR% LSS 21 ( - echo ❌ Java 21 or higher is required. Found Java %JAVA_EFFECTIVE_MAJOR% - exit /b 1 -) -echo ✅ Java %JAVA_EFFECTIVE_MAJOR% and jlink detected - -echo ▶ Building Stirling-PDF JAR... - -set DISABLE_ADDITIONAL_FEATURES=true -call gradlew.bat clean bootJar --no-daemon -if errorlevel 1 ( - echo ❌ Failed to build Stirling-PDF JAR - exit /b 1 -) - -REM Find the built JAR(s) -echo ▶ Listing all built JAR files in app\core\build\libs: -dir /b app\core\build\libs\stirling-pdf-*.jar -for %%f in (app\core\build\libs\stirling-pdf-*.jar) do set STIRLING_JAR=%%f -if not exist "%STIRLING_JAR%" ( - echo ❌ No Stirling-PDF JAR found in build/libs/ - exit /b 1 -) - -echo ✅ Built JAR: %STIRLING_JAR% - -echo ▶ Creating Tauri directories... -if not exist "frontend\src-tauri\libs" mkdir "frontend\src-tauri\libs" -if not exist "frontend\src-tauri\runtime" mkdir "frontend\src-tauri\runtime" - -echo ▶ Copying JAR to Tauri libs directory... -copy "%STIRLING_JAR%" "frontend\src-tauri\libs\" -echo ✅ JAR copied to frontend\src-tauri\libs\ - -REM Log out all JAR files now in the Tauri libs directory -echo ▶ Listing all JAR files in frontend\src-tauri\libs after copy: -dir /b frontend\src-tauri\libs\stirling-pdf-*.jar - -echo ▶ Creating custom JRE with jlink... -if exist "frontend\src-tauri\runtime\jre" rmdir /s /q "frontend\src-tauri\runtime\jre" - -REM Use predefined module list for Windows (jdeps may not be available) -set MODULES=java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported - -echo ▶ Creating JLink runtime with modules: %MODULES% - -jlink ^ - --add-modules %MODULES% ^ - --strip-debug ^ - --compress=2 ^ - --no-header-files ^ - --no-man-pages ^ - --output "frontend\src-tauri\runtime\jre" - -if not exist "frontend\src-tauri\runtime\jre" ( - echo ❌ Failed to create JLink runtime - exit /b 1 -) - -echo ✅ JLink runtime created at frontend\src-tauri\runtime\jre - -echo ▶ Creating launcher scripts for testing... - -REM Create Windows launcher script -echo @echo off > "frontend\src-tauri\runtime\launch-stirling.bat" -echo REM Launcher script for Stirling-PDF with bundled JRE >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo. >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo set SCRIPT_DIR=%%~dp0 >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo set JRE_DIR=%%SCRIPT_DIR%%jre >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo set LIBS_DIR=%%SCRIPT_DIR%%..\libs >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo. >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo REM Find the Stirling-PDF JAR >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo for %%%%f in ("%%LIBS_DIR%%\Stirling-PDF-*.jar") do set STIRLING_JAR=%%%%f >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo. >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo if not exist "%%STIRLING_JAR%%" ^( >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo echo ❌ Stirling-PDF JAR not found in %%LIBS_DIR%% >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo exit /b 1 >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo ^) >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo. >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo REM Launch with bundled JRE >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo "%%JRE_DIR%%\bin\java.exe" ^^ >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo -Xmx2g ^^ >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo -DBROWSER_OPEN=true ^^ >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo -jar "%%STIRLING_JAR%%" ^^ >> "frontend\src-tauri\runtime\launch-stirling.bat" -echo %%* >> "frontend\src-tauri\runtime\launch-stirling.bat" - -echo ✅ Created launcher scripts for testing - -echo ▶ Testing bundled JRE... -"frontend\src-tauri\runtime\jre\bin\java.exe" --version >nul 2>&1 -if errorlevel 1 ( - echo ❌ Bundled JRE test failed - exit /b 1 -) else ( - echo ✅ Bundled JRE works correctly -) - -echo. -echo ✅ 🎉 JLink build setup completed successfully! -echo. -echo 📊 Summary: -echo • JAR: %STIRLING_JAR% -echo • Runtime: frontend\src-tauri\runtime\jre -echo • Modules: %MODULES% -echo. -echo 📋 Next steps: -echo 1. cd frontend -echo 2. npm run tauri-build -echo. -echo 💡 Testing: -echo • Test bundled runtime: frontend\src-tauri\runtime\launch-stirling.bat -echo • Tauri configuration already updated to include bundled JRE -echo. -echo 💡 Benefits: -echo • No external JRE dependency -echo • Smaller distribution size with custom runtime -echo • Better security with minimal required modules -echo • Consistent Java version across all deployments -echo. -echo ✅ The application will now run without requiring users to install Java! diff --git a/scripts/build-tauri-jlink.sh b/scripts/build-tauri-jlink.sh deleted file mode 100755 index 9aee738e0c..0000000000 --- a/scripts/build-tauri-jlink.sh +++ /dev/null @@ -1,229 +0,0 @@ -#!/bin/bash - -# Build script for Tauri with JLink runtime bundling -# This script creates a self-contained Java runtime for Stirling-PDF - -set -e - -echo "🔧 Building Stirling-PDF with JLink runtime for Tauri..." - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_step() { - echo -e "${BLUE}▶ $1${NC}" -} - -print_success() { - echo -e "${GREEN}✅ $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠️ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -# Check if Java is installed and version -print_step "Checking Java environment..." -if ! command -v java &> /dev/null; then - print_error "Java is not installed or not in PATH" - exit 1 -fi - -if ! command -v jlink &> /dev/null; then - print_error "jlink is not available. Please ensure you have a JDK (not just JRE) installed." - exit 1 -fi - -JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2 | cut -d'.' -f1) -if [ "$JAVA_VERSION" -lt 21 ]; then - print_error "Java 21 or higher is required. Found Java $JAVA_VERSION" - exit 1 -fi - -print_success "Java $JAVA_VERSION detected with jlink support" - -# Check if jpackage is available (Java 14+) -if command -v jpackage &> /dev/null; then - print_success "jpackage is available for native packaging" -else - print_warning "jpackage is not available - using jlink only" -fi - -# Clean and build the Stirling-PDF JAR -print_step "Building Stirling-PDF JAR..." -export DISABLE_ADDITIONAL_FEATURES=true -./gradlew clean bootJar --no-daemon - -if [ ! -f compgen -G "app/core/build/libs/stirling-pdf-*.jar" ]; then - print_error "Failed to build stirling-pdf JAR" - exit 1 -fi - -# Find the built JAR -STIRLING_JAR=$(ls app/core/build/libs/stirling-pdf-*.jar | head -n 1) -print_success "Built JAR: $STIRLING_JAR" - -# Create directories for Tauri -TAURI_SRC_DIR="frontend/src-tauri" -TAURI_LIBS_DIR="$TAURI_SRC_DIR/libs" -TAURI_RUNTIME_DIR="$TAURI_SRC_DIR/runtime" - -print_step "Creating Tauri directories..." -mkdir -p "$TAURI_LIBS_DIR" -mkdir -p "$TAURI_RUNTIME_DIR" - -# Copy the JAR to Tauri libs directory -print_step "Copying JAR to Tauri libs directory..." -cp "$STIRLING_JAR" "$TAURI_LIBS_DIR/" -print_success "JAR copied to $TAURI_LIBS_DIR/" - -# Create a custom JRE using jlink -print_step "Creating custom JRE with jlink..." - -# Determine modules needed by analyzing the JAR -print_step "Analyzing JAR dependencies..." - -# Use jdeps to analyze module dependencies if available -if command -v jdeps &> /dev/null; then - print_step "Running jdeps analysis..." - REQUIRED_MODULES=$(jdeps --print-module-deps --ignore-missing-deps "$STIRLING_JAR" 2>/dev/null || echo "") - if [ -n "$REQUIRED_MODULES" ]; then - print_success "jdeps detected modules: $REQUIRED_MODULES" - # Add additional modules we know Stirling-PDF needs - MODULES="$REQUIRED_MODULES,java.compiler,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" - else - print_warning "jdeps analysis failed, using predefined module list" - MODULES="java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" - fi -else - print_warning "jdeps not available, using predefined module list" - MODULES="java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" -fi - -print_step "Creating JLink runtime with modules: $MODULES" - -# Remove existing runtime if present -rm -rf "$TAURI_RUNTIME_DIR/jre" - -# Create the custom JRE -jlink \ - --add-modules "$MODULES" \ - --strip-debug \ - --compress=2 \ - --no-header-files \ - --no-man-pages \ - --output "$TAURI_RUNTIME_DIR/jre" - -if [ ! -d "$TAURI_RUNTIME_DIR/jre" ]; then - print_error "Failed to create JLink runtime" - exit 1 -fi - -print_success "JLink runtime created at $TAURI_RUNTIME_DIR/jre" - -# Calculate runtime size -RUNTIME_SIZE=$(du -sh "$TAURI_RUNTIME_DIR/jre" | cut -f1) -print_success "Runtime size: $RUNTIME_SIZE" - -# Create launcher scripts for testing -print_step "Creating launcher scripts for testing..." - -LAUNCHER_SCRIPT="$TAURI_RUNTIME_DIR/launch-stirling.sh" -cat > "$LAUNCHER_SCRIPT" << 'EOF' -#!/bin/bash -# Launcher script for Stirling-PDF with bundled JRE - -# Get the directory of this script -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -JRE_DIR="$SCRIPT_DIR/jre" -LIBS_DIR="$(dirname "$SCRIPT_DIR")/libs" - -# Find the Stirling-PDF JAR -STIRLING_JAR=$(compgen -G "app/core/build/libs/stirling-pdf-*.jar") - -if [ ! -f "$STIRLING_JAR" ]; then - echo "❌ Stirling-PDF JAR not found in $LIBS_DIR" - exit 1 -fi - -# Launch with bundled JRE -"$JRE_DIR/bin/java" \ - -Xmx2g \ - -DBROWSER_OPEN=true \ - -jar "$STIRLING_JAR" \ - "$@" -EOF - -chmod +x "$LAUNCHER_SCRIPT" - -# Create Windows launcher -LAUNCHER_BAT="$TAURI_RUNTIME_DIR/launch-stirling.bat" -cat > "$LAUNCHER_BAT" << 'EOF' -@echo off -REM Launcher script for Stirling-PDF with bundled JRE - -set SCRIPT_DIR=%~dp0 -set JRE_DIR=%SCRIPT_DIR%jre -set LIBS_DIR=%SCRIPT_DIR%..\libs - -REM Find the Stirling-PDF JAR -for %%f in ("%LIBS_DIR%\Stirling-PDF-*.jar") do set STIRLING_JAR=%%f - -if not exist "%STIRLING_JAR%" ( - echo ❌ Stirling-PDF JAR not found in %LIBS_DIR% - exit /b 1 -) - -REM Launch with bundled JRE -"%JRE_DIR%\bin\java.exe" ^ - -Xmx2g ^ - -DBROWSER_OPEN=true ^ - -jar "%STIRLING_JAR%" ^ - %* -EOF - -print_success "Created launcher scripts for testing" - -# Test the bundled runtime -print_step "Testing bundled JRE..." -if [ -f "$TAURI_RUNTIME_DIR/jre/bin/java" ]; then - JAVA_VERSION_OUTPUT=$("$TAURI_RUNTIME_DIR/jre/bin/java" --version 2>&1 | head -n 1) - print_success "Bundled JRE works: $JAVA_VERSION_OUTPUT" -else - print_error "Bundled JRE executable not found" - exit 1 -fi - -# Display summary -echo "" -print_success "🎉 JLink build setup completed successfully!" -echo "" -echo -e "${BLUE}📊 Summary:${NC}" -echo " • JAR: $STIRLING_JAR" -echo " • Runtime: $TAURI_RUNTIME_DIR/jre ($RUNTIME_SIZE)" -echo " • Modules: $MODULES" -echo "" -echo -e "${BLUE}📋 Next steps:${NC}" -echo " 1. cd frontend" -echo " 2. npm run tauri-build" -echo "" -echo -e "${BLUE}💡 Testing:${NC}" -echo " • Test bundled runtime: $LAUNCHER_SCRIPT" -echo " • Tauri configuration already updated to include bundled JRE" -echo "" -echo -e "${BLUE}💡 Benefits:${NC}" -echo " • No external JRE dependency" -echo " • Smaller distribution size with custom runtime" -echo " • Better security with minimal required modules" -echo " • Consistent Java version across all deployments" -echo "" -print_success "The application will now run without requiring users to install Java!"