fix(docker): multi-theme packaging with PS1 orchestration and full bundle export

Replace all-themes bat logic with PowerShell to survive branch checkout; add api-admin and full-themes service modes; fix bat findstr path check and cmd exit codes; document six-in-one tar and update gitignore for player/full-themes artifacts.
This commit is contained in:
2026-06-24 14:18:31 +08:00
parent 25dd0cf514
commit 9c5e8d6f5c
8 changed files with 425 additions and 298 deletions

4
.gitignore vendored
View File

@@ -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/

View File

@@ -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 ^<branch^>
echo docs\docker\build-and-export-images.bat --service player --tag ^<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 <branch> <tag>
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%

View File

@@ -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 <branch>"
Write-Host " docs\docker\build-and-export-images.bat --service player --tag <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

View File

@@ -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

View File

@@ -1,17 +1,17 @@
# 鏋勫缓 api / player / admin 鐢熶骇闀滃儚骞跺<EFBFBD>鍑轰负 tar
# 鐢ㄦ硶锛堝湪椤圭洰鏍圭洰褰曟垨鏈<EFBFBD>洰褰曟墽琛屽潎鍙<EFBFBD>:
# ?? 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 "<EFBFBD>壘鍒?$ComposeFile锛堝綋鍓嶇洰褰? $Root?
throw "????$ComposeFile?????? $Root??
}
if (-not (Test-Path $EnvFile)) {
if (Test-Path ".env.docker.example") {
Write-Warning "<EFBFBD>壘鍒?.env.docker锛屼娇鐢?.env.docker.example锛堢敓浜ц<EFBFBD>澶嶅埗骞朵慨鏀瑰瘑閽ワ級"
Write-Warning "????.env.docker????.env.docker.example????????????"
$EnvFile = ".env.docker.example"
} else {
throw "<EFBFBD>壘鍒?.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 @"
鏈嶅姟鍣ㄥ<EFBFBD>鍏ュ苟閲嶅缓:
????????:
docker load -i $([System.IO.Path]::GetFileName($OutputPath))
docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate $recreate
"@

View File

@@ -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 <<EOF

View File

@@ -20,7 +20,8 @@
| `docs/docker/build-and-export-player-theme-3.bat` | Windows仅 theme-3 移动主题,自动切分支) |
| `docs/docker/build-and-export-player-theme-4.bat` | Windows仅 theme-4 海军蓝极简,自动切分支) |
| `docs/docker/build-and-export-admin.bat` | Windows仅 admin可双击 |
| `docs/docker/build-and-export-all-themes.bat` | Windows**四套主题 player 一键打包**,自动切分支) |
| `docs/docker/build-and-export-all-themes.ps1` | Windows**四套主题 player 一键打包**,自动切分支,推荐 |
| `docs/docker/build-and-export-all-themes.bat` | Windows同上薄包装启动器双击可用 |
| `docs/docker/build-and-export-images.ps1` | WindowsPowerShell |
| `docs/docker/build-and-export-images.sh` | Linux / macOS / Git Bash |
@@ -131,18 +132,27 @@ docs\docker\build-and-export-images.bat --export-only
脚本会自动:切到各主题分支 → 构建 player 镜像 → 导出 tar → 切回原分支 → 构建 api/admin。
```bat
> **为何用 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
```
### 分支要求

View File

@@ -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