fix(epf-dump): постусловие выходного каталога + маскировка учётных данных

Пропущенный при разборе #49-52 навык того же класса: epf-dump разбирает EPF/ERF
через платформу в каталог XML. Успех определялся только по коду возврата (ложный
успех при пустом выходе), а строка Running светила /P<пароль> (1cv8) и --password=
(ibcmd). Добавлены postcondition непустого OutputDir и маскировка, обе ветки,
оба порта. Покрывает и erf-dump (общий скрипт).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-19 20:15:34 +03:00
co-authored by Claude Opus 4.8
parent 00cd0f3f5f
commit 06485d216b
2 changed files with 59 additions and 9 deletions
+25 -3
View File
@@ -1,4 +1,4 @@
# epf-dump v1.6 — Dump external data processor or report (EPF/ERF) to XML sources
# epf-dump v1.7 — Dump external data processor or report (EPF/ERF) to XML sources
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
<#
@@ -155,6 +155,19 @@ function Invoke-IbcmdProcess {
}
function Test-DirNonEmpty {
# Postcondition: the platform must have written files into the output directory.
# Exit code 0 with an empty dir (broken/headless env) is a false success — reject it.
param([string]$Path)
return (Test-Path $Path -PathType Container) -and ([bool](Get-ChildItem -LiteralPath $Path -Force -ErrorAction SilentlyContinue | Select-Object -First 1))
}
function Format-SafeArgs {
# Mask credential tokens (/N, /P for 1cv8; --user=, --password= for ibcmd) for display.
param([string[]]$A)
($A | ForEach-Object { $_ -replace '^(/[NP]).+', '$1***' -replace '^(--(user|password)=).+', '$1***' }) -join ' '
}
$engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" }
if ($engine -eq "ibcmd") {
if (-not $InfoBasePath) {
@@ -189,12 +202,16 @@ try {
if ($UserName) { $arguments += "--user=$UserName" }
if ($Password) { $arguments += "--password=$Password" }
$arguments += "--data=$tempDir"
Write-Host "Running: ibcmd $($arguments -join ' ')"
Write-Host "Running: ibcmd $(Format-SafeArgs $arguments)"
$__ib = Invoke-IbcmdProcess $V8Path $arguments
$output = $__ib.Output
$exitCode = $__ib.ExitCode
$outMissing = ($exitCode -eq 0) -and -not (Test-DirNonEmpty $OutputDir)
if ($outMissing) { $exitCode = 1 }
if ($exitCode -eq 0) {
Write-Host "External data processor/report dumped successfully to: $OutputDir" -ForegroundColor Green
} elseif ($outMissing) {
Write-Host "Error: exit code 0 but no files under $OutputDir — dump produced no output" -ForegroundColor Red
} else {
Write-Host "Error dumping external data processor/report (code: $exitCode)" -ForegroundColor Red
}
@@ -224,13 +241,18 @@ try {
$arguments += "/DisableStartupDialogs"
# --- Execute ---
Write-Host "Running: 1cv8.exe $($arguments -join ' ')"
Write-Host "Running: 1cv8.exe $(Format-SafeArgs $arguments)"
$process = Start-Process -FilePath $V8Path -ArgumentList $arguments -NoNewWindow -Wait -PassThru
$exitCode = $process.ExitCode
# --- Result ---
# Postcondition: exit 0 with an empty output directory is a false success.
$outMissing = ($exitCode -eq 0) -and -not (Test-DirNonEmpty $OutputDir)
if ($outMissing) { $exitCode = 1 }
if ($exitCode -eq 0) {
Write-Host "Dump completed successfully to: $OutputDir" -ForegroundColor Green
} elseif ($outMissing) {
Write-Host "Error: exit code 0 but no files under $OutputDir — dump produced no output" -ForegroundColor Red
} else {
Write-Host "Error dumping (code: $exitCode)" -ForegroundColor Red
}
+34 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# epf-dump v1.6 — Dump external data processor or report (EPF/ERF) to XML sources
# epf-dump v1.7 — Dump external data processor or report (EPF/ERF) to XML sources
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -99,6 +99,22 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace")
def dir_nonempty(path):
"""Postcondition: the platform must have written files into the output directory.
Exit code 0 with an empty dir (broken/headless env) is a false success — reject it."""
return os.path.isdir(path) and any(os.scandir(path))
def _mask(args):
"""Mask credential tokens (/N, /P for 1cv8; --user=, --password= for ibcmd) for display."""
out = []
for a in args:
a = re.sub(r"^(/[NP]).+", r"\1***", a)
a = re.sub(r"^(--(?:user|password)=).+", r"\1***", a)
out.append(a)
return out
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
@@ -163,17 +179,23 @@ def main():
if args.Password:
arguments.append(f"--password={args.Password}")
arguments.append(f"--data={ib_data}")
print(f"Running: ibcmd {' '.join(arguments)}")
print(f"Running: ibcmd {' '.join(_mask(arguments))}")
result = run_ibcmd([v8path] + arguments, warn_no_user=False)
if result.returncode == 0:
exit_code = result.returncode
out_missing = exit_code == 0 and not dir_nonempty(args.OutputDir)
if out_missing:
exit_code = 1
if exit_code == 0:
print(f"External data processor/report dumped successfully to: {args.OutputDir}")
elif out_missing:
print(f"Error: exit code 0 but no files under {args.OutputDir} — dump produced no output", file=sys.stderr)
else:
print(f"Error dumping external data processor/report (code: {result.returncode})", file=sys.stderr)
print(f"Error dumping external data processor/report (code: {exit_code})", file=sys.stderr)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
sys.exit(result.returncode)
sys.exit(exit_code)
# --- Build arguments ---
arguments = ["DESIGNER"]
@@ -197,7 +219,7 @@ def main():
arguments.append("/DisableStartupDialogs")
# --- Execute ---
print(f"Running: 1cv8.exe {' '.join(arguments)}")
print(f"Running: 1cv8.exe {' '.join(_mask(arguments))}")
result = subprocess.run(
[v8path] + arguments,
capture_output=True,
@@ -206,8 +228,14 @@ def main():
exit_code = result.returncode
# --- Result ---
# Postcondition: exit 0 with an empty output directory is a false success.
out_missing = exit_code == 0 and not dir_nonempty(args.OutputDir)
if out_missing:
exit_code = 1
if exit_code == 0:
print(f"Dump completed successfully to: {args.OutputDir}")
elif out_missing:
print(f"Error: exit code 0 but no files under {args.OutputDir} — dump produced no output", file=sys.stderr)
else:
print(f"Error dumping (code: {exit_code})", file=sys.stderr)