mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-28 14:09:41 +03:00
fix(py-порты): ошибки печатать в тот же поток, что и PS
Двадцать один py-порт печатал ошибки в stderr, тогда как их PS-мастера пишут через Write-Host в stdout. Счётчики совпадали один в один (14↔14, 19↔19, 13↔13) — сообщения были те же, разъехался только поток. Это нарушало соответствие из docs/python-porting-guide.md, где Write-Host сопоставлен обычному print. Это не косметика. Харнесс не чередует потоки, а группирует: сначала весь stderr, потом весь stdout. Из-за этого в py-порте вердикт «Error dumping configuration (code: 1)» печатался ПЕРЕД строками, которые его объясняют, а причина из лога платформы оказывалась в самом низу — причинный порядок вывода переворачивался. Порт, работающий на macOS, читался хуже того, что работает на Windows. Тесты этого не ловили по построению: текст ошибки сверяют только кейсы со строковым expectError, а он смотрит в stderr — потому такие кейсы есть лишь у семейства, где потоки сходятся, а в db-* их ноль. Добавлен гард check-error-streams.mjs: нет записи в stderr в PS-порте — не должно быть и в py, и симметрично. Он сразу нашёл пять навыков сверх тех, что я насчитал вручную, и отсеял два ложных срабатывания (в meta-remove слово Write-Error стоит в комментарии «почему НЕ Write-Error»). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fdeae5c87f
commit
4f61ef77ca
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# cf-info v1.6 — Compact summary of 1C configuration root
|
||||
# cf-info v1.7 — Compact summary of 1C configuration root
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -61,11 +61,11 @@ if os.path.isdir(config_path):
|
||||
if os.path.isfile(candidate):
|
||||
config_path = candidate
|
||||
else:
|
||||
print(f"[ERROR] No Configuration.xml found in directory: {config_path}", file=sys.stderr)
|
||||
print(f"[ERROR] No Configuration.xml found in directory: {config_path}")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isfile(config_path):
|
||||
print(f"[ERROR] File not found: {config_path}", file=sys.stderr)
|
||||
print(f"[ERROR] File not found: {config_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Load XML ---
|
||||
@@ -82,12 +82,12 @@ NS = {
|
||||
|
||||
md_root = xml_root # root is MetaDataObject itself
|
||||
if etree.QName(md_root.tag).localname != "MetaDataObject":
|
||||
print("[ERROR] Not a valid 1C metadata XML file (no MetaDataObject root)", file=sys.stderr)
|
||||
print("[ERROR] Not a valid 1C metadata XML file (no MetaDataObject root)")
|
||||
sys.exit(1)
|
||||
|
||||
cfg_node = md_root.find("md:Configuration", NS)
|
||||
if cfg_node is None:
|
||||
print("[ERROR] No <Configuration> element found", file=sys.stderr)
|
||||
print("[ERROR] No <Configuration> element found")
|
||||
sys.exit(1)
|
||||
|
||||
version = md_root.get("version", "")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-create v1.13 — Create 1C information base
|
||||
# db-create v1.14 — Create 1C information base
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -292,7 +288,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -313,7 +309,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -421,15 +417,15 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate template ---
|
||||
if args.UseTemplate and not os.path.exists(args.UseTemplate):
|
||||
print(f"Error: template file not found: {args.UseTemplate}", file=sys.stderr)
|
||||
print(f"Error: template file not found: {args.UseTemplate}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- ibcmd branch (file infobase only) ---
|
||||
@@ -456,10 +452,9 @@ def main():
|
||||
print(
|
||||
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
||||
"— information base was not created",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
print(f"Error creating information base (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error creating information base (code: {exit_code})")
|
||||
print_platform_output(result)
|
||||
sys.exit(exit_code)
|
||||
|
||||
@@ -516,10 +511,9 @@ def main():
|
||||
print(
|
||||
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
||||
"— information base was not created",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
print(f"Error creating information base (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error creating information base (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-dump-cf v1.15 — Dump 1C configuration to CF file
|
||||
# db-dump-cf v1.16 — Dump 1C configuration to CF file
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -442,10 +438,10 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Ensure output directory exists ---
|
||||
@@ -456,7 +452,7 @@ def main():
|
||||
# --- ibcmd branch (file infobase only) ---
|
||||
if engine == "ibcmd":
|
||||
if args.AllExtensions:
|
||||
print("Error: ibcmd config save does not support -AllExtensions (use -Extension)", file=sys.stderr)
|
||||
print("Error: ibcmd config save does not support -AllExtensions (use -Extension)")
|
||||
sys.exit(1)
|
||||
arguments = ["infobase", "config", "save", f"--db-path={args.InfoBasePath}"]
|
||||
if args.Extension:
|
||||
@@ -479,9 +475,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped")
|
||||
else:
|
||||
print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping configuration (code: {exit_code})")
|
||||
sys.exit(exit_code)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -529,9 +525,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped")
|
||||
else:
|
||||
print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping configuration (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-dump-dt v1.14 — Dump 1C information base to DT file
|
||||
# db-dump-dt v1.15 — Dump 1C information base to DT file
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -440,10 +436,10 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Ensure output directory exists ---
|
||||
@@ -472,9 +468,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Information base dumped successfully to: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped")
|
||||
else:
|
||||
print(f"Error dumping information base (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping information base (code: {exit_code})")
|
||||
sys.exit(exit_code)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -516,9 +512,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Information base dumped successfully to: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped")
|
||||
else:
|
||||
print(f"Error dumping information base (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping information base (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-dump-xml v1.20 — Dump 1C configuration to XML files
|
||||
# db-dump-xml v1.21 — Dump 1C configuration to XML files
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -205,7 +205,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -213,7 +212,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -281,14 +279,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -330,14 +326,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -368,7 +364,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -385,7 +381,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -457,7 +453,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -545,10 +541,10 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate Partial mode ---
|
||||
@@ -556,7 +552,7 @@ def main():
|
||||
# то, что уже напечатал другой навык (например /db-repo update со списком полученных объектов).
|
||||
if args.ObjectsFile:
|
||||
if not os.path.exists(args.ObjectsFile):
|
||||
print("Error: -ObjectsFile not found: %s" % args.ObjectsFile, file=sys.stderr)
|
||||
print("Error: -ObjectsFile not found: %s" % args.ObjectsFile)
|
||||
sys.exit(1)
|
||||
with open(args.ObjectsFile, encoding="utf-8-sig") as f:
|
||||
from_file = [s.strip() for s in f.read().splitlines()
|
||||
@@ -569,7 +565,7 @@ def main():
|
||||
if args.Mode == "UpdateInfo":
|
||||
# Не «шире/уже», а другая операция: обновление ConfigDumpInfo без выгрузки файлов.
|
||||
print("Error: -Mode UpdateInfo does not take an object list — it only refreshes "
|
||||
"ConfigDumpInfo.xml", file=sys.stderr)
|
||||
"ConfigDumpInfo.xml")
|
||||
sys.exit(1)
|
||||
if args.Mode in ("Full", "Changes"):
|
||||
print("[note] перечислены объекты — выгружаются только они; -Mode %s не применён"
|
||||
@@ -578,7 +574,7 @@ def main():
|
||||
if not args.Mode:
|
||||
args.Mode = "Changes"
|
||||
if args.Mode == "Partial" and not args.Objects:
|
||||
print("Error: -Objects or -ObjectsFile required for Partial mode", file=sys.stderr)
|
||||
print("Error: -Objects or -ObjectsFile required for Partial mode")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Create output dir if needed ---
|
||||
@@ -589,12 +585,12 @@ def main():
|
||||
# --- ibcmd branch (file infobase only; hierarchical Full/Changes) ---
|
||||
if engine == "ibcmd":
|
||||
if args.Format == "Plain":
|
||||
print("Error: ibcmd config export supports hierarchical format only (use -Format Hierarchical or 1cv8)", file=sys.stderr)
|
||||
print("Error: ibcmd config export supports hierarchical format only (use -Format Hierarchical or 1cv8)")
|
||||
sys.exit(1)
|
||||
if args.AllExtensions:
|
||||
arguments = ["infobase", "config", "export", "all-extensions", args.ConfigDir, f"--db-path={args.InfoBasePath}"]
|
||||
elif args.Mode == "UpdateInfo":
|
||||
print("Error: ibcmd config export does not support Mode UpdateInfo; use 1cv8", file=sys.stderr)
|
||||
print("Error: ibcmd config export does not support Mode UpdateInfo; use 1cv8")
|
||||
sys.exit(1)
|
||||
elif args.Mode == "Partial":
|
||||
obj_list = [o.strip() for o in args.Objects.split(",") if o.strip()]
|
||||
@@ -624,9 +620,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Configuration exported successfully to: {args.ConfigDir}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not exported", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not exported")
|
||||
else:
|
||||
print(f"Error exporting configuration (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error exporting configuration (code: {exit_code})")
|
||||
sys.exit(exit_code)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -703,9 +699,9 @@ def main():
|
||||
print("Dump completed successfully")
|
||||
print(f"Configuration dumped to: {args.ConfigDir}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not dumped", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not dumped")
|
||||
else:
|
||||
print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping configuration (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-load-cf v1.16 — Load 1C configuration from CF file
|
||||
# db-load-cf v1.17 — Load 1C configuration from CF file
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -460,21 +456,21 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate input file ---
|
||||
if not os.path.isfile(args.InputFile):
|
||||
print(f"Error: input file not found: {args.InputFile}", file=sys.stderr)
|
||||
print(f"Error: input file not found: {args.InputFile}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- ibcmd branch (file infobase only) ---
|
||||
if engine == "ibcmd":
|
||||
if args.AllExtensions:
|
||||
print("Error: ibcmd config load does not support -AllExtensions (use -Extension)", file=sys.stderr)
|
||||
print("Error: ibcmd config load does not support -AllExtensions (use -Extension)")
|
||||
sys.exit(1)
|
||||
arguments = ["infobase", "config", "load", f"--db-path={args.InfoBasePath}"]
|
||||
if args.Extension:
|
||||
@@ -493,7 +489,7 @@ def main():
|
||||
if result.returncode == 0:
|
||||
print(f"Configuration loaded successfully from: {args.InputFile}")
|
||||
else:
|
||||
print(f"Error loading configuration (code: {result.returncode}){describe_exit(result.returncode)}", file=sys.stderr)
|
||||
print(f"Error loading configuration (code: {result.returncode}){describe_exit(result.returncode)}")
|
||||
sys.exit(result.returncode)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -537,7 +533,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Configuration loaded successfully from: {args.InputFile}")
|
||||
else:
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-load-dt v1.15 — Load 1C information base from DT file
|
||||
# db-load-dt v1.16 — Load 1C information base from DT file
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -460,15 +456,15 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate input file ---
|
||||
if not os.path.isfile(args.InputFile):
|
||||
print(f"Error: input file not found: {args.InputFile}", file=sys.stderr)
|
||||
print(f"Error: input file not found: {args.InputFile}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- ibcmd branch (file infobase only) ---
|
||||
@@ -490,7 +486,7 @@ def main():
|
||||
if result.returncode == 0:
|
||||
print(f"Information base restored successfully from: {args.InputFile}")
|
||||
else:
|
||||
print(f"Error restoring information base (code: {result.returncode}){describe_exit(result.returncode)}", file=sys.stderr)
|
||||
print(f"Error restoring information base (code: {result.returncode}){describe_exit(result.returncode)}")
|
||||
sys.exit(result.returncode)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -532,7 +528,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Information base restored successfully from: {args.InputFile}")
|
||||
else:
|
||||
print(f"Error restoring information base (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error restoring information base (code: {exit_code}){describe_exit(exit_code)}")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-load-git v1.25 — Load Git changes into 1C database
|
||||
# db-load-git v1.26 — Load Git changes into 1C database
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -226,7 +226,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -234,7 +233,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -302,14 +300,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -351,14 +347,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -389,7 +385,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -406,7 +402,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -510,7 +506,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -635,10 +631,10 @@ def main():
|
||||
engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8"
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Resolve additional arguments for the selected engine ---
|
||||
@@ -655,19 +651,19 @@ def main():
|
||||
|
||||
# --- Validate config dir ---
|
||||
if not os.path.exists(args.ConfigDir):
|
||||
print(f"Error: config directory not found: {args.ConfigDir}", file=sys.stderr)
|
||||
print(f"Error: config directory not found: {args.ConfigDir}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate Commit mode ---
|
||||
if args.Source == "Commit" and not args.CommitRange:
|
||||
print("Error: -CommitRange required for Source=Commit", file=sys.stderr)
|
||||
print("Error: -CommitRange required for Source=Commit")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Check git ---
|
||||
try:
|
||||
subprocess.run(["git", "--version"], capture_output=True, text=True, check=True)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
print("Error: git not found in PATH", file=sys.stderr)
|
||||
print("Error: git not found in PATH")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Get changed files from Git ---
|
||||
@@ -746,10 +742,10 @@ def main():
|
||||
config_files.append(rel_path)
|
||||
|
||||
if support_skipped:
|
||||
print("[ВНИМАНИЕ] Состояние поддержки изменено в коммите, но частично не загружается (исключено):", file=sys.stderr)
|
||||
print("[ВНИМАНИЕ] Состояние поддержки изменено в коммите, но частично не загружается (исключено):")
|
||||
for sf in support_skipped:
|
||||
print(f" - {sf}", file=sys.stderr)
|
||||
print(" Смена состояния поддержки применяется только полной загрузкой (db-load-xml -Mode Full).", file=sys.stderr)
|
||||
print(f" - {sf}")
|
||||
print(" Смена состояния поддержки применяется только полной загрузкой (db-load-xml -Mode Full).")
|
||||
|
||||
if len(config_files) == 0:
|
||||
print("No configuration files found in changes")
|
||||
@@ -773,10 +769,10 @@ def main():
|
||||
if engine == "ibcmd":
|
||||
# --- ibcmd branch (file infobase only; import specific files) ---
|
||||
if args.Format == "Plain":
|
||||
print("Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)", file=sys.stderr)
|
||||
print("Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)")
|
||||
sys.exit(1)
|
||||
if args.AllExtensions:
|
||||
print("Error: ibcmd config import does not support -AllExtensions (use -Extension or 1cv8)", file=sys.stderr)
|
||||
print("Error: ibcmd config import does not support -AllExtensions (use -Extension or 1cv8)")
|
||||
sys.exit(1)
|
||||
arguments = ["infobase", "config", "import", "files"] + config_files
|
||||
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
|
||||
@@ -793,7 +789,7 @@ def main():
|
||||
print(f"Running: ibcmd {_redact(' '.join(format_args_for_display(arguments, engine)), args.Password, args.UserName)}")
|
||||
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
||||
if result.returncode != 0:
|
||||
print(f"Error loading changes (code: {result.returncode}){describe_exit(result.returncode)}", file=sys.stderr)
|
||||
print(f"Error loading changes (code: {result.returncode}){describe_exit(result.returncode)}")
|
||||
sys.exit(result.returncode)
|
||||
print(f"Changes loaded successfully ({len(config_files)} files)")
|
||||
exit_code = 0
|
||||
@@ -811,7 +807,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print("Database configuration updated successfully")
|
||||
else:
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
print_platform_output(ar)
|
||||
sys.exit(exit_code)
|
||||
|
||||
@@ -873,7 +869,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print("Load completed successfully")
|
||||
else:
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
|
||||
log_content = ""
|
||||
if os.path.isfile(out_file):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-load-xml v1.27 — Load 1C configuration from XML files
|
||||
# db-load-xml v1.28 — Load 1C configuration from XML files
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -226,7 +226,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -234,7 +233,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -302,14 +300,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -351,14 +347,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -389,7 +385,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -406,7 +402,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -510,7 +506,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -624,15 +620,15 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate config dir ---
|
||||
if not os.path.exists(args.ConfigDir):
|
||||
print(f"Error: config directory not found: {args.ConfigDir}", file=sys.stderr)
|
||||
print(f"Error: config directory not found: {args.ConfigDir}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate Partial mode ---
|
||||
@@ -645,13 +641,13 @@ def main():
|
||||
if not args.Mode:
|
||||
args.Mode = "Full"
|
||||
if args.Mode == "Partial" and not args.Files and not args.ListFile:
|
||||
print("Error: -Files or -ListFile required for Partial mode", file=sys.stderr)
|
||||
print("Error: -Files or -ListFile required for Partial mode")
|
||||
sys.exit(1)
|
||||
|
||||
# --- ibcmd branch (file infobase only; hierarchical full-directory import) ---
|
||||
if engine == "ibcmd":
|
||||
if args.Format == "Plain":
|
||||
print("Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)", file=sys.stderr)
|
||||
print("Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)")
|
||||
sys.exit(1)
|
||||
if args.AllExtensions:
|
||||
arguments = ["infobase", "config", "import", "all-extensions", args.ConfigDir, f"--db-path={args.InfoBasePath}"]
|
||||
@@ -659,7 +655,7 @@ def main():
|
||||
# partial: import specific files (relative to ConfigDir)
|
||||
if args.ListFile:
|
||||
if not os.path.isfile(args.ListFile):
|
||||
print(f"Error: list file not found: {args.ListFile}", file=sys.stderr)
|
||||
print(f"Error: list file not found: {args.ListFile}")
|
||||
sys.exit(1)
|
||||
with open(args.ListFile, encoding="utf-8-sig") as f:
|
||||
file_list = [ln.strip() for ln in f if ln.strip()]
|
||||
@@ -668,7 +664,7 @@ def main():
|
||||
else:
|
||||
file_list = []
|
||||
if not file_list:
|
||||
print("Error: -Files or -ListFile required for partial import", file=sys.stderr)
|
||||
print("Error: -Files or -ListFile required for partial import")
|
||||
sys.exit(1)
|
||||
arguments = ["infobase", "config", "import", "files"] + file_list
|
||||
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
|
||||
@@ -690,7 +686,7 @@ def main():
|
||||
print(f"Running: ibcmd {_redact(' '.join(format_args_for_display(arguments, engine)), args.Password, args.UserName)}")
|
||||
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
||||
if result.returncode != 0:
|
||||
print(f"Error loading configuration from files (code: {result.returncode}){describe_exit(result.returncode)}", file=sys.stderr)
|
||||
print(f"Error loading configuration from files (code: {result.returncode}){describe_exit(result.returncode)}")
|
||||
sys.exit(result.returncode)
|
||||
print(f"Configuration loaded successfully from: {args.ConfigDir}")
|
||||
exit_code = 0
|
||||
@@ -708,7 +704,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print("Database configuration updated successfully")
|
||||
else:
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
print_platform_output(ar)
|
||||
sys.exit(exit_code)
|
||||
|
||||
@@ -745,7 +741,7 @@ def main():
|
||||
# Build list file
|
||||
if args.ListFile:
|
||||
if not os.path.isfile(args.ListFile):
|
||||
print(f"Error: list file not found: {args.ListFile}", file=sys.stderr)
|
||||
print(f"Error: list file not found: {args.ListFile}")
|
||||
sys.exit(1)
|
||||
with open(args.ListFile, encoding="utf-8-sig") as f:
|
||||
raw_list = [ln.strip() for ln in f if ln.strip()]
|
||||
@@ -757,12 +753,12 @@ def main():
|
||||
support_files = [x for x in raw_list if support_re.search(x)]
|
||||
file_list = [x for x in raw_list if not support_re.search(x)]
|
||||
if support_files:
|
||||
print("[ВНИМАНИЕ] Служебные файлы состояния поддержки исключены из частичной загрузки (частично не грузятся):", file=sys.stderr)
|
||||
print("[ВНИМАНИЕ] Служебные файлы состояния поддержки исключены из частичной загрузки (частично не грузятся):")
|
||||
for sf in support_files:
|
||||
print(f" - {sf}", file=sys.stderr)
|
||||
print(" Смена состояния поддержки применяется только полной загрузкой: -Mode Full.", file=sys.stderr)
|
||||
print(f" - {sf}")
|
||||
print(" Смена состояния поддержки применяется только полной загрузкой: -Mode Full.")
|
||||
if not file_list:
|
||||
print("Error: после исключения служебных файлов поддержки загружать нечего. Для смены поддержки используйте -Mode Full.", file=sys.stderr)
|
||||
print("Error: после исключения служебных файлов поддержки загружать нечего. Для смены поддержки используйте -Mode Full.")
|
||||
sys.exit(1)
|
||||
generated_list_file = os.path.join(temp_dir, "load_list.txt")
|
||||
with open(generated_list_file, "w", encoding="utf-8-sig") as f:
|
||||
@@ -819,7 +815,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print("Load completed successfully")
|
||||
else:
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error loading configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
|
||||
if log_content:
|
||||
print("--- Log ---")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-repo v1.13 — 1C configuration repository operations
|
||||
# db-repo v1.14 — 1C configuration repository operations
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
||||
"""Работа с хранилищем конфигурации 1С.
|
||||
@@ -132,7 +132,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -140,7 +139,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -224,14 +222,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -254,7 +252,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -271,7 +269,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-run v1.9 — Launch 1C:Enterprise
|
||||
# db-run v1.10 — Launch 1C:Enterprise
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -117,7 +117,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -125,7 +124,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -193,14 +191,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -225,7 +221,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -260,14 +256,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -327,7 +323,7 @@ def main():
|
||||
|
||||
# --- Validate connection ---
|
||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Build arguments ---
|
||||
@@ -377,7 +373,7 @@ def main():
|
||||
time.sleep(0.2)
|
||||
rc = proc.poll()
|
||||
if rc is not None:
|
||||
print(f"Error: 1C:Enterprise exited immediately (code: {rc})", file=sys.stderr)
|
||||
print(f"Error: 1C:Enterprise exited immediately (code: {rc})")
|
||||
sys.exit(rc if rc and rc > 0 else 1)
|
||||
print(f"PID: {proc.pid}")
|
||||
print("1C:Enterprise launched")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# db-update v1.18 — Update 1C database configuration
|
||||
# db-update v1.19 — Update 1C database configuration
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -205,7 +205,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -213,7 +212,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -281,14 +279,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -330,14 +326,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -368,7 +364,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -385,7 +381,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -489,7 +485,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -586,16 +582,16 @@ def main():
|
||||
# --- Validate connection ---
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
|
||||
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef")
|
||||
sys.exit(1)
|
||||
|
||||
# --- ibcmd branch (file infobase only) ---
|
||||
if engine == "ibcmd":
|
||||
if args.AllExtensions:
|
||||
print("Error: ibcmd config apply does not support -AllExtensions (use -Extension)", file=sys.stderr)
|
||||
print("Error: ibcmd config apply does not support -AllExtensions (use -Extension)")
|
||||
sys.exit(1)
|
||||
arguments = ["infobase", "config", "apply", f"--db-path={args.InfoBasePath}", "--force"]
|
||||
if args.Dynamic == "+":
|
||||
@@ -617,7 +613,7 @@ def main():
|
||||
if result.returncode == 0:
|
||||
print("Database configuration updated successfully")
|
||||
else:
|
||||
print(f"Error updating database configuration (code: {result.returncode}){describe_exit(result.returncode)}", file=sys.stderr)
|
||||
print(f"Error updating database configuration (code: {result.returncode}){describe_exit(result.returncode)}")
|
||||
sys.exit(result.returncode)
|
||||
|
||||
# --- Temp dir ---
|
||||
@@ -674,7 +670,7 @@ def main():
|
||||
if exit_code == 0:
|
||||
print("Database configuration updated successfully")
|
||||
else:
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}", file=sys.stderr)
|
||||
print(f"Error updating database configuration (code: {exit_code}){describe_exit(exit_code)}")
|
||||
|
||||
log_content = ""
|
||||
if os.path.isfile(out_file):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# epf-build v1.15 — Build external data processor or report (EPF/ERF) from XML sources
|
||||
# epf-build v1.16 — Build external data processor or report (EPF/ERF) from XML sources
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -440,7 +436,7 @@ def main():
|
||||
}
|
||||
extra_args = resolve_extra_args(engine, v8_extra, ibcmd_extra, arg_hints)
|
||||
if engine == "ibcmd" and args.InfoBaseServer and args.InfoBaseRef:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath or omit for stub)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath or omit for stub)")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Auto-create stub database if no connection specified ---
|
||||
@@ -461,14 +457,14 @@ def main():
|
||||
stub_cmd += ["-AdditionalIbcmdArguments"] + list(ibcmd_extra)
|
||||
result = subprocess.run(stub_cmd, capture_output=False)
|
||||
if result.returncode != 0:
|
||||
print("Error: failed to create stub database", file=sys.stderr)
|
||||
print("Error: failed to create stub database")
|
||||
sys.exit(1)
|
||||
args.InfoBasePath = auto_base_path
|
||||
auto_created_base = auto_base_path
|
||||
|
||||
# --- Validate source file ---
|
||||
if not os.path.isfile(args.SourceFile):
|
||||
print(f"Error: source file not found: {args.SourceFile}", file=sys.stderr)
|
||||
print(f"Error: source file not found: {args.SourceFile}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Ensure output directory exists ---
|
||||
@@ -502,9 +498,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"External data processor/report built successfully: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output")
|
||||
else:
|
||||
print(f"Error building external data processor/report (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error building external data processor/report (code: {exit_code})")
|
||||
sys.exit(exit_code)
|
||||
|
||||
# --- Build arguments ---
|
||||
@@ -541,9 +537,9 @@ def main():
|
||||
if exit_code == 0:
|
||||
print(f"Build completed successfully: {args.OutputFile}")
|
||||
elif out_missing:
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output", file=sys.stderr)
|
||||
print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output")
|
||||
else:
|
||||
print(f"Error building (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error building (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# epf-dump v1.14 — Dump external data processor or report (EPF/ERF) to XML sources
|
||||
# epf-dump v1.15 — Dump external data processor or report (EPF/ERF) to XML sources
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
print(
|
||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||
f"({param} cannot extend the ibcmd command)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
for k in owned:
|
||||
@@ -128,7 +127,6 @@ def assert_extra_args(extra, engine, hints):
|
||||
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||
print(
|
||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
||||
print(
|
||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||
"(use -AdditionalIbcmdArguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine != "ibcmd" and ibcmd_extra:
|
||||
print(
|
||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||
"(use -AdditionalV8Arguments)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
||||
v8path = max(candidates, key=_version_key)
|
||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||
else:
|
||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
||||
print("Error: 1C executable not found. Specify -V8Path")
|
||||
sys.exit(1)
|
||||
if os.path.isdir(v8path):
|
||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||
v8path = os.path.join(v8path, exe)
|
||||
if not os.path.isfile(v8path):
|
||||
print(f"Error: 1C executable not found at {v8path}", file=sys.stderr)
|
||||
print(f"Error: 1C executable not found at {v8path}")
|
||||
sys.exit(1)
|
||||
return v8path
|
||||
|
||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
||||
if not path:
|
||||
return
|
||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)", file=sys.stderr)
|
||||
print(f"Error: information base not found at {path} (no 1Cv8.1CD)")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
||||
if len(v) > 3 and v[-1] in "\\/":
|
||||
v = v[:-1]
|
||||
if '"' in v:
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}", file=sys.stderr)
|
||||
print(f"Error: {param or 'path'} contains a quote character: {value}")
|
||||
sys.exit(1)
|
||||
return v
|
||||
|
||||
@@ -372,7 +368,7 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True):
|
||||
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||
"""
|
||||
if warn_no_user and os.name == "nt" and not has_username:
|
||||
sys.stderr.write(IBCMD_NOUSER_HINT)
|
||||
sys.stdout.write(IBCMD_NOUSER_HINT)
|
||||
sys.stderr.flush()
|
||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||
r.stdout = decode_platform_bytes(r.stdout)
|
||||
@@ -448,20 +444,20 @@ def main():
|
||||
|
||||
# --- Validate database connection ---
|
||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print("Error: database connection required. Specify -InfoBasePath or -InfoBaseServer/-InfoBaseRef", file=sys.stderr)
|
||||
print("Error: database connection required. Specify -InfoBasePath or -InfoBaseServer/-InfoBaseRef")
|
||||
print("Dump in an empty database loses reference types (CatalogRef, DocumentRef, etc.) irreversibly.")
|
||||
sys.exit(1)
|
||||
if engine == "ibcmd":
|
||||
if not args.InfoBasePath:
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
|
||||
print("Error: ibcmd supports file infobases only (use -InfoBasePath)")
|
||||
sys.exit(1)
|
||||
if args.Format == "Plain":
|
||||
print("Error: ibcmd config export supports hierarchical format only (use -Format Hierarchical or 1cv8)", file=sys.stderr)
|
||||
print("Error: ibcmd config export supports hierarchical format only (use -Format Hierarchical or 1cv8)")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate input file ---
|
||||
if not os.path.isfile(args.InputFile):
|
||||
print(f"Error: input file not found: {args.InputFile}", file=sys.stderr)
|
||||
print(f"Error: input file not found: {args.InputFile}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Ensure output directory exists ---
|
||||
@@ -493,9 +489,9 @@ def main():
|
||||
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)
|
||||
print(f"Error: exit code 0 but no files under {args.OutputDir} — dump produced no output")
|
||||
else:
|
||||
print(f"Error dumping external data processor/report (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping external data processor/report (code: {exit_code})")
|
||||
sys.exit(exit_code)
|
||||
|
||||
# --- Build arguments ---
|
||||
@@ -533,9 +529,9 @@ def main():
|
||||
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)
|
||||
print(f"Error: exit code 0 but no files under {args.OutputDir} — dump produced no output")
|
||||
else:
|
||||
print(f"Error dumping (code: {exit_code})", file=sys.stderr)
|
||||
print(f"Error dumping (code: {exit_code})")
|
||||
|
||||
if os.path.isfile(out_file):
|
||||
try:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# meta-validate v1.23 — Validate 1C metadata object structure (Python port)
|
||||
# meta-validate v1.24 — Validate 1C metadata object structure (Python port)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import os
|
||||
@@ -80,7 +80,7 @@ if len(path_list) > 1:
|
||||
except Exception as e:
|
||||
# Падение одного объекта не должно рвать батч — в варианте с отдельным процессом
|
||||
# это обеспечивалось изоляцией процессов.
|
||||
print(f"[ERROR] {single_path}: {type(e).__name__}: {e}", file=sys.stderr)
|
||||
print(f"[ERROR] {single_path}: {type(e).__name__}: {e}")
|
||||
rc = 1
|
||||
finally:
|
||||
sys.argv = _saved_argv
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-info v1.5 — Analyze 1C role rights
|
||||
# role-info v1.6 — Analyze 1C role rights
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -55,7 +55,7 @@ if not os.path.isabs(rights_path):
|
||||
rights_path = os.path.join(os.getcwd(), rights_path)
|
||||
|
||||
if not os.path.isfile(rights_path):
|
||||
print(f"[ERROR] File not found: {rights_path}", file=sys.stderr)
|
||||
print(f"[ERROR] File not found: {rights_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Try to find metadata file for role name/synonym ---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# subsystem-info v1.5 — Compact summary of 1C subsystem structure
|
||||
# subsystem-info v1.6 — Compact summary of 1C subsystem structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -105,7 +105,7 @@ def load_subsystem_xml(xml_path):
|
||||
doc_root = tree.getroot()
|
||||
sub = doc_root.find("md:Subsystem", NS)
|
||||
if sub is None:
|
||||
print(f"[ERROR] Not a valid subsystem XML: {xml_path}", file=sys.stderr)
|
||||
print(f"[ERROR] Not a valid subsystem XML: {xml_path}")
|
||||
sys.exit(1)
|
||||
return {"Doc": doc_root, "Sub": sub}
|
||||
|
||||
@@ -408,7 +408,7 @@ if args.Mode == "tree":
|
||||
root_dir = subsystem_path
|
||||
else:
|
||||
if not os.path.isfile(subsystem_path):
|
||||
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
|
||||
print(f"[ERROR] File not found: {subsystem_path}")
|
||||
sys.exit(1)
|
||||
root_xml = subsystem_path
|
||||
|
||||
@@ -488,7 +488,7 @@ if args.Mode == "tree":
|
||||
if args.Name:
|
||||
xml_files = [f for f in xml_files if os.path.splitext(f)[0] == args.Name]
|
||||
if not xml_files:
|
||||
print(f"[ERROR] Subsystem '{args.Name}' not found in {root_dir}", file=sys.stderr)
|
||||
print(f"[ERROR] Subsystem '{args.Name}' not found in {root_dir}")
|
||||
sys.exit(1)
|
||||
for i, fname in enumerate(xml_files):
|
||||
build_tree_entry(os.path.join(root_dir, fname), "", i == len(xml_files) - 1, True)
|
||||
@@ -500,7 +500,7 @@ elif args.Mode == "ci":
|
||||
# Mode: ci -- CommandInterface.xml
|
||||
# ============================================================
|
||||
if os.path.isdir(subsystem_path):
|
||||
print("[ERROR] ci mode requires a subsystem .xml file, not a directory", file=sys.stderr)
|
||||
print("[ERROR] ci mode requires a subsystem .xml file, not a directory")
|
||||
sys.exit(1)
|
||||
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
||||
if not os.path.isfile(subsystem_path):
|
||||
@@ -511,7 +511,7 @@ elif args.Mode == "ci":
|
||||
if os.path.isfile(c):
|
||||
subsystem_path = c
|
||||
if not os.path.isfile(subsystem_path):
|
||||
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
|
||||
print(f"[ERROR] File not found: {subsystem_path}")
|
||||
sys.exit(1)
|
||||
|
||||
parsed = load_subsystem_xml(subsystem_path)
|
||||
@@ -535,7 +535,7 @@ else:
|
||||
elif os.path.isfile(sibling):
|
||||
subsystem_path = sibling
|
||||
else:
|
||||
print(f"[ERROR] No {dir_name}.xml found in directory. Use -Mode tree for directory listing.", file=sys.stderr)
|
||||
print(f"[ERROR] No {dir_name}.xml found in directory. Use -Mode tree for directory listing.")
|
||||
sys.exit(1)
|
||||
|
||||
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
||||
@@ -547,7 +547,7 @@ else:
|
||||
if os.path.isfile(c):
|
||||
subsystem_path = c
|
||||
if not os.path.isfile(subsystem_path):
|
||||
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
|
||||
print(f"[ERROR] File not found: {subsystem_path}")
|
||||
sys.exit(1)
|
||||
|
||||
parsed = load_subsystem_xml(subsystem_path)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# web-publish v1.8 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства)
|
||||
# web-publish v1.9 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
"""
|
||||
@@ -147,7 +147,7 @@ def main():
|
||||
ver = os.path.basename(os.path.dirname(v8_path))
|
||||
print(f'Auto-selected platform {ver}: {v8_path}')
|
||||
else:
|
||||
print('Error: платформа 1С не найдена. Укажите -V8Path', file=sys.stderr)
|
||||
print('Error: платформа 1С не найдена. Укажите -V8Path')
|
||||
sys.exit(1)
|
||||
elif os.path.isfile(v8_path):
|
||||
v8_path = os.path.dirname(v8_path)
|
||||
@@ -155,12 +155,12 @@ def main():
|
||||
# Validate wsap24.dll
|
||||
wsap_dll = os.path.join(v8_path, 'wsap24.dll')
|
||||
if not os.path.exists(wsap_dll):
|
||||
print(f'Error: wsap24.dll не найден в {v8_path}', file=sys.stderr)
|
||||
print(f'Error: wsap24.dll не найден в {v8_path}')
|
||||
sys.exit(1)
|
||||
|
||||
# --- Validate connection ---
|
||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
||||
print('Error: укажите -InfoBasePath или -InfoBaseServer + -InfoBaseRef', file=sys.stderr)
|
||||
print('Error: укажите -InfoBasePath или -InfoBaseServer + -InfoBaseRef')
|
||||
sys.exit(1)
|
||||
|
||||
# --- Resolve ApachePath ---
|
||||
@@ -213,7 +213,7 @@ def main():
|
||||
zip_url = f'https://www.apachelounge.com{zip_url}'
|
||||
print(f'Найдено: {zip_url}')
|
||||
else:
|
||||
print('Не удалось определить ссылку автоматически.', file=sys.stderr)
|
||||
print('Не удалось определить ссылку автоматически.')
|
||||
print(f'Скачайте вручную: {download_page}')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -221,7 +221,7 @@ def main():
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f'Error: не удалось скачать Apache: {e}', file=sys.stderr)
|
||||
print(f'Error: не удалось скачать Apache: {e}')
|
||||
print('Скачайте вручную: https://www.apachelounge.com/download/')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -244,7 +244,7 @@ def main():
|
||||
if found_inner:
|
||||
inner_dir = found_inner
|
||||
else:
|
||||
print('Error: каталог Apache24 не найден в архиве', file=sys.stderr)
|
||||
print('Error: каталог Apache24 не найден в архиве')
|
||||
sys.exit(1)
|
||||
|
||||
os.makedirs(apache_path, exist_ok=True)
|
||||
@@ -297,7 +297,7 @@ def main():
|
||||
app_name = app_name.lower()
|
||||
|
||||
if not app_name:
|
||||
print('Error: не удалось определить имя публикации. Укажите -AppName', file=sys.stderr)
|
||||
print('Error: не удалось определить имя публикации. Укажите -AppName')
|
||||
sys.exit(1)
|
||||
|
||||
print(f'Публикация: {app_name}')
|
||||
@@ -340,7 +340,7 @@ def main():
|
||||
# --- Update httpd.conf ---
|
||||
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
||||
if not os.path.exists(conf_file):
|
||||
print(f'Error: httpd.conf не найден: {conf_file}', file=sys.stderr)
|
||||
print(f'Error: httpd.conf не найден: {conf_file}')
|
||||
sys.exit(1)
|
||||
|
||||
with open(conf_file, 'r', encoding='utf-8-sig') as f:
|
||||
@@ -415,7 +415,7 @@ def main():
|
||||
holder_name = f'{holder_proc.name()} (PID: {holder_pid})'
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
holder_name = f'PID {holder_pid}'
|
||||
print(f'Error: порт {port} занят процессом {holder_name}', file=sys.stderr)
|
||||
print(f'Error: порт {port} занят процессом {holder_name}')
|
||||
print('Укажите другой порт: -Port 9090')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -451,7 +451,7 @@ def main():
|
||||
if httpd_check:
|
||||
print(f'Apache запущен (PID: {httpd_check[0].pid})')
|
||||
else:
|
||||
print('Apache не удалось запустить', file=sys.stderr)
|
||||
print('Apache не удалось запустить')
|
||||
# Run config test for diagnostics
|
||||
try:
|
||||
result = subprocess.run(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# web-stop v1.2 — Stop Apache HTTP Server
|
||||
# web-stop v1.3 — Stop Apache HTTP Server
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
"""
|
||||
@@ -130,7 +130,7 @@ def main():
|
||||
time.sleep(1)
|
||||
final = get_our_httpd(httpd_exe_norm)
|
||||
if final:
|
||||
print('Error: не удалось остановить Apache', file=sys.stderr)
|
||||
print('Error: не удалось остановить Apache')
|
||||
sys.exit(1)
|
||||
|
||||
print('Apache остановлен')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# web-unpublish v1.2 — Remove 1C web publication
|
||||
# web-unpublish v1.3 — Remove 1C web publication
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
"""
|
||||
@@ -75,13 +75,13 @@ def main():
|
||||
|
||||
# --- Validate params ---
|
||||
if not args.All and not args.AppName:
|
||||
print('Error: укажите -AppName или -All', file=sys.stderr)
|
||||
print('Error: укажите -AppName или -All')
|
||||
sys.exit(1)
|
||||
|
||||
# --- Read httpd.conf ---
|
||||
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
||||
if not os.path.exists(conf_file):
|
||||
print(f'Error: httpd.conf не найден: {conf_file}', file=sys.stderr)
|
||||
print(f'Error: httpd.conf не найден: {conf_file}')
|
||||
sys.exit(1)
|
||||
|
||||
with open(conf_file, 'r', encoding='utf-8-sig') as f:
|
||||
@@ -161,7 +161,7 @@ def main():
|
||||
if check:
|
||||
print('Apache перезапущен')
|
||||
else:
|
||||
print('Error: Apache не удалось перезапустить', file=sys.stderr)
|
||||
print('Error: Apache не удалось перезапустить')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('Публикаций не осталось — останавливаю Apache...')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# xdto-decompile v1.2 — Convert 1C XDTO package to XML Schema (XSD) (Python port)
|
||||
# xdto-decompile v1.3 — Convert 1C XDTO package to XML Schema (XSD) (Python port)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import os
|
||||
@@ -89,7 +89,7 @@ elif os.path.isdir(package_path):
|
||||
md_path = m
|
||||
|
||||
if not bin_path:
|
||||
print(f"Не найден Ext/Package.bin для пути: {package_path}", file=sys.stderr)
|
||||
print(f"Не найден Ext/Package.bin для пути: {package_path}")
|
||||
sys.exit(1)
|
||||
|
||||
doc = _parse_xml(bin_path)
|
||||
@@ -101,7 +101,7 @@ def local(el):
|
||||
|
||||
|
||||
if local(pkg) != "package":
|
||||
print(f"Ожидался корневой <package>, получен <{local(pkg)}>", file=sys.stderr)
|
||||
print(f"Ожидался корневой <package>, получен <{local(pkg)}>")
|
||||
sys.exit(1)
|
||||
|
||||
target_ns = pkg.get("targetNamespace")
|
||||
|
||||
@@ -105,6 +105,11 @@ Switch-параметры (`-NoValidate`) → `action='store_true'`.
|
||||
| `Write-Host "text"` | `print("text")` |
|
||||
| `Write-Error "text"` | `print("text", file=sys.stderr)` |
|
||||
|
||||
> Поток обязателен к соблюдению, а не рекомендателен: харнесс не чередует stdout и stderr,
|
||||
> а группирует их — вердикт, ушедший не в тот поток, печатается ПЕРЕД строками, которые его
|
||||
> объясняют, и причинный порядок вывода переворачивается. Держит инвариант
|
||||
> `tests/skills/check-error-streams.mjs`.
|
||||
|
||||
## lxml vs stdlib
|
||||
|
||||
- **Compile/init скрипты** (строковая сборка): только stdlib
|
||||
|
||||
@@ -111,6 +111,7 @@ node tests/skills/check-inline-drift.mjs --list # реестр: семья →
|
||||
| `check-form-purposes.mjs` | таблица назначений форм в `form-add`: состав видов, свойство «основная форма» и оба порта сходятся с `docs/1c-form-spec.md` |
|
||||
| `check-positional-binding.mjs` | read-only навыки (`*-info` / `*-validate` / `cfe-diff`): позиционным остаётся только путь ко входу, лишний позиционный аргумент не перезаписывает указанный файл |
|
||||
| `check-agent-portability.mjs` | исходники навыков не привязаны к конкретному AI-агенту: единственная разрешённая форма — плейсхолдер `${CLAUDE_SKILL_DIR}/`, который разворачивает `scripts/switch.py` |
|
||||
| `check-error-streams.mjs` | сообщения об ошибках идут в один и тот же поток в обоих портах навыка (соответствие из `docs/python-porting-guide.md`) |
|
||||
| `check-nonascii-fs.mjs` | `fsutil`: удаление и копирование держат не-ASCII пути (кириллический `%TEMP%`, кириллические имена объектов 1С), обе копии модуля не разошлись |
|
||||
|
||||
`check-inline-drift.mjs` держит реестр семей внутри себя: у каждой семьи перечислены варианты, у
|
||||
|
||||
@@ -17,6 +17,7 @@ const GUARDS = [
|
||||
['check-form-purposes.mjs', 'назначения форм в form-add: согласованы со спецификацией и между портами'],
|
||||
['check-positional-binding.mjs', 'read-only навыки: позиционным остаётся только путь ко входу'],
|
||||
['check-agent-portability.mjs', 'исходники навыков: без привязки к конкретному AI-агенту'],
|
||||
['check-error-streams.mjs', 'сообщения об ошибках: один и тот же поток в обоих портах'],
|
||||
['check-nonascii-fs.mjs', 'fsutil: удаление и копирование держат не-ASCII пути, копии не разошлись'],
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
// Инвариант: сообщения об ошибках уходят в ОДИН И ТОТ ЖЕ поток в обоих портах навыка.
|
||||
//
|
||||
// Соответствие задано в docs/python-porting-guide.md: `Write-Host` → `print`,
|
||||
// `Write-Error` → `print(..., file=sys.stderr)`. Тринадцать навыков группы db-*/epf-*/web-*
|
||||
// его нарушали: PS писал ошибки через Write-Host (stdout), а py-порт — в stderr. Счётчики
|
||||
// совпадали один в один (14↔14, 19↔19, 13↔13) — то есть сообщения были те же, разъехался
|
||||
// только поток.
|
||||
//
|
||||
// Почему это не косметика: харнесс не чередует потоки, а группирует — сначала весь stderr,
|
||||
// потом весь stdout. Из-за этого в py-порте вердикт «Error dumping configuration (code: 1)»
|
||||
// печатался ПЕРЕД строками, которые его объясняют, а причина из лога платформы оказывалась
|
||||
// в самом низу. Причинный порядок вывода переворачивался, и порт для macOS читался хуже
|
||||
// того, что работает на Windows.
|
||||
//
|
||||
// Почему не ловилось тестами: текст ошибки сверяют только кейсы со строковым `expectError`,
|
||||
// а он смотрит в stderr — поэтому такие кейсы есть лишь у семейства, где потоки сходятся.
|
||||
// В db-* их ноль, и дыра пряталась за собственным следствием.
|
||||
//
|
||||
// Запуск: node tests/skills/check-error-streams.mjs
|
||||
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||
const SKILLS = join(ROOT, '.claude', 'skills');
|
||||
|
||||
const errors = [];
|
||||
let checked = 0;
|
||||
|
||||
for (const skill of readdirSync(SKILLS)) {
|
||||
const dir = join(SKILLS, skill, 'scripts');
|
||||
if (!existsSync(dir)) continue;
|
||||
for (const file of readdirSync(dir)) {
|
||||
if (!file.endsWith('.ps1')) continue;
|
||||
const base = file.slice(0, -4);
|
||||
const pyPath = join(dir, base + '.py');
|
||||
if (!existsSync(pyPath)) continue;
|
||||
|
||||
// Форм записи в stderr по две с каждой стороны, и считать надо обе: PS пишет через
|
||||
// Write-Error и через [Console]::Error.WriteLine, py — через file=sys.stderr и
|
||||
// sys.stderr.write. Комментарии выбрасываем: в meta-remove.ps1 слово Write-Error стоит
|
||||
// в пояснении «почему НЕ Write-Error», и по одной форме гард давал ложную тревогу.
|
||||
const strip = (text, marker) => text.split('\n')
|
||||
.filter(l => !l.trimStart().startsWith(marker)).join('\n');
|
||||
const ps = strip(readFileSync(join(dir, file), 'utf8').replace(/^/, ''), '#');
|
||||
const py = strip(readFileSync(pyPath, 'utf8'), '#');
|
||||
const psErr = (ps.match(/Write-Error|Console\]::Error\.Write/g) || []).length;
|
||||
const pyErr = (py.match(/file=sys\.stderr|sys\.stderr\.write/g) || []).length;
|
||||
checked++;
|
||||
|
||||
if (psErr === 0 && pyErr > 0) {
|
||||
errors.push(`${skill}/${base}: PS не использует Write-Error, а py пишет в stderr `
|
||||
+ `(${pyErr} мест). Ошибки должны идти в тот же поток, что и в PS — убрать file=sys.stderr.`);
|
||||
}
|
||||
if (psErr > 0 && pyErr === 0) {
|
||||
errors.push(`${skill}/${base}: PS использует Write-Error (${psErr} мест), а py пишет всё `
|
||||
+ `в stdout. Ошибки должны идти в тот же поток — добавить file=sys.stderr.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Проверено пар портов: ${checked}`);
|
||||
if (errors.length === 0) {
|
||||
console.log('OK — потоки сообщений об ошибках совпадают в обоих портах.');
|
||||
process.exit(0);
|
||||
}
|
||||
console.log(`\n${errors.length} РАСХОЖДЕНИЙ:`);
|
||||
for (const e of errors) console.log(` [ERROR] ${e}`);
|
||||
console.log('\nСоответствие потоков: docs/python-porting-guide.md, таблица маппинга PS → Python.');
|
||||
process.exit(1);
|
||||
Reference in New Issue
Block a user