diff --git a/.dockerignore b/.dockerignore index 77566b0..db90f6f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -18,14 +18,11 @@ dist uploads CapacityReportData auth.ini -Configure.json *.log *.tmp *.temp *.bak -build/temp -build/pyinstaller target src-tauri/target src-tauri/binaries diff --git a/.gitignore b/.gitignore index 24f7144..f7766b1 100644 --- a/.gitignore +++ b/.gitignore @@ -35,8 +35,6 @@ dist/ cache/ logs/ uploads/ -build/temp/ -build/pyinstaller/ src-tauri/target/ src-tauri/binaries/ CapacityReportData/ diff --git a/README.md b/README.md index 10dfdb2..6ac8298 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ CapaReport/ ├─ frontend/ # Vue 前端 ├─ src-tauri/ # Tauri 桌面壳 ├─ scripts/ # 一键构建脚本 -├─ build/ # Docker 和 PyInstaller 配置 +├─ packaging/ # Docker、Compose、PyInstaller 配置 +├─ dist/ # 一键构建后的最终产物 ├─ docs/project_context.md # 项目维护记录 ├─ Configure.json # 应用配置 ├─ ReportScript.sql # SQL 处理脚本 @@ -117,6 +118,8 @@ scripts\build.bat docker -SkipDockerBuild scripts\build.bat all -SkipDesktopBuild ``` +构建完成后只保留 `dist/` 下的最终产物;`dist/.tmp`、`frontend/dist`、`src-tauri/target`、`src-tauri/binaries` 等中间产物会自动清理。 + ### Server Portable ```bat @@ -126,8 +129,8 @@ scripts\build.bat server 输出: ```text -dist\packages\CapacityReport-Server-windows-x64\ -dist\packages\CapacityReport-Server-windows-x64.zip +dist\server\CapacityReport-Server-windows-x64\ +dist\server\CapacityReport-Server-windows-x64.zip ``` 便携版内包含后端可执行文件、前端构建产物、`Configure.json`、`ReportScript.sql`、`cache/`、`logs/` 和启动脚本。默认监听端口为 `9081`。 @@ -141,8 +144,8 @@ scripts\build.bat desktop 输出: ```text -src-tauri\target\release\bundle\msi\*.msi -src-tauri\target\release\bundle\nsis\*-setup.exe +dist\desktop\*.msi +dist\desktop\*-setup.exe ``` 桌面版使用 Tauri 启动 Python sidecar。sidecar 默认监听 `127.0.0.1:19082`,运行数据写入系统 app data 目录,不写入安装目录。 @@ -159,16 +162,21 @@ cargo install tauri-cli --locked scripts\build.bat docker ``` -输出镜像: +输出: ```text capacity-report-app:latest +dist\docker\capacity-report-app-latest.tar +dist\docker\docker-compose.yml +dist\docker\Configure.json +dist\docker\ReportScript.sql ``` 启动: ```bat -docker compose -f build\docker-compose.yml up -d +docker load -i dist\docker\capacity-report-app-latest.tar +docker compose -f dist\docker\docker-compose.yml up -d ``` 访问: @@ -180,7 +188,7 @@ http://localhost:19081 停止: ```bat -docker compose -f build\docker-compose.yml down +docker compose -f dist\docker\docker-compose.yml down ``` ## Linux / macOS 构建 diff --git a/build/.dockerignore b/build/.dockerignore deleted file mode 100644 index af7694e..0000000 --- a/build/.dockerignore +++ /dev/null @@ -1,52 +0,0 @@ -# Git -.git -.gitignore - -# Python cache and virtual environments -__pycache__/ -*.py[cod] -.venv/ -venv/ -ENV/ -env/ - -# Frontend local artifacts -frontend/node_modules/ -frontend/dist/ -frontend/.vite/ - -# Runtime and package output -cache/ -logs/ -dist/ -uploads/ -CapacityReportData/ -error.log -*.log - -# Build helper output -build/temp/ - -# Local-only files -.env -.env.local -auth.ini - -# Deployment files copied separately by build.py -docker-compose*.yml -Dockerfile* -.dockerignore - -# MySQL data -mysql/ - -# Temporary and OS files -*.tmp -*.temp -.temp/ -.DS_Store -Thumbs.db - -# Documentation is not required in the runtime image. -*.md -!README.md diff --git a/build/README.md b/build/README.md deleted file mode 100644 index a1d6c67..0000000 --- a/build/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Build Notes - -Use the root scripts for release builds: - -```powershell -scripts\build.bat server -scripts\build.bat docker -scripts\build.bat desktop -``` - -On Linux or macOS: - -```bash -sh scripts/build.sh server -sh scripts/build.sh docker -sh scripts/build.sh desktop -``` - -## Targets - -- `server`: builds a portable server package for the current OS. -- `docker`: builds the Docker image and validates the compose file. -- `desktop`: builds a Tauri desktop package with the Python server sidecar. -- `all`: runs all targets. - -Native server and desktop packages must be built on the target OS. Build Windows packages on Windows, Linux packages on Linux, and macOS packages on macOS. Docker images can be built from the Windows development host. diff --git a/docs/project_context.md b/docs/project_context.md index 895dac4..ec42693 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -387,7 +387,7 @@ - `app/config.py` supports `CAPAREPORT_BASE_DIR`; frozen PyInstaller server builds default `BASE_DIR` to the executable directory. The Tauri sidecar sets `CAPAREPORT_BASE_DIR` to the app data directory so runtime files are not written into the install directory. - Tauri startup creates app-data `cache/` and `logs/`, then copies bundled `Configure.json` and `ReportScript.sql` on first run. Resource lookup supports both normal Tauri resources and the `_up_` directory generated for bundled `../` resources. - Windows desktop shutdown uses `taskkill /F /T /PID` before killing the shell child, which avoids PyInstaller one-file sidecar process leftovers. -- Docker build now copies only required app files and frontend build output. `.dockerignore` excludes local dependencies, caches, build output, and local config; `build/docker-compose.yml` mounts paths relative to the `build/` directory. +- Docker build now copies only required app files and frontend build output. `.dockerignore` excludes local dependencies, caches, and generated output; deployment compose files are emitted under `dist/docker/`. - `app/main.py` accepts `--host` and `--port` and exposes `run_server()` so portable launchers, Docker, and the Tauri sidecar share the same backend entry point. - Windows verification completed: `scripts\build.bat server -NoArchive`, `scripts\build.bat server`, `scripts\build.bat docker`, and `scripts\build.bat desktop`. Server portable `/health`, Docker container `/health`, and desktop sidecar `/health` all returned HTTP 200; desktop first run also copied config and SQL into app data. - Cleanup verification completed: `.venv\Scripts\python.exe -m compileall app`, `npm run build`, PowerShell AST parse for `scripts/build.ps1`, Docker-hosted `sh -n scripts/build.sh`, and `cargo check --manifest-path src-tauri\Cargo.toml` with a temporary sidecar placeholder all passed. Linux/macOS native server and desktop packages still need native OS verification. @@ -397,6 +397,13 @@ ## 2026-05-20: Tauri desktop console and installer language - `src-tauri/src/main.rs` uses `#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]` so Windows release builds use the GUI subsystem and do not open an extra console window when launched from Explorer or the installer shortcut. -- `build/capareport-server.spec` keeps Server Portable in console mode, but desktop one-file sidecar builds (`CAPAREPORT_ONEFILE=1`) use PyInstaller `console=False`; PyInstaller should select `runw.exe` for the sidecar so it also stays hidden behind the Tauri window. +- `packaging/capareport-server.spec` keeps Server Portable in console mode, but desktop one-file sidecar builds (`CAPAREPORT_ONEFILE=1`) use PyInstaller `console=False`; PyInstaller should select `runw.exe` for the sidecar so it also stays hidden behind the Tauri window. - `src-tauri/tauri.conf.json` sets Windows installer localization through `bundle.windows.wix.language = "zh-CN"` and `bundle.windows.nsis.languages = ["SimpChinese"]`. NSIS language keys must use NSIS names such as `SimpChinese`, while WiX/MSI uses locale names such as `zh-CN`. - Verification on Windows: `cargo check --manifest-path src-tauri\Cargo.toml` passed, `scripts\build.bat desktop` generated the MSI and NSIS bundles, PE subsystem checks reported `Windows GUI` for both `capacity-report-desktop.exe` and `capareport-server.exe`, and the generated NSIS script included `MUI_LANGUAGE "SimpChinese"`. + +## 2026-05-20: Unified release output under dist + +- The former `build/` packaging source directory was renamed to `packaging/` to avoid confusing source-side packaging recipes with generated output. `packaging/` now holds `Dockerfile`, `docker-compose.yml`, the PyInstaller spec, and MySQL container config. +- `scripts/build.ps1` and `scripts/build.sh` now use `dist/.tmp/` for PyInstaller work output and copy final deliverables to `dist/server/`, `dist/desktop/`, and `dist/docker/`. Successful builds remove `dist/.tmp`, `frontend/dist`, `src-tauri/target`, and `src-tauri/binaries`. +- Docker builds now include `Configure.json` in the image and also create a deployable `dist/docker/` bundle containing `capacity-report-app-latest.tar`, `docker-compose.yml`, `Configure.json`, `ReportScript.sql`, `mysql/`, `cache/`, and `logs/`. +- Server Portable still includes `Configure.json` and `ReportScript.sql` inside `dist/server/CapacityReport-Server--x64/`. Tauri desktop still bundles both files through `src-tauri/tauri.conf.json` resources and copies them to app data on first run. diff --git a/build/Dockerfile b/packaging/Dockerfile similarity index 93% rename from build/Dockerfile rename to packaging/Dockerfile index b5753f8..cb12bdf 100644 --- a/build/Dockerfile +++ b/packaging/Dockerfile @@ -31,7 +31,7 @@ RUN pip install --no-cache-dir -r requirements.txt RUN mkdir -p /var/log/supervisor /var/run /app/cache COPY app ./app -COPY ReportScript.sql start.sh supervisord.conf ./ +COPY Configure.json ReportScript.sql start.sh supervisord.conf ./ COPY --from=frontend-build /frontend/dist /app/frontend/dist RUN chmod +x /app/start.sh 2>/dev/null || true diff --git a/build/capareport-server.spec b/packaging/capareport-server.spec similarity index 100% rename from build/capareport-server.spec rename to packaging/capareport-server.spec diff --git a/build/docker-compose.yml b/packaging/docker-compose.yml similarity index 90% rename from build/docker-compose.yml rename to packaging/docker-compose.yml index a97330e..e185b7f 100644 --- a/build/docker-compose.yml +++ b/packaging/docker-compose.yml @@ -6,10 +6,10 @@ services: ports: - "19081:9081" volumes: - - ../cache:/app/cache - - ../Configure.json:/app/Configure.json - - ../ReportScript.sql:/app/ReportScript.sql - - ../logs:/var/log/supervisor + - ./cache:/app/cache + - ./Configure.json:/app/Configure.json + - ./ReportScript.sql:/app/ReportScript.sql + - ./logs:/var/log/supervisor environment: - TZ=Asia/Shanghai - PYTHONUNBUFFERED=1 diff --git a/build/mysql/conf.d/custom.cnf b/packaging/mysql/conf.d/custom.cnf similarity index 100% rename from build/mysql/conf.d/custom.cnf rename to packaging/mysql/conf.d/custom.cnf diff --git a/build/mysql/init/01-init-db.sql b/packaging/mysql/init/01-init-db.sql similarity index 100% rename from build/mysql/init/01-init-db.sql rename to packaging/mysql/init/01-init-db.sql diff --git a/scripts/build.ps1 b/scripts/build.ps1 index f348b16..658e4b9 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -17,13 +17,16 @@ $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$Root = Resolve-Path (Join-Path $ScriptDir "..") +$Root = (Resolve-Path (Join-Path $ScriptDir "..")).Path $DistDir = Join-Path $Root "dist" -$PackageDir = Join-Path $DistDir "packages" -$PyInstallerDir = Join-Path $Root "build\pyinstaller" +$ServerDistDir = Join-Path $DistDir "server" +$DesktopDistDir = Join-Path $DistDir "desktop" +$DockerDistDir = Join-Path $DistDir "docker" +$TempDir = Join-Path $DistDir ".tmp" +$PyInstallerDir = Join-Path $TempDir "pyinstaller" $FrontendDir = Join-Path $Root "frontend" +$PackagingDir = Join-Path $Root "packaging" $ServerName = "capareport-server" -$AppVersion = "2.0.2" $DesktopApiBase = "http://127.0.0.1:19082" function Write-Step($Message) { @@ -46,6 +49,21 @@ function Show-Usage { Write-Host " -SkipDesktopBuild" } +function Assert-InWorkspace($Path) { + $resolved = (Resolve-Path -LiteralPath $Path).Path + if (!$resolved.StartsWith($Root, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Refusing to operate outside workspace: $resolved" + } + return $resolved +} + +function Remove-Tree($Path) { + if (Test-Path -LiteralPath $Path) { + $resolved = Assert-InWorkspace $Path + Remove-Item -LiteralPath $resolved -Recurse -Force + } +} + function Invoke-Checked($Command, $WorkingDirectory = $Root) { Write-Info $Command Push-Location $WorkingDirectory @@ -136,9 +154,15 @@ function Build-Frontend($ApiBase) { function Build-ServerBinary([switch]$OneFile) { Ensure-PythonEnvironment Write-Step "Building server binary" - Remove-Item -LiteralPath $PyInstallerDir -Recurse -Force -ErrorAction SilentlyContinue + Remove-Tree $PyInstallerDir + New-Item -ItemType Directory -Force -Path $PyInstallerDir | Out-Null + $python = Join-Path $Root ".venv\Scripts\python.exe" + $workPath = Join-Path $PyInstallerDir "work" + $distPath = Join-Path $PyInstallerDir "dist" + $specPath = Join-Path $PackagingDir "capareport-server.spec" $previousOneFile = $env:CAPAREPORT_ONEFILE + if ($OneFile) { $env:CAPAREPORT_ONEFILE = "1" } @@ -147,7 +171,7 @@ function Build-ServerBinary([switch]$OneFile) { } try { - Invoke-Checked "`"$python`" -m PyInstaller --clean --noconfirm --workpath build\pyinstaller\work --distpath build\pyinstaller\dist build\capareport-server.spec" $Root + Invoke-Checked "`"$python`" -m PyInstaller --clean --noconfirm --workpath `"$workPath`" --distpath `"$distPath`" `"$specPath`"" $Root } finally { if ($null -ne $previousOneFile) { @@ -159,14 +183,14 @@ function Build-ServerBinary([switch]$OneFile) { } if ($OneFile) { - $binaryDir = Join-Path $PyInstallerDir "dist" + $binaryDir = $distPath $binaryPath = Join-Path $binaryDir (Get-ExecutableName (Get-CurrentPlatform)) if (!(Test-Path $binaryPath)) { throw "PyInstaller output not found: $binaryPath" } } else { - $binaryDir = Join-Path $PyInstallerDir "dist\$ServerName" + $binaryDir = Join-Path $distPath $ServerName if (!(Test-Path $binaryDir)) { throw "PyInstaller output not found: $binaryDir" } @@ -237,11 +261,14 @@ function Build-ServerPortable { $platformName = "windows" } + $outputDir = Join-Path $ServerDistDir "CapacityReport-Server-$platformName-x64" + $archive = "$outputDir.zip" + Remove-Tree $outputDir + Remove-Item -LiteralPath $archive -Force -ErrorAction SilentlyContinue + Build-Frontend $null $binaryDir = Build-ServerBinary - $outputDir = Join-Path $PackageDir "CapacityReport-Server-$platformName-x64" - Remove-Item -LiteralPath $outputDir -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $outputDir | Out-Null Copy-Item -Path (Join-Path $binaryDir "*") -Destination $outputDir -Recurse -Force @@ -249,8 +276,6 @@ function Build-ServerPortable { Write-ServerLaunchers $outputDir $platformName if (!$NoArchive) { - $archive = "$outputDir.zip" - Remove-Item -LiteralPath $archive -Force -ErrorAction SilentlyContinue Compress-Archive -Path (Join-Path $outputDir "*") -DestinationPath $archive -Force Write-Info "Archive: $archive" } @@ -258,15 +283,36 @@ function Build-ServerPortable { Write-Info "Server package: $outputDir" } +function Copy-DockerBundle { + Remove-Tree $DockerDistDir + New-Item -ItemType Directory -Force -Path $DockerDistDir | Out-Null + New-Item -ItemType Directory -Force -Path (Join-Path $DockerDistDir "cache") | Out-Null + New-Item -ItemType Directory -Force -Path (Join-Path $DockerDistDir "logs") | Out-Null + Copy-Item -Path (Join-Path $PackagingDir "docker-compose.yml") -Destination (Join-Path $DockerDistDir "docker-compose.yml") -Force + Copy-Item -Path (Join-Path $PackagingDir "mysql") -Destination (Join-Path $DockerDistDir "mysql") -Recurse -Force + Copy-Item -Path (Join-Path $Root "Configure.json") -Destination (Join-Path $DockerDistDir "Configure.json") -Force + Copy-Item -Path (Join-Path $Root "ReportScript.sql") -Destination (Join-Path $DockerDistDir "ReportScript.sql") -Force +} + function Build-DockerImage { + Remove-Tree $DockerDistDir + if ($SkipDockerBuild) { - Write-Host "Docker build skipped." + Write-Host "Docker image build skipped." + Copy-DockerBundle + Invoke-Checked "docker compose -f `"$DockerDistDir\docker-compose.yml`" config" $Root + Write-Info "Docker package: $DockerDistDir" return } Write-Step "Building Docker image" - Invoke-Checked "docker build --progress=plain -f build\Dockerfile -t capacity-report-app:latest ." $Root - Invoke-Checked "docker compose -f build\docker-compose.yml config" $Root + Invoke-Checked "docker build --progress=plain -f `"$PackagingDir\Dockerfile`" -t capacity-report-app:latest ." $Root + + Write-Step "Packaging Docker output" + Copy-DockerBundle + Invoke-Checked "docker save -o `"$DockerDistDir\capacity-report-app-latest.tar`" capacity-report-app:latest" $Root + Invoke-Checked "docker compose -f `"$DockerDistDir\docker-compose.yml`" config" $Root + Write-Info "Docker package: $DockerDistDir" } function Build-DesktopPackage { @@ -279,6 +325,8 @@ function Build-DesktopPackage { throw "Rust is required for the Tauri desktop build" } + Remove-Tree $DesktopDistDir + Build-Frontend $DesktopApiBase $binaryDir = Build-ServerBinary -OneFile $platformName = Get-CurrentPlatform @@ -294,8 +342,23 @@ function Build-DesktopPackage { Invoke-Checked "cargo install tauri-cli --locked" $Root } + Remove-Tree (Join-Path $Root "src-tauri\target\release\bundle") Write-Step "Building Tauri desktop package" Invoke-Checked "cargo tauri build" (Join-Path $Root "src-tauri") + + New-Item -ItemType Directory -Force -Path $DesktopDistDir | Out-Null + $bundleDir = Join-Path $Root "src-tauri\target\release\bundle" + Get-ChildItem -Path $bundleDir -Recurse -File -Include "*.msi", "*.exe" | + Copy-Item -Destination $DesktopDistDir -Force + Write-Info "Desktop package: $DesktopDistDir" +} + +function Clean-Intermediates { + Write-Step "Cleaning intermediate output" + Remove-Tree $TempDir + Remove-Tree (Join-Path $Root "frontend\dist") + Remove-Tree (Join-Path $Root "src-tauri\binaries") + Remove-Tree (Join-Path $Root "src-tauri\target") } if ($Help) { @@ -304,13 +367,14 @@ if ($Help) { } if ($Clean) { - Write-Step "Cleaning build output" - Remove-Item -LiteralPath $DistDir -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -LiteralPath $PyInstallerDir -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -LiteralPath (Join-Path $Root "src-tauri\binaries") -Recurse -Force -ErrorAction SilentlyContinue + Write-Step "Cleaning dist output" + Remove-Tree $DistDir + Remove-Tree (Join-Path $Root "frontend\dist") + Remove-Tree (Join-Path $Root "src-tauri\binaries") + Remove-Tree (Join-Path $Root "src-tauri\target") } -New-Item -ItemType Directory -Force -Path $PackageDir | Out-Null +New-Item -ItemType Directory -Force -Path $DistDir | Out-Null switch ($Target) { "server" { Build-ServerPortable } @@ -323,4 +387,6 @@ switch ($Target) { } } +Clean-Intermediates + Write-Step "Build finished" diff --git a/scripts/build.sh b/scripts/build.sh index 1ce8de7..a215a2f 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -4,7 +4,15 @@ set -eu TARGET="${1:-server}" SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)" +DIST_DIR="$ROOT/dist" +SERVER_DIST_DIR="$DIST_DIR/server" +DESKTOP_DIST_DIR="$DIST_DIR/desktop" +DOCKER_DIST_DIR="$DIST_DIR/docker" +TMP_DIR="$DIST_DIR/.tmp" +PYINSTALLER_DIR="$TMP_DIR/pyinstaller" +PACKAGING_DIR="$ROOT/packaging" DESKTOP_API_BASE="http://127.0.0.1:19082" +SERVER_NAME="capareport-server" cd "$ROOT" log() { @@ -24,6 +32,18 @@ platform_name() { esac } +server_exe_name() { + case "$(platform_name)" in + windows) printf '%s.exe' "$SERVER_NAME" ;; + *) printf '%s' "$SERVER_NAME" ;; + esac +} + +clean_intermediates() { + log "Cleaning intermediate output" + rm -rf "$TMP_DIR" "$ROOT/frontend/dist" "$ROOT/src-tauri/binaries" "$ROOT/src-tauri/target" +} + ensure_python() { log "Preparing Python environment" if [ ! -x ".venv/bin/python" ]; then @@ -49,20 +69,22 @@ build_server_binary() { mode="${1:-onedir}" ensure_python log "Building server binary" - rm -rf build/pyinstaller + rm -rf "$PYINSTALLER_DIR" + mkdir -p "$PYINSTALLER_DIR" + if [ "$mode" = "onefile" ]; then CAPAREPORT_ONEFILE=1 export CAPAREPORT_ONEFILE run .venv/bin/python -m PyInstaller --clean --noconfirm \ - --workpath build/pyinstaller/work \ - --distpath build/pyinstaller/dist \ - build/capareport-server.spec + --workpath "$PYINSTALLER_DIR/work" \ + --distpath "$PYINSTALLER_DIR/dist" \ + "$PACKAGING_DIR/capareport-server.spec" unset CAPAREPORT_ONEFILE else run .venv/bin/python -m PyInstaller --clean --noconfirm \ - --workpath build/pyinstaller/work \ - --distpath build/pyinstaller/dist \ - build/capareport-server.spec + --workpath "$PYINSTALLER_DIR/work" \ + --distpath "$PYINSTALLER_DIR/dist" \ + "$PACKAGING_DIR/capareport-server.spec" fi } @@ -85,38 +107,64 @@ EOF build_server() { platform="$(platform_name)" + exe_name="$(server_exe_name)" + out_dir="$SERVER_DIST_DIR/CapacityReport-Server-$platform-x64" + rm -rf "$out_dir" "$SERVER_DIST_DIR/$(basename "$out_dir").tar.gz" + build_frontend server build_server_binary onedir - out_dir="dist/packages/CapacityReport-Server-$platform-x64" - rm -rf "$out_dir" mkdir -p "$out_dir/cache" "$out_dir/logs" "$out_dir/frontend" - cp -R build/pyinstaller/dist/capareport-server/. "$out_dir/" + cp -R "$PYINSTALLER_DIR/dist/$SERVER_NAME/." "$out_dir/" cp Configure.json ReportScript.sql "$out_dir/" cp -R frontend/dist "$out_dir/frontend/" - write_launchers "$out_dir" "capareport-server" - (cd "$out_dir/.." && tar -czf "$(basename "$out_dir").tar.gz" "$(basename "$out_dir")") + write_launchers "$out_dir" "$exe_name" + (cd "$SERVER_DIST_DIR" && tar -czf "$(basename "$out_dir").tar.gz" "$(basename "$out_dir")") log "Server package: $out_dir" } +copy_docker_bundle() { + rm -rf "$DOCKER_DIST_DIR" + mkdir -p "$DOCKER_DIST_DIR/cache" "$DOCKER_DIST_DIR/logs" + cp "$PACKAGING_DIR/docker-compose.yml" "$DOCKER_DIST_DIR/docker-compose.yml" + cp Configure.json ReportScript.sql "$DOCKER_DIST_DIR/" + cp -R "$PACKAGING_DIR/mysql" "$DOCKER_DIST_DIR/mysql" +} + build_docker() { + rm -rf "$DOCKER_DIST_DIR" + log "Building Docker image" - run docker build --progress=plain -f build/Dockerfile -t capacity-report-app:latest . - run docker compose -f build/docker-compose.yml config + run docker build --progress=plain -f "$PACKAGING_DIR/Dockerfile" -t capacity-report-app:latest . + log "Packaging Docker output" + copy_docker_bundle + run docker save -o "$DOCKER_DIST_DIR/capacity-report-app-latest.tar" capacity-report-app:latest + run docker compose -f "$DOCKER_DIST_DIR/docker-compose.yml" config + log "Docker package: $DOCKER_DIST_DIR" } build_desktop() { + rm -rf "$DESKTOP_DIST_DIR" + build_frontend desktop build_server_binary onefile triple="$(rustc -vV | awk '/^host:/ {print $2}')" mkdir -p src-tauri/binaries - cp build/pyinstaller/dist/capareport-server "src-tauri/binaries/capareport-server-$triple" + cp "$PYINSTALLER_DIR/dist/$(server_exe_name)" "src-tauri/binaries/$SERVER_NAME-$triple" if ! cargo tauri --version >/dev/null 2>&1; then run cargo install tauri-cli --locked fi + rm -rf src-tauri/target/release/bundle (cd src-tauri && run cargo tauri build) + + rm -rf "$DESKTOP_DIST_DIR" + mkdir -p "$DESKTOP_DIST_DIR" + find src-tauri/target/release/bundle -type f \( -name '*.msi' -o -name '*.exe' \) -exec cp {} "$DESKTOP_DIST_DIR/" \; + log "Desktop package: $DESKTOP_DIST_DIR" } +mkdir -p "$DIST_DIR" + case "$TARGET" in server) build_server ;; docker) build_docker ;; @@ -125,4 +173,6 @@ case "$TARGET" in *) echo "Usage: scripts/build.sh [server|docker|desktop|all]" >&2; exit 1 ;; esac +clean_intermediates + log "Build finished"