All checks were successful
Publish FaceAI Container / publish (push) Successful in 6m52s
- Updated Dockerfile to include default MySQL client for better database interaction. - Modified entrypoint.sh to support additional workspace for legacy applications and added MySQL readiness check before startup. - Enhanced PowerShell script for trimming MySQL dumps to include overlay dumps and improved error handling for missing race and user IDs. - Added new image files and face encoding pickles for various projects, ensuring comprehensive data availability. - Removed outdated face encoding pickle from PISA directory to maintain data relevance. Co-authored-by: Copilot <copilot@github.com>
408 lines
No EOL
15 KiB
PowerShell
408 lines
No EOL
15 KiB
PowerShell
param(
|
|
[string]$SourceDump = (Join-Path $PSScriptRoot '..\db\pg-model-seed-trimmed-20260421.sql'),
|
|
[string]$OverlayDump = (Join-Path $PSScriptRoot '..\db\pg-local-model-fixtures-overlay-20260422.sql'),
|
|
[string]$OutputDump = (Join-Path $PSScriptRoot '..\db\pg-local-purpose-seed-20260422.sql'),
|
|
[int[]]$KeepRaceIds = @(1018547, 1018557),
|
|
[int[]]$KeepUserIds = @(2),
|
|
[string]$ContainerName = 'regalami-dump-trim-mysql-temp',
|
|
[string]$VolumeName = 'regalami-dump-trim-mysql-temp-data',
|
|
[string]$DatabaseName = 'pgtrim',
|
|
[string]$RootPassword = 'root'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Invoke-DockerCapture {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$DockerArgs
|
|
)
|
|
|
|
$output = & docker @DockerArgs 2>&1
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw (("docker " + ($DockerArgs -join ' ')) + " failed:`n" + ($output -join "`n"))
|
|
}
|
|
|
|
return $output
|
|
}
|
|
|
|
function Invoke-DockerQuiet {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$DockerArgs
|
|
)
|
|
|
|
& docker @DockerArgs | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "docker $($DockerArgs -join ' ') failed"
|
|
}
|
|
}
|
|
|
|
function Wait-ForMysqlReady {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Password
|
|
)
|
|
|
|
for ($attempt = 0; $attempt -lt 180; $attempt++) {
|
|
$logs = & docker logs $Name 2>&1
|
|
if ($LASTEXITCODE -eq 0 -and ($logs -join "`n") -match 'ready for connections.*port: 3306') {
|
|
return
|
|
}
|
|
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
|
|
throw "MySQL in container $Name did not become ready in time."
|
|
}
|
|
|
|
function Invoke-MysqlQuery {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Query,
|
|
[switch]$SkipDatabase
|
|
)
|
|
|
|
$dockerArgs = @('exec', '-e', "MYSQL_PWD=$RootPassword", $ContainerName, 'mysql', '-N', '-B', '-uroot')
|
|
if (-not $SkipDatabase) {
|
|
$dockerArgs += @('-D', $DatabaseName)
|
|
}
|
|
$dockerArgs += @('-e', $Query)
|
|
return Invoke-DockerCapture -DockerArgs $dockerArgs
|
|
}
|
|
|
|
function Get-SqlScalar {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Query,
|
|
[switch]$SkipDatabase
|
|
)
|
|
|
|
$result = Invoke-MysqlQuery -Query $Query -SkipDatabase:$SkipDatabase
|
|
if (-not $result) {
|
|
return ''
|
|
}
|
|
|
|
return ($result | Select-Object -First 1).Trim()
|
|
}
|
|
|
|
function Quote-Identifier {
|
|
param([string]$Name)
|
|
return '`' + $Name.Replace('`', '``') + '`'
|
|
}
|
|
|
|
function ConvertTo-SqlIntList {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[int[]]$Values
|
|
)
|
|
|
|
$dedupedValues = $Values | Sort-Object -Unique
|
|
if (-not $dedupedValues -or $dedupedValues.Count -eq 0) {
|
|
throw 'At least one numeric keep id is required.'
|
|
}
|
|
|
|
return ($dedupedValues | ForEach-Object { [string]$_ }) -join ','
|
|
}
|
|
|
|
if (-not (Test-Path $SourceDump)) {
|
|
throw "Source dump not found: $SourceDump"
|
|
}
|
|
|
|
$dbDirectory = Split-Path -Parent (Resolve-Path $SourceDump).Path
|
|
$sourceFileName = Split-Path -Leaf $SourceDump
|
|
$outputFileName = Split-Path -Leaf $OutputDump
|
|
$overlayResolvedPath = $null
|
|
$overlayFileName = ''
|
|
$overlayDirectory = $dbDirectory
|
|
$overlayMount = @()
|
|
if ($OverlayDump -and (Test-Path $OverlayDump)) {
|
|
$overlayResolvedPath = (Resolve-Path $OverlayDump).Path
|
|
$overlayFileName = Split-Path -Leaf $overlayResolvedPath
|
|
$overlayDirectory = Split-Path -Parent $overlayResolvedPath
|
|
if ($overlayDirectory -ne $dbDirectory) {
|
|
$overlayMount = @('-v', "${overlayDirectory}:/workspace/overlay")
|
|
}
|
|
}
|
|
$importErrorLogFileName = [System.IO.Path]::GetFileNameWithoutExtension($sourceFileName) + '.import-errors.log'
|
|
$importErrorLogPath = Join-Path $dbDirectory $importErrorLogFileName
|
|
$sourceSizeBytes = (Get-Item $SourceDump).Length
|
|
$keepRaceListSql = ConvertTo-SqlIntList -Values $KeepRaceIds
|
|
$keepUserListSql = ConvertTo-SqlIntList -Values $KeepUserIds
|
|
|
|
Write-Host "Using source dump: $SourceDump"
|
|
Write-Host "Source size: $sourceSizeBytes bytes"
|
|
Write-Host "Import error log: $importErrorLogPath"
|
|
if ($overlayResolvedPath) {
|
|
Write-Host "Using overlay dump: $overlayResolvedPath"
|
|
}
|
|
Write-Host "KeepRaceIdsRequested=$keepRaceListSql"
|
|
Write-Host "KeepUserIdsRequested=$keepUserListSql"
|
|
|
|
try {
|
|
& docker rm -f $ContainerName 1>$null 2>$null
|
|
& docker volume rm $VolumeName 1>$null 2>$null
|
|
|
|
$dockerRunArgs = @(
|
|
'run', '-d', '--name', $ContainerName,
|
|
'-e', "MYSQL_ROOT_PASSWORD=$RootPassword",
|
|
'-e', 'MYSQL_ROOT_HOST=%',
|
|
'-v', "${dbDirectory}:/workspace/db",
|
|
'-v', "${VolumeName}:/var/lib/mysql"
|
|
)
|
|
if ($overlayMount.Count -gt 0) {
|
|
$dockerRunArgs += $overlayMount
|
|
}
|
|
$dockerRunArgs += @(
|
|
'mysql:8.4',
|
|
'--max_allowed_packet=1G',
|
|
'--net_read_timeout=600',
|
|
'--net_write_timeout=600'
|
|
)
|
|
|
|
Invoke-DockerCapture -DockerArgs $dockerRunArgs | Out-Null
|
|
Write-Host 'Temporary MySQL container started.'
|
|
|
|
Wait-ForMysqlReady -Name $ContainerName -Password $RootPassword
|
|
Write-Host 'Temporary MySQL is ready.'
|
|
|
|
Invoke-MysqlQuery -SkipDatabase -Query "DROP DATABASE IF EXISTS $DatabaseName; CREATE DATABASE $DatabaseName CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;" | Out-Null
|
|
Write-Host 'Temporary database created.'
|
|
|
|
Write-Host "Importing source dump into temporary MySQL database..."
|
|
if (Test-Path $importErrorLogPath) {
|
|
Remove-Item $importErrorLogPath -Force
|
|
}
|
|
Invoke-DockerQuiet -DockerArgs @(
|
|
'exec', $ContainerName, 'sh', '-lc',
|
|
"mysql --force -uroot -p$RootPassword $DatabaseName < /workspace/db/$sourceFileName 2> /workspace/db/$importErrorLogFileName"
|
|
)
|
|
|
|
if ($overlayResolvedPath) {
|
|
$overlayContainerPath = if ($overlayDirectory -eq $dbDirectory) {
|
|
"/workspace/db/$overlayFileName"
|
|
}
|
|
else {
|
|
"/workspace/overlay/$overlayFileName"
|
|
}
|
|
|
|
Write-Host "Importing overlay dump into temporary MySQL database..."
|
|
Invoke-DockerQuiet -DockerArgs @(
|
|
'exec', $ContainerName, 'sh', '-lc',
|
|
"mysql --force -uroot -p$RootPassword $DatabaseName < $overlayContainerPath"
|
|
)
|
|
}
|
|
|
|
$beforeGara = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM gara;')
|
|
$beforePuntoFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM punto_foto;')
|
|
$beforeFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM foto;')
|
|
$beforeLogFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM log_foto;')
|
|
$usersExists = (Get-SqlScalar -SkipDatabase -Query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '$DatabaseName' AND table_name = 'users';") -eq '1'
|
|
$beforeUsers = 0
|
|
if ($usersExists) {
|
|
$beforeUsers = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM users;')
|
|
}
|
|
|
|
Invoke-MysqlQuery -Query @"
|
|
DROP TABLE IF EXISTS keep_gara_ids;
|
|
CREATE TABLE keep_gara_ids AS
|
|
SELECT id_gara
|
|
FROM gara
|
|
WHERE id_gara IN ($keepRaceListSql)
|
|
ORDER BY FIELD(id_gara, $keepRaceListSql);
|
|
|
|
DROP TABLE IF EXISTS keep_punto_foto_ids;
|
|
CREATE TABLE keep_punto_foto_ids AS
|
|
SELECT id_puntoFoto
|
|
FROM punto_foto
|
|
WHERE id_gara IN (SELECT id_gara FROM keep_gara_ids);
|
|
|
|
DROP TABLE IF EXISTS keep_foto_ids;
|
|
CREATE TABLE keep_foto_ids AS
|
|
SELECT id_foto
|
|
FROM foto
|
|
WHERE id_gara IN (SELECT id_gara FROM keep_gara_ids);
|
|
"@ | Out-Null
|
|
|
|
$keptGaraIds = Invoke-MysqlQuery -Query 'SELECT id_gara FROM keep_gara_ids ORDER BY id_gara;'
|
|
$missingRaceIds = @()
|
|
foreach ($requestedRaceId in ($KeepRaceIds | Sort-Object -Unique)) {
|
|
if (-not (($keptGaraIds | ForEach-Object { $_.Trim() }) -contains [string]$requestedRaceId)) {
|
|
$missingRaceIds += $requestedRaceId
|
|
}
|
|
}
|
|
if ($missingRaceIds.Count -gt 0) {
|
|
throw "One or more requested race ids are missing from the imported data: $($missingRaceIds -join ',')"
|
|
}
|
|
|
|
$cleanupStats = [System.Collections.Generic.List[string]]::new()
|
|
|
|
Invoke-MysqlQuery -Query 'DELETE FROM log_foto;' | Out-Null
|
|
$cleanupStats.Add('log_foto:deleted-all')
|
|
|
|
$garaDependentTables = Invoke-MysqlQuery -SkipDatabase -Query @"
|
|
SELECT table_name
|
|
FROM information_schema.columns
|
|
WHERE table_schema = '$DatabaseName'
|
|
AND column_name = 'id_gara'
|
|
AND table_name NOT IN ('gara', 'foto', 'punto_foto', 'keep_gara_ids', 'keep_punto_foto_ids', 'keep_foto_ids', 'keep_user_ids')
|
|
ORDER BY table_name;
|
|
"@
|
|
|
|
foreach ($tableName in $garaDependentTables) {
|
|
if (-not $tableName) {
|
|
continue
|
|
}
|
|
$trimmedTableName = $tableName.Trim()
|
|
$quotedTable = Quote-Identifier $trimmedTableName
|
|
$deletedRows = [int64](Get-SqlScalar -Query "SELECT COUNT(*) FROM $quotedTable WHERE id_gara IS NOT NULL AND id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);")
|
|
if ($deletedRows -gt 0) {
|
|
Invoke-MysqlQuery -Query "DELETE FROM $quotedTable WHERE id_gara IS NOT NULL AND id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);" | Out-Null
|
|
$cleanupStats.Add("$($trimmedTableName):$deletedRows")
|
|
}
|
|
}
|
|
|
|
$fotoDependentTables = Invoke-MysqlQuery -SkipDatabase -Query @"
|
|
SELECT table_name
|
|
FROM information_schema.columns
|
|
WHERE table_schema = '$DatabaseName'
|
|
AND column_name = 'id_foto'
|
|
AND table_name NOT IN ('foto', 'log_foto', 'keep_foto_ids', 'keep_gara_ids', 'keep_punto_foto_ids', 'keep_user_ids')
|
|
ORDER BY table_name;
|
|
"@
|
|
|
|
foreach ($tableName in $fotoDependentTables) {
|
|
if (-not $tableName) {
|
|
continue
|
|
}
|
|
$trimmedTableName = $tableName.Trim()
|
|
$quotedTable = Quote-Identifier $trimmedTableName
|
|
$deletedRows = [int64](Get-SqlScalar -Query "SELECT COUNT(*) FROM $quotedTable WHERE id_foto IS NOT NULL AND id_foto NOT IN (SELECT id_foto FROM keep_foto_ids);")
|
|
if ($deletedRows -gt 0) {
|
|
Invoke-MysqlQuery -Query "DELETE FROM $quotedTable WHERE id_foto IS NOT NULL AND id_foto NOT IN (SELECT id_foto FROM keep_foto_ids);" | Out-Null
|
|
$cleanupStats.Add("$($trimmedTableName):$deletedRows")
|
|
}
|
|
}
|
|
|
|
$deletedFotoRows = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM foto WHERE id_foto NOT IN (SELECT id_foto FROM keep_foto_ids);')
|
|
if ($deletedFotoRows -gt 0) {
|
|
Invoke-MysqlQuery -Query 'DELETE FROM foto WHERE id_foto NOT IN (SELECT id_foto FROM keep_foto_ids);' | Out-Null
|
|
$cleanupStats.Add("foto:$deletedFotoRows")
|
|
}
|
|
|
|
$deletedPuntoFotoRows = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM punto_foto WHERE id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);')
|
|
if ($deletedPuntoFotoRows -gt 0) {
|
|
Invoke-MysqlQuery -Query 'DELETE FROM punto_foto WHERE id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);' | Out-Null
|
|
$cleanupStats.Add("punto_foto:$deletedPuntoFotoRows")
|
|
}
|
|
|
|
$deletedGaraRows = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM gara WHERE id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);')
|
|
if ($deletedGaraRows -gt 0) {
|
|
Invoke-MysqlQuery -Query 'DELETE FROM gara WHERE id_gara NOT IN (SELECT id_gara FROM keep_gara_ids);' | Out-Null
|
|
$cleanupStats.Add("gara:$deletedGaraRows")
|
|
}
|
|
|
|
$keptUserIds = @()
|
|
if ($usersExists) {
|
|
Invoke-MysqlQuery -Query @"
|
|
DROP TABLE IF EXISTS keep_user_ids;
|
|
CREATE TABLE keep_user_ids AS
|
|
SELECT id_users
|
|
FROM users
|
|
WHERE id_users IN ($keepUserListSql)
|
|
ORDER BY FIELD(id_users, $keepUserListSql);
|
|
"@ | Out-Null
|
|
|
|
$keptUserIds = Invoke-MysqlQuery -Query 'SELECT id_users FROM keep_user_ids ORDER BY id_users;'
|
|
$missingUserIds = @()
|
|
foreach ($requestedUserId in ($KeepUserIds | Sort-Object -Unique)) {
|
|
if (-not (($keptUserIds | ForEach-Object { $_.Trim() }) -contains [string]$requestedUserId)) {
|
|
$missingUserIds += $requestedUserId
|
|
}
|
|
}
|
|
if ($missingUserIds.Count -gt 0) {
|
|
throw "One or more requested user ids are missing from the imported data: $($missingUserIds -join ',')"
|
|
}
|
|
|
|
$usersDependentTables = Invoke-MysqlQuery -SkipDatabase -Query @"
|
|
SELECT table_name
|
|
FROM information_schema.columns
|
|
WHERE table_schema = '$DatabaseName'
|
|
AND column_name = 'id_users'
|
|
AND table_name NOT IN ('users', 'keep_user_ids', 'keep_gara_ids', 'keep_punto_foto_ids', 'keep_foto_ids')
|
|
ORDER BY table_name;
|
|
"@
|
|
|
|
foreach ($tableName in $usersDependentTables) {
|
|
if (-not $tableName) {
|
|
continue
|
|
}
|
|
$trimmedTableName = $tableName.Trim()
|
|
$quotedTable = Quote-Identifier $trimmedTableName
|
|
$deletedRows = [int64](Get-SqlScalar -Query "SELECT COUNT(*) FROM $quotedTable WHERE id_users IS NOT NULL AND id_users NOT IN (SELECT id_users FROM keep_user_ids);")
|
|
if ($deletedRows -gt 0) {
|
|
Invoke-MysqlQuery -Query "DELETE FROM $quotedTable WHERE id_users IS NOT NULL AND id_users NOT IN (SELECT id_users FROM keep_user_ids);" | Out-Null
|
|
$cleanupStats.Add("$($trimmedTableName):$deletedRows")
|
|
}
|
|
}
|
|
|
|
$deletedUsers = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM users WHERE id_users NOT IN (SELECT id_users FROM keep_user_ids);')
|
|
if ($deletedUsers -gt 0) {
|
|
Invoke-MysqlQuery -Query 'DELETE FROM users WHERE id_users NOT IN (SELECT id_users FROM keep_user_ids);' | Out-Null
|
|
$cleanupStats.Add("users:$deletedUsers")
|
|
}
|
|
}
|
|
|
|
$afterGara = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM gara;')
|
|
$afterPuntoFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM punto_foto;')
|
|
$afterFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM foto;')
|
|
$afterLogFoto = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM log_foto;')
|
|
$afterUsers = 0
|
|
if ($usersExists) {
|
|
$afterUsers = [int64](Get-SqlScalar -Query 'SELECT COUNT(*) FROM users;')
|
|
}
|
|
|
|
if (Test-Path $OutputDump) {
|
|
Remove-Item $OutputDump -Force
|
|
}
|
|
|
|
Write-Host "Exporting curated dump..."
|
|
Invoke-DockerQuiet -DockerArgs @(
|
|
'exec', $ContainerName, 'sh', '-lc',
|
|
"mysqldump -uroot -p$RootPassword --default-character-set=utf8mb4 --single-transaction --routines --triggers --set-gtid-purged=OFF $DatabaseName > /workspace/db/$outputFileName"
|
|
)
|
|
|
|
if (-not (Test-Path $OutputDump)) {
|
|
throw "Curated dump was not created: $OutputDump"
|
|
}
|
|
|
|
$outputSizeBytes = (Get-Item $OutputDump).Length
|
|
|
|
Write-Host "OriginalSizeBytes=$sourceSizeBytes"
|
|
Write-Host "CuratedSizeBytes=$outputSizeBytes"
|
|
Write-Host "BeforeGara=$beforeGara"
|
|
Write-Host "AfterGara=$afterGara"
|
|
Write-Host "BeforePuntoFoto=$beforePuntoFoto"
|
|
Write-Host "AfterPuntoFoto=$afterPuntoFoto"
|
|
Write-Host "BeforeFoto=$beforeFoto"
|
|
Write-Host "AfterFoto=$afterFoto"
|
|
Write-Host "BeforeLogFoto=$beforeLogFoto"
|
|
Write-Host "AfterLogFoto=$afterLogFoto"
|
|
Write-Host "BeforeUsers=$beforeUsers"
|
|
Write-Host "AfterUsers=$afterUsers"
|
|
Write-Host ("KeptGaraIds=" + (($keptGaraIds | ForEach-Object { $_.Trim() }) -join ','))
|
|
if ($usersExists) {
|
|
Write-Host ("KeptUserIds=" + (($keptUserIds | ForEach-Object { $_.Trim() }) -join ','))
|
|
}
|
|
Write-Host ("DependencyCleanup=" + (($cleanupStats | Sort-Object) -join ';'))
|
|
if (Test-Path $importErrorLogPath) {
|
|
$importErrorCount = (Get-Item $importErrorLogPath).Length
|
|
Write-Host "ImportErrorLogBytes=$importErrorCount"
|
|
}
|
|
}
|
|
finally {
|
|
& docker rm -f $ContainerName 1>$null 2>$null
|
|
& docker volume rm $VolumeName 1>$null 2>$null
|
|
} |