diff --git a/.gitignore b/.gitignore index 617fcb6..6bfddea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ docker-build.log thebet365-images.tar thebet365-images-*.tar thebet365-images-*.manifest.txt +thebet365-full-themes-*.tar +thebet365-full-themes-*.manifest.txt +thebet365-player-*.tar +thebet365-player-*.manifest.txt thebet365-admin-latest.tar thebet365-admin-latest.manifest.txt .claude/ diff --git a/docs/docker/build-and-export-all-themes.bat b/docs/docker/build-and-export-all-themes.bat index 298ccba..94a48d0 100644 --- a/docs/docker/build-and-export-all-themes.bat +++ b/docs/docker/build-and-export-all-themes.bat @@ -1,261 +1,13 @@ @echo off -setlocal EnableDelayedExpansion - -REM ================================================================ -REM build-and-export-all-themes.bat +REM Thin launcher for build-and-export-all-themes.ps1 +REM CMD reads batch files line-by-line from disk; git checkout to theme-* removes +REM this file from the working tree. PowerShell loads the script into memory first. REM -REM Four-theme player image batch builder. -REM Automatically switches Git branches to build each theme. -REM Usage: -REM docs\docker\build-and-export-all-themes.bat -REM docs\docker\build-and-export-all-themes.bat --use-cache -REM docs\docker\build-and-export-all-themes.bat --export-only -REM docs\docker\build-and-export-all-themes.bat --skip-api-admin -REM -REM Output files (project root, already in .gitignore): -REM thebet365-player-main.tar -> thebet365-player:main -REM thebet365-player-theme-2.tar -> thebet365-player:theme-2 -REM thebet365-player-theme-3.tar -> thebet365-player:theme-3 -REM thebet365-player-theme-4.tar -> thebet365-player:theme-4 -REM thebet365-images-latest.tar -> api:latest + admin:latest -REM ================================================================ +REM Usage: +REM docs\docker\build-and-export-all-themes.bat +REM docs\docker\build-and-export-all-themes.bat --use-cache +REM docs\docker\build-and-export-all-themes.bat --export-only +REM docs\docker\build-and-export-all-themes.bat --skip-api-admin -cd /d "%~dp0..\.." -set "ROOT_DIR=%CD%" - -set "USE_CACHE=0" -set "EXPORT_ONLY=0" -set "SKIP_API_ADMIN=0" - -:parse_args -if "%~1"=="" goto args_done -if /i "%~1"=="--use-cache" set "USE_CACHE=1" & shift & goto parse_args -if /i "%~1"=="--export-only" set "EXPORT_ONLY=1" & shift & goto parse_args -if /i "%~1"=="--skip-api-admin" set "SKIP_API_ADMIN=1" & shift & goto parse_args -if /i "%~1"=="-h" goto show_help -if /i "%~1"=="--help" goto show_help -if /i "%~1"=="/?" goto show_help -echo Unknown argument: %~1 -goto show_help - -:show_help -echo. -echo Usage: docs\docker\build-and-export-all-themes.bat [options] -echo. -echo --use-cache Use Docker build cache (default: --no-cache) -echo --export-only Skip build, export existing images only -echo --skip-api-admin Only build 4 player themes, skip api/admin -echo -h, --help Show this help -echo. -echo Output (project root): -echo thebet365-player-main.tar -echo thebet365-player-theme-2.tar -echo thebet365-player-theme-3.tar -echo thebet365-player-theme-4.tar -echo thebet365-images-latest.tar (api + admin, unless --skip-api-admin) -echo. -exit /b 0 - -:args_done - -REM -- Validate docker is available -where docker >nul 2>&1 -if errorlevel 1 ( - echo ERROR: docker not found. Please start Docker Desktop first. - exit /b 1 -) - -REM -- Record current branch so we can restore it -set "ORIGINAL_BRANCH=" -for /f "delims=" %%B in ('git rev-parse --abbrev-ref HEAD 2^>nul') do set "ORIGINAL_BRANCH=%%B" -if not defined ORIGINAL_BRANCH ( - echo ERROR: Cannot determine current Git branch. Run this script from the repo root. - exit /b 1 -) -echo [INFO] Current branch: %ORIGINAL_BRANCH% - -REM -- Stash any local changes so branch switching works cleanly -set "DID_STASH=0" -git diff --quiet 2>nul -set "D1=!ERRORLEVEL!" -git diff --cached --quiet 2>nul -set "D2=!ERRORLEVEL!" -if not "!D1!"=="0" goto do_stash -if not "!D2!"=="0" goto do_stash -goto stash_done - -:do_stash -echo [INFO] Stashing local changes before branch switching... -git stash push -m "build-all-themes auto-stash" >nul 2>&1 -if errorlevel 1 ( - echo [WARN] git stash failed. Branch switching may fail if there are conflicts. -) else ( - set "DID_STASH=1" - echo [INFO] Changes stashed. -) - -:stash_done - -REM -- Build extra arguments for the base script -set "EXTRA_ARGS=" -if "%USE_CACHE%"=="1" set "EXTRA_ARGS=!EXTRA_ARGS! --use-cache" -if "%EXPORT_ONLY%"=="1" set "EXTRA_ARGS=!EXTRA_ARGS! --export-only" - -set "FAILED_THEMES=" -set "SUCCESS_COUNT=0" - -REM ================================================================ -REM Build each player theme -REM ================================================================ - -call :build_theme main main -call :build_theme theme-2 theme-2 -call :build_theme theme-3 theme-3 -call :build_theme theme-4 theme-4 - -REM -- Restore original branch -echo. -echo [INFO] Restoring original branch: %ORIGINAL_BRANCH% -cd /d "%ROOT_DIR%" -git checkout %ORIGINAL_BRANCH% >nul 2>&1 -if errorlevel 1 ( - echo [WARN] Could not restore branch %ORIGINAL_BRANCH%. Run: git checkout %ORIGINAL_BRANCH% -) else ( - echo [INFO] Restored to %ORIGINAL_BRANCH% -) - -REM -- Pop stash if we stashed earlier -if "%DID_STASH%"=="1" ( - echo [INFO] Restoring stashed changes... - git stash pop >nul 2>&1 - if errorlevel 1 ( - echo [WARN] git stash pop failed. Run: git stash pop - ) else ( - echo [INFO] Stash restored. - ) -) - -REM -- Build api + admin on main branch -if "%SKIP_API_ADMIN%"=="1" goto skip_api_admin - -echo. -echo [INFO] Building api + admin (tag: latest) on branch main... -cd /d "%ROOT_DIR%" -git checkout main >nul 2>&1 -cd /d "%ROOT_DIR%" -call docs\docker\build-and-export-images.bat --tag latest!EXTRA_ARGS! -set "API_ERR=!ERRORLEVEL!" -cd /d "%ROOT_DIR%" -if "!API_ERR!"=="0" goto api_ok -echo [ERROR] api/admin build failed -set "FAILED_THEMES=!FAILED_THEMES! api/admin" -goto after_api_admin - -:api_ok -set /a SUCCESS_COUNT+=1 - -:after_api_admin -echo. -echo [INFO] Restoring original branch: %ORIGINAL_BRANCH% -git checkout %ORIGINAL_BRANCH% >nul 2>&1 - -:skip_api_admin - -REM ================================================================ -REM Summary -REM ================================================================ -echo. -echo ================================================================ -echo BUILD SUMMARY -echo ================================================================ -echo Succeeded: !SUCCESS_COUNT! task(s) -if not defined FAILED_THEMES goto all_ok - -echo Failed: !FAILED_THEMES! -echo. -echo Tip: Retry failed themes manually: -echo git checkout ^ -echo docs\docker\build-and-export-images.bat --service player --tag ^ -echo ================================================================ -echo. -endlocal -exit /b 1 - -:all_ok -echo. -echo All done! Output files in project root: -echo thebet365-player-main.tar -echo thebet365-player-theme-2.tar -echo thebet365-player-theme-3.tar -echo thebet365-player-theme-4.tar -if "%SKIP_API_ADMIN%"=="0" ( - echo thebet365-images-latest.tar (api + admin) -) -echo. -echo Server import commands: -echo docker load -i thebet365-images-latest.tar -echo docker load -i thebet365-player-main.tar -echo docker load -i thebet365-player-theme-2.tar -echo docker load -i thebet365-player-theme-3.tar -echo docker load -i thebet365-player-theme-4.tar -echo. -echo Start (first multi-theme deploy): -echo docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d -echo ================================================================ -echo. -endlocal -exit /b 0 - - -REM ================================================================ -REM :build_theme -REM Switch to branch, build player, mark success/fail. -REM Uses cd /d ROOT_DIR instead of pushd to avoid stack issues. -REM ================================================================ -:build_theme -set "THEME_BRANCH=%~1" -set "THEME_TAG=%~2" - -echo. -echo ---------------------------------------------------------------- -echo [INFO] Theme: %THEME_BRANCH% (image tag: %THEME_TAG%) -echo ---------------------------------------------------------------- - -cd /d "%ROOT_DIR%" -git checkout %THEME_BRANCH% >nul 2>&1 -if errorlevel 1 ( - echo [ERROR] Cannot switch to branch %THEME_BRANCH% -- skipping - set "FAILED_THEMES=!FAILED_THEMES! %THEME_BRANCH%" - exit /b 0 -) -echo [INFO] Switched to %THEME_BRANCH% - -cd /d "%ROOT_DIR%" -call docs\docker\build-and-export-images.bat --service player --tag %THEME_TAG%!EXTRA_ARGS! -set "BUILD_ERR=!ERRORLEVEL!" - -cd /d "%ROOT_DIR%" -if "!BUILD_ERR!"=="0" goto build_ok_%THEME_TAG% -echo [ERROR] Player build failed for %THEME_BRANCH% -set "FAILED_THEMES=!FAILED_THEMES! %THEME_BRANCH%" -exit /b 0 - -:build_ok_main - echo [OK] thebet365-player:main exported successfully - set /a SUCCESS_COUNT+=1 - exit /b 0 - -:build_ok_theme-2 - echo [OK] thebet365-player:theme-2 exported successfully - set /a SUCCESS_COUNT+=1 - exit /b 0 - -:build_ok_theme-3 - echo [OK] thebet365-player:theme-3 exported successfully - set /a SUCCESS_COUNT+=1 - exit /b 0 - -:build_ok_theme-4 - echo [OK] thebet365-player:theme-4 exported successfully - set /a SUCCESS_COUNT+=1 - exit /b 0 +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0build-and-export-all-themes.ps1" %* +exit /b %ERRORLEVEL% diff --git a/docs/docker/build-and-export-all-themes.ps1 b/docs/docker/build-and-export-all-themes.ps1 new file mode 100644 index 0000000..5460292 --- /dev/null +++ b/docs/docker/build-and-export-all-themes.ps1 @@ -0,0 +1,285 @@ +# 四套主题 player 一键打包(自动切换 Git 分支) +# PowerShell 会在启动时将整份脚本载入内存,git checkout 后仍可继续执行。 +# 用法(项目根目录或本目录均可): +# .\docs\docker\build-and-export-all-themes.ps1 +# .\docs\docker\build-and-export-all-themes.ps1 -UseCache +# .\docs\docker\build-and-export-all-themes.ps1 -ExportOnly +# .\docs\docker\build-and-export-all-themes.ps1 -SkipApiAdmin +# 或通过 bat 启动(参数支持 --use-cache 等 CMD 风格): +# docs\docker\build-and-export-all-themes.bat --use-cache + +param( + [switch]$UseCache, + [switch]$ExportOnly, + [switch]$SkipApiAdmin, + [switch]$SkipBundle, + [switch]$Help +) + +$ErrorActionPreference = "Stop" + +foreach ($arg in $args) { + switch ($arg.ToLowerInvariant()) { + "--use-cache" { $UseCache = $true; continue } + "--export-only" { $ExportOnly = $true; continue } + "--skip-api-admin" { $SkipApiAdmin = $true; continue } + "--skip-bundle" { $SkipBundle = $true; continue } + "-h" { $Help = $true; continue } + "--help" { $Help = $true; continue } + "/?" { $Help = $true; continue } + default { throw "Unknown argument: $arg" } + } +} + +function Show-Help { + Write-Host @" + +Usage: docs\docker\build-and-export-all-themes.ps1 [options] + + -UseCache, --use-cache Use Docker build cache (default: --no-cache) + -ExportOnly, --export-only Skip build, export existing images only + -SkipApiAdmin, --skip-api-admin + Only build 4 player themes, skip api/admin + -SkipBundle, --skip-bundle Skip thebet365-full-themes-latest.tar (6-in-1 bundle) + -Help, -h, --help Show this help + +Output (project root): + thebet365-player-main.tar + thebet365-player-theme-2.tar + thebet365-player-theme-3.tar + thebet365-player-theme-4.tar + thebet365-images-latest.tar (api + admin, unless -SkipApiAdmin) + thebet365-full-themes-latest.tar (api + admin + 4 player themes, unless -SkipBundle) + +"@ +} + +if ($Help) { + Show-Help + exit 0 +} + +$Root = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path + +$Themes = @( + @{ Branch = "main"; Tag = "main" }, + @{ Branch = "theme-2"; Tag = "theme-2" }, + @{ Branch = "theme-3"; Tag = "theme-3" }, + @{ Branch = "theme-4"; Tag = "theme-4" } +) + +if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { + throw "docker not found. Please start Docker Desktop first." +} + +if (-not (Test-Path (Join-Path $Root "docs\docker\build-and-export-images.bat"))) { + throw "Missing build script: docs\docker\build-and-export-images.bat" +} + +function Invoke-Git { + param([Parameter(ValueFromRemainingArguments = $true)][string[]]$GitArgs) + + $prevErrorAction = $ErrorActionPreference + $ErrorActionPreference = "SilentlyContinue" + try { + & git @GitArgs 2>$null | Out-Null + return $LASTEXITCODE + } finally { + $ErrorActionPreference = $prevErrorAction + } +} + +function Get-GitOutput { + param([Parameter(ValueFromRemainingArguments = $true)][string[]]$GitArgs) + + $prevErrorAction = $ErrorActionPreference + $ErrorActionPreference = "SilentlyContinue" + try { + return ((& git @GitArgs 2>$null | Out-String).Trim()) + } finally { + $ErrorActionPreference = $prevErrorAction + } +} + +Set-Location $Root + +$OriginalBranch = Get-GitOutput rev-parse --abbrev-ref HEAD +if ([string]::IsNullOrWhiteSpace($OriginalBranch)) { + throw "Cannot determine current Git branch. Run this script from the repo root." +} +Write-Host "[INFO] Current branch: $OriginalBranch" + +$DidStash = $false +$diffExit = Invoke-Git diff --quiet +$cachedExit = Invoke-Git diff --cached --quiet +if ($diffExit -ne 0 -or $cachedExit -ne 0) { + Write-Host "[INFO] Stashing local changes before branch switching..." + $stashExit = Invoke-Git stash push -m "build-all-themes auto-stash" + if ($stashExit -eq 0) { + $DidStash = $true + Write-Host "[INFO] Changes stashed." + } else { + Write-Warning "git stash failed. Branch switching may fail if there are conflicts." + } +} + +$FailedThemes = [System.Collections.Generic.List[string]]::new() +$SuccessCount = 0 + +function Invoke-BuildExport { + param( + [string]$Service = "", + [string]$Tag + ) + + $argList = @() + if (-not [string]::IsNullOrWhiteSpace($Service)) { + $argList += "--service" + $argList += $Service + } + $argList += "--tag" + $argList += $Tag + if ($UseCache) { $argList += "--use-cache" } + if ($ExportOnly) { $argList += "--export-only" } + + Set-Location $Root + $batPath = Join-Path $Root "docs\docker\build-and-export-images.bat" + $argText = ($argList | ForEach-Object { + if ($_ -match '\s') { "`"$_`"" } else { $_ } + }) -join ' ' + & cmd.exe /c "`"$batPath`" $argText" + return $LASTEXITCODE +} + +function Invoke-PlayerThemeBuild { + param( + [string]$Branch, + [string]$Tag + ) + + Write-Host "" + Write-Host "----------------------------------------------------------------" + Write-Host "[INFO] Theme: $Branch (image tag: $Tag)" + Write-Host "----------------------------------------------------------------" + + Set-Location $Root + $checkoutExit = Invoke-Git checkout $Branch + if ($checkoutExit -ne 0) { + Write-Host "[ERROR] Cannot switch to branch $Branch -- skipping" + $script:FailedThemes.Add($Branch) | Out-Null + return + } + Write-Host "[INFO] Switched to $Branch" + + Set-Location $Root + if ((Invoke-BuildExport -Service "player" -Tag $Tag) -ne 0) { + Write-Host "[ERROR] Player build failed for $Branch" + $script:FailedThemes.Add($Branch) | Out-Null + return + } + + Write-Host "[OK] thebet365-player:$Tag exported successfully" + $script:SuccessCount++ +} + +foreach ($theme in $Themes) { + Invoke-PlayerThemeBuild -Branch $theme.Branch -Tag $theme.Tag +} + +Write-Host "" +Write-Host "[INFO] Restoring original branch: $OriginalBranch" +Set-Location $Root +if ((Invoke-Git checkout $OriginalBranch) -eq 0) { + Write-Host "[INFO] Restored to $OriginalBranch" +} else { + Write-Warning "Could not restore branch $OriginalBranch. Run: git checkout $OriginalBranch" +} + +if ($DidStash) { + Write-Host "[INFO] Restoring stashed changes..." + if ((Invoke-Git stash pop) -eq 0) { + Write-Host "[INFO] Stash restored." + } else { + Write-Warning "git stash pop failed. Run: git stash pop" + } +} + +if (-not $SkipApiAdmin) { + Write-Host "" + Write-Host "[INFO] Building api + admin (tag: latest) on branch main..." + Set-Location $Root + if ((Invoke-Git checkout main) -ne 0) { + Write-Host "[ERROR] Cannot switch to branch main for api/admin build" + $FailedThemes.Add("api/admin") | Out-Null + } else { + Set-Location $Root + if ((Invoke-BuildExport -Service "api-admin" -Tag "latest") -ne 0) { + Write-Host "[ERROR] api/admin build failed" + $FailedThemes.Add("api/admin") | Out-Null + } else { + $SuccessCount++ + } + } + + Write-Host "" + Write-Host "[INFO] Restoring original branch: $OriginalBranch" + Set-Location $Root + Invoke-Git checkout $OriginalBranch | Out-Null +} + +if (-not $SkipBundle -and -not $SkipApiAdmin -and $FailedThemes.Count -eq 0) { + Write-Host "" + Write-Host "[INFO] Exporting full bundle (api + admin + 4 player themes)..." + Set-Location $Root + if ((Invoke-BuildExport -Service "full-themes" -Tag "latest") -ne 0) { + Write-Host "[ERROR] Full bundle export failed" + $FailedThemes.Add("full-bundle") | Out-Null + } else { + Write-Host "[OK] thebet365-full-themes-latest.tar exported successfully" + $SuccessCount++ + } +} + +Write-Host "" +Write-Host "================================================================" +Write-Host " BUILD SUMMARY" +Write-Host "================================================================" +Write-Host " Succeeded: $SuccessCount task(s)" + +if ($FailedThemes.Count -gt 0) { + Write-Host " Failed: $($FailedThemes -join ' ')" + Write-Host "" + Write-Host " Tip: Retry failed themes manually:" + Write-Host " git checkout " + Write-Host " docs\docker\build-and-export-images.bat --service player --tag " + Write-Host "================================================================" + exit 1 +} + +Write-Host "" +Write-Host " All done! Output files in project root:" +Write-Host " thebet365-player-main.tar" +Write-Host " thebet365-player-theme-2.tar" +Write-Host " thebet365-player-theme-3.tar" +Write-Host " thebet365-player-theme-4.tar" +if (-not $SkipApiAdmin) { + Write-Host " thebet365-images-latest.tar (api + admin)" +} +if (-not $SkipBundle -and -not $SkipApiAdmin) { + Write-Host " thebet365-full-themes-latest.tar (api + admin + 4 themes, upload this only)" +} +Write-Host "" +Write-Host " Server import (single bundle):" +Write-Host " docker load -i thebet365-full-themes-latest.tar" +Write-Host " docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d" +Write-Host "" +Write-Host " Or import separate tars:" +if (-not $SkipApiAdmin) { + Write-Host " docker load -i thebet365-images-latest.tar" +} +Write-Host " docker load -i thebet365-player-main.tar" +Write-Host " docker load -i thebet365-player-theme-2.tar" +Write-Host " docker load -i thebet365-player-theme-3.tar" +Write-Host " docker load -i thebet365-player-theme-4.tar" +Write-Host "================================================================" +exit 0 diff --git a/docs/docker/build-and-export-images.bat b/docs/docker/build-and-export-images.bat index 44fea38..ea6e690 100644 --- a/docs/docker/build-and-export-images.bat +++ b/docs/docker/build-and-export-images.bat @@ -69,7 +69,7 @@ goto parse_args echo. echo Usage: docs\docker\build-and-export-images.bat [options] echo. -echo --service SVC api ^| player ^| admin ^| all (default: all) +echo --service SVC api ^| player ^| admin ^| api-admin ^| full-themes ^| all (default: all) echo --tag TAG Image tag (default: latest) echo --use-cache Use Docker build cache echo --export-only Skip build, export existing images only @@ -117,8 +117,7 @@ if errorlevel 1 exit /b 1 if not defined OUTPUT call :default_output set "OUTPUT_PATH=%OUTPUT%" -echo %OUTPUT_PATH%| findstr /r "^[A-Za-z]:\\" >nul -if errorlevel 1 set "OUTPUT_PATH=%CD%\%OUTPUT%" +if /i not "%OUTPUT:~1,1%"==":" set "OUTPUT_PATH=%CD%\%OUTPUT%" where docker >nul 2>&1 if errorlevel 1 ( @@ -126,7 +125,7 @@ if errorlevel 1 ( exit /b 1 ) -if "%EXPORT_ONLY%"=="0" goto do_build +if "%EXPORT_ONLY%"=="0" if /i not "%SERVICE%"=="full-themes" goto do_build goto do_export :do_build @@ -135,6 +134,8 @@ set "IMAGE_TAG=%TAG%" if "%NO_CACHE%"=="1" goto build_no_cache if /i "%SERVICE%"=="all" ( docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build api player admin +) else if /i "%SERVICE%"=="api-admin" ( + docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build api admin ) else ( docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build %SERVICE% ) @@ -143,6 +144,8 @@ goto build_done :build_no_cache if /i "%SERVICE%"=="all" ( docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build --no-cache api player admin +) else if /i "%SERVICE%"=="api-admin" ( + docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build --no-cache api admin ) else ( docker compose -f "%COMPOSE_FILE%" --env-file "%ENV_FILE%" build --no-cache %SERVICE% ) @@ -157,6 +160,10 @@ if errorlevel 1 ( echo ==^> Exporting %SERVICE% to %OUTPUT_PATH% if /i "%SERVICE%"=="all" ( docker save thebet365-api:%TAG% thebet365-player:%TAG% thebet365-admin:%TAG% -o "%OUTPUT_PATH%" +) else if /i "%SERVICE%"=="api-admin" ( + docker save thebet365-api:%TAG% thebet365-admin:%TAG% -o "%OUTPUT_PATH%" +) else if /i "%SERVICE%"=="full-themes" ( + docker save thebet365-api:%TAG% thebet365-admin:%TAG% thebet365-player:main thebet365-player:theme-2 thebet365-player:theme-3 thebet365-player:theme-4 -o "%OUTPUT_PATH%" ) else ( docker save thebet365-%SERVICE%:%TAG% -o "%OUTPUT_PATH%" ) @@ -187,16 +194,34 @@ for %%F in ("%OUTPUT_PATH%") do set /a SIZE_MB=%%~zF/1048576 echo git_dirty=!GIT_DIRTY! echo tar=!TAR_NAME! echo tar_sha256=!TAR_SHA256! - if /i not "!SERVICE!"=="all" ( - echo image=thebet365-!SERVICE!:%TAG% - echo image_id=!SVC_IMAGE_ID! - ) else ( + if /i "!SERVICE!"=="all" ( echo api_image=thebet365-api:%TAG% echo api_image_id=!API_IMAGE_ID! echo player_image=thebet365-player:%TAG% echo player_image_id=!PLAYER_IMAGE_ID! echo admin_image=thebet365-admin:%TAG% echo admin_image_id=!ADMIN_IMAGE_ID! + ) else if /i "!SERVICE!"=="api-admin" ( + echo api_image=thebet365-api:%TAG% + echo api_image_id=!API_IMAGE_ID! + echo admin_image=thebet365-admin:%TAG% + echo admin_image_id=!ADMIN_IMAGE_ID! + ) else if /i "!SERVICE!"=="full-themes" ( + echo api_image=thebet365-api:%TAG% + echo api_image_id=!API_IMAGE_ID! + echo admin_image=thebet365-admin:%TAG% + echo admin_image_id=!ADMIN_IMAGE_ID! + echo player_main_image=thebet365-player:main + echo player_main_image_id=!PLAYER_MAIN_IMAGE_ID! + echo player_theme_2_image=thebet365-player:theme-2 + echo player_theme_2_image_id=!PLAYER_THEME2_IMAGE_ID! + echo player_theme_3_image=thebet365-player:theme-3 + echo player_theme_3_image_id=!PLAYER_THEME3_IMAGE_ID! + echo player_theme_4_image=thebet365-player:theme-4 + echo player_theme_4_image_id=!PLAYER_THEME4_IMAGE_ID! + ) else ( + echo image=thebet365-!SERVICE!:%TAG% + echo image_id=!SVC_IMAGE_ID! ) ) @@ -207,6 +232,12 @@ echo Server load and recreate: if /i "%SERVICE%"=="all" ( echo docker load -i !TAR_NAME! echo docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate api player admin +) else if /i "%SERVICE%"=="api-admin" ( + echo docker load -i !TAR_NAME! + echo docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate api admin +) else if /i "%SERVICE%"=="full-themes" ( + echo docker load -i !TAR_NAME! + echo docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d ) else ( echo docker load -i !TAR_NAME! echo docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate %SERVICE% @@ -223,6 +254,10 @@ exit /b 0 :default_output if /i "%SERVICE%"=="all" ( set "OUTPUT=thebet365-images-%TAG%.tar" +) else if /i "%SERVICE%"=="api-admin" ( + set "OUTPUT=thebet365-images-%TAG%.tar" +) else if /i "%SERVICE%"=="full-themes" ( + set "OUTPUT=thebet365-full-themes-%TAG%.tar" ) else ( set "OUTPUT=thebet365-%SERVICE%-%TAG%.tar" ) @@ -230,10 +265,12 @@ exit /b 0 :validate_service if /i "%SERVICE%"=="all" exit /b 0 +if /i "%SERVICE%"=="api-admin" exit /b 0 +if /i "%SERVICE%"=="full-themes" exit /b 0 if /i "%SERVICE%"=="api" exit /b 0 if /i "%SERVICE%"=="player" exit /b 0 if /i "%SERVICE%"=="admin" exit /b 0 -echo ERROR: invalid --service: %SERVICE% (use api, player, admin, or all) +echo ERROR: invalid --service: %SERVICE% (use api, player, admin, api-admin, full-themes, or all) echo TIP: 四套主题一键打包请使用 docs\docker\build-and-export-all-themes.bat exit /b 1 @@ -273,10 +310,19 @@ set "API_IMAGE_ID=" set "PLAYER_IMAGE_ID=" set "ADMIN_IMAGE_ID=" set "SVC_IMAGE_ID=" +set "PLAYER_MAIN_IMAGE_ID=" +set "PLAYER_THEME2_IMAGE_ID=" +set "PLAYER_THEME3_IMAGE_ID=" +set "PLAYER_THEME4_IMAGE_ID=" for /f "delims=" %%I in ('docker image inspect thebet365-api:%TAG% --format "{{.Id}}" 2^>nul') do set "API_IMAGE_ID=%%I" for /f "delims=" %%I in ('docker image inspect thebet365-player:%TAG% --format "{{.Id}}" 2^>nul') do set "PLAYER_IMAGE_ID=%%I" for /f "delims=" %%I in ('docker image inspect thebet365-admin:%TAG% --format "{{.Id}}" 2^>nul') do set "ADMIN_IMAGE_ID=%%I" -if /i not "%SERVICE%"=="all" ( +if /i "%SERVICE%"=="full-themes" ( + for /f "delims=" %%I in ('docker image inspect thebet365-player:main --format "{{.Id}}" 2^>nul') do set "PLAYER_MAIN_IMAGE_ID=%%I" + for /f "delims=" %%I in ('docker image inspect thebet365-player:theme-2 --format "{{.Id}}" 2^>nul') do set "PLAYER_THEME2_IMAGE_ID=%%I" + for /f "delims=" %%I in ('docker image inspect thebet365-player:theme-3 --format "{{.Id}}" 2^>nul') do set "PLAYER_THEME3_IMAGE_ID=%%I" + for /f "delims=" %%I in ('docker image inspect thebet365-player:theme-4 --format "{{.Id}}" 2^>nul') do set "PLAYER_THEME4_IMAGE_ID=%%I" +) else if /i not "%SERVICE%"=="all" ( for /f "delims=" %%I in ('docker image inspect thebet365-%SERVICE%:%TAG% --format "{{.Id}}" 2^>nul') do set "SVC_IMAGE_ID=%%I" ) exit /b 0 diff --git a/docs/docker/build-and-export-images.ps1 b/docs/docker/build-and-export-images.ps1 index a87be55..c8d00d9 100644 --- a/docs/docker/build-and-export-images.ps1 +++ b/docs/docker/build-and-export-images.ps1 @@ -1,17 +1,17 @@ -# 构建 api / player / admin 生产镜像并导出为 tar -# 用法(在项目根目录或本目录执行均可): +# ?? api / player / admin ???????? tar +# ??????????????????: # .\docs\docker\build-and-export-images.ps1 # .\docs\docker\build-and-export-images.ps1 -Service admin # .\docs\docker\build-and-export-images.ps1 -Tag v1.2.3 # .\docs\docker\build-and-export-images.ps1 -UseCache # .\docs\docker\build-and-export-images.ps1 -ExportOnly # -# ҶһԶз֧Ƽ +# ???????????????????????????????????? # docs\docker\build-and-export-all-themes.bat param( - [ValidateSet("all", "api", "player", "admin")] + [ValidateSet("all", "api", "player", "admin", "api-admin")] [string]$Service = "all", [string]$Tag = $env:IMAGE_TAG, [switch]$UseCache, @@ -31,27 +31,27 @@ function Get-DefaultTag { "latest" } function Get-DefaultOutput { param([string]$Svc, [string]$ImageTag) - if ($Svc -eq "all") { return "thebet365-images-$ImageTag.tar" } + if ($Svc -eq "all" -or $Svc -eq "api-admin") { return "thebet365-images-$ImageTag.tar" } return "thebet365-$Svc-$ImageTag.tar" } function Assert-ImageTag { param([string]$Value) if ($Value -notmatch '^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$') { - throw "镜像 tag 不合? $Value" + throw "?? tag ???? $Value" } } if (-not (Test-Path $ComposeFile)) { - throw "未找?$ComposeFile(当前目? $Root? + throw "????$ComposeFile?????? $Root?? } if (-not (Test-Path $EnvFile)) { if (Test-Path ".env.docker.example") { - Write-Warning "未找?.env.docker,使?.env.docker.example(生产请复制并修改密钥)" + Write-Warning "????.env.docker????.env.docker.example????????????" $EnvFile = ".env.docker.example" } else { - throw "未找?.env.docker ?.env.docker.example" + throw "????.env.docker ??.env.docker.example" } } @@ -60,7 +60,11 @@ if ([string]::IsNullOrWhiteSpace($Tag)) { } Assert-ImageTag $Tag -$BuildServices = if ($Service -eq "all") { $AllServices } else { @($Service) } +$BuildServices = switch ($Service) { + "all" { $AllServices } + "api-admin" { @("api", "admin") } + default { @($Service) } +} $SaveImages = $BuildServices | ForEach-Object { "thebet365-${_}:${Tag}" } if ([string]::IsNullOrWhiteSpace($Output)) { @@ -68,7 +72,7 @@ if ([string]::IsNullOrWhiteSpace($Output)) { } if (-not $ExportOnly) { - Write-Host "==> 构建镜像: $($BuildServices -join ', ') (tag: $Tag)" + Write-Host "==> ????: $($BuildServices -join ', ') (tag: $Tag)" $buildArgs = @( "compose", "-f", $ComposeFile, "--env-file", $EnvFile, "build" ) @@ -80,7 +84,7 @@ if (-not $ExportOnly) { try { $env:IMAGE_TAG = $Tag & docker @buildArgs - if ($LASTEXITCODE -ne 0) { throw "docker build 失败,退出码 $LASTEXITCODE" } + if ($LASTEXITCODE -ne 0) { throw "docker build ?????? $LASTEXITCODE" } } finally { $env:IMAGE_TAG = $oldImageTag } @@ -88,9 +92,9 @@ if (-not $ExportOnly) { $OutputPath = if ([System.IO.Path]::IsPathRooted($Output)) { $Output } else { Join-Path $Root $Output } -Write-Host "==> 导出镜像 ($Service) -> $OutputPath" +Write-Host "==> ???? ($Service) -> $OutputPath" & docker save @SaveImages -o $OutputPath -if ($LASTEXITCODE -ne 0) { throw "docker save 失败,退出码 $LASTEXITCODE" } +if ($LASTEXITCODE -ne 0) { throw "docker save ?????? $LASTEXITCODE" } $manifestPath = if ($OutputPath.EndsWith(".tar")) { $OutputPath.Substring(0, $OutputPath.Length - 4) + ".manifest.txt" @@ -121,7 +125,7 @@ $manifestLines = @( ) foreach ($svc in $BuildServices) { $imageId = (& docker image inspect "thebet365-${svc}:${Tag}" --format "{{.Id}}").Trim() - if ($Service -eq "all") { + if ($Service -eq "all" -or $Service -eq "api-admin") { $manifestLines += "${svc}_image=thebet365-${svc}:${Tag}" $manifestLines += "${svc}_image_id=$imageId" } else { @@ -132,13 +136,17 @@ foreach ($svc in $BuildServices) { $manifestLines | Set-Content -Encoding UTF8 $manifestPath $sizeMb = [math]::Round((Get-Item $OutputPath).Length / 1MB, 2) -Write-Host "完成: $OutputPath (${sizeMb} MB)" +Write-Host "??: $OutputPath (${sizeMb} MB)" Write-Host "Manifest: $manifestPath" -$recreate = if ($Service -eq "all") { "api player admin" } else { $Service } +$recreate = switch ($Service) { + "all" { "api player admin" } + "api-admin" { "api admin" } + default { $Service } +} Write-Host @" -服务器导入并重建: +????????: docker load -i $([System.IO.Path]::GetFileName($OutputPath)) docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate $recreate "@ diff --git a/docs/docker/build-and-export-images.sh b/docs/docker/build-and-export-images.sh index 39c9d63..5b04613 100755 --- a/docs/docker/build-and-export-images.sh +++ b/docs/docker/build-and-export-images.sh @@ -25,7 +25,7 @@ default_tag() { echo "latest"; } default_output() { local svc="$1" image_tag="$2" - if [[ "$svc" == "all" ]]; then + if [[ "$svc" == "all" || "$svc" == "api-admin" ]]; then echo "thebet365-images-${image_tag}.tar" else echo "thebet365-${svc}-${image_tag}.tar" @@ -39,8 +39,8 @@ validate_tag() { validate_service() { case "$1" in - all|api|player|admin) ;; - *) echo "错误: --service 无效: $1(可选 api / player / admin / all)" >&2; exit 1 ;; + all|api|player|admin|api-admin) ;; + *) echo "错误: --service 无效: $1(可选 api / player / admin / api-admin / all)" >&2; exit 1 ;; esac } @@ -49,7 +49,7 @@ usage() { 用法: docs/docker/build-and-export-images.sh [选项] 选项: - --service SVC api | player | admin | all(默认 all) + --service SVC api | player | admin | api-admin | all(默认 all) --tag TAG 镜像 tag(默认 latest) --use-cache 构建时使用 Docker 缓存(默认 --no-cache) --export-only 跳过构建,仅导出已有指定 tag 镜像 @@ -105,6 +105,8 @@ OUTPUT="${OUTPUT:-$(default_output "$SERVICE" "$TAG")}" if [[ "$SERVICE" == "all" ]]; then BUILD_SERVICES=(api player admin) +elif [[ "$SERVICE" == "api-admin" ]]; then + BUILD_SERVICES=(api admin) else BUILD_SERVICES=("$SERVICE") fi @@ -161,7 +163,7 @@ fi echo "tar=$(basename "$OUTPUT_PATH")" echo "tar_sha256=$CHECKSUM" for svc in "${BUILD_SERVICES[@]}"; do - if [[ "$SERVICE" == "all" ]]; then + if [[ "$SERVICE" == "all" || "$SERVICE" == "api-admin" ]]; then echo "${svc}_image=thebet365-${svc}:$TAG" echo "${svc}_image_id=$(docker image inspect "thebet365-${svc}:$TAG" --format '{{.Id}}')" else @@ -182,6 +184,8 @@ echo "Manifest: $MANIFEST_PATH" RECREATE="$SERVICE" if [[ "$SERVICE" == "all" ]]; then RECREATE="api player admin" +elif [[ "$SERVICE" == "api-admin" ]]; then + RECREATE="api admin" fi cat < **为何用 PowerShell?** 本脚本仅存在于 `main` 分支;执行过程中会 `git checkout theme-*`,CMD 批处理会从磁盘逐行读取,切分支后脚本文件消失会导致中断。PowerShell 启动时已将整份脚本载入内存,可安全跨分支执行。`.bat` 仅为薄包装,内部转调 `.ps1`。 + +```powershell cd C:\path\to\thebet365 +.\docs\docker\build-and-export-all-themes.ps1 +``` + +或双击 / CMD: + +```bat docs\docker\build-and-export-all-themes.bat ``` -可选参数: +可选参数(PowerShell 与 bat 均支持): | 参数 | 说明 | |------|------| -| `--use-cache` | 使用 Docker 层缓存,加快重复构建速度 | -| `--export-only` | 跳过构建,仅导出本地已有镜像 | -| `--skip-api-admin` | 只打四套 player,跳过 api/admin | +| `-UseCache` / `--use-cache` | 使用 Docker 层缓存,加快重复构建速度 | +| `-ExportOnly` / `--export-only` | 跳过构建,仅导出本地已有镜像 | +| `-SkipApiAdmin` / `--skip-api-admin` | 只打四套 player,跳过 api/admin | +| `-SkipBundle` / `--skip-bundle` | 跳过六合一包,仅保留分散 tar | ### 产物 @@ -153,6 +163,13 @@ docs\docker\build-and-export-all-themes.bat | `thebet365-player-theme-3.tar` | `thebet365-player:theme-3` | | `thebet365-player-theme-4.tar` | `thebet365-player:theme-4` | | `thebet365-images-latest.tar` | `thebet365-api:latest` + `thebet365-admin:latest` | +| **`thebet365-full-themes-latest.tar`** | **上面全部 6 个镜像(上传这一个即可)** | + +也可单独导出六合一包(本地镜像已齐时): + +```bat +docs\docker\build-and-export-images.bat --service full-themes --export-only --tag latest +``` ### 分支要求 diff --git a/docs/四套主题Docker部署任务.md b/docs/四套主题Docker部署任务.md index d0f0ffa..a1032e1 100644 --- a/docs/四套主题Docker部署任务.md +++ b/docs/四套主题Docker部署任务.md @@ -276,7 +276,18 @@ pnpm dev:player # http://localhost:5173 目视确认仍是本主题皮 ## 六、构建与上传镜像 -### 6.1 四个 player 镜像(Windows CMD) +### 6.1 四个 player 镜像 + +**一键打包(推荐,仅 `main` 分支提供脚本):** + +```powershell +cd C:\path\to\thebet365 +.\docs\docker\build-and-export-all-themes.ps1 -SkipApiAdmin +``` + +或 `docs\docker\build-and-export-all-themes.bat --skip-api-admin`(内部转调 PowerShell,避免切分支后 bat 从磁盘消失)。 + +**手动逐步(Windows CMD):** ```bat cd C:\path\to\thebet365