mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-02 16:20:54 +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
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -61,11 +61,11 @@ if os.path.isdir(config_path):
|
|||||||
if os.path.isfile(candidate):
|
if os.path.isfile(candidate):
|
||||||
config_path = candidate
|
config_path = candidate
|
||||||
else:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
if not os.path.isfile(config_path):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Load XML ---
|
# --- Load XML ---
|
||||||
@@ -82,12 +82,12 @@ NS = {
|
|||||||
|
|
||||||
md_root = xml_root # root is MetaDataObject itself
|
md_root = xml_root # root is MetaDataObject itself
|
||||||
if etree.QName(md_root.tag).localname != "MetaDataObject":
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
cfg_node = md_root.find("md:Configuration", NS)
|
cfg_node = md_root.find("md:Configuration", NS)
|
||||||
if cfg_node is None:
|
if cfg_node is None:
|
||||||
print("[ERROR] No <Configuration> element found", file=sys.stderr)
|
print("[ERROR] No <Configuration> element found")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
version = md_root.get("version", "")
|
version = md_root.get("version", "")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -313,7 +309,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -421,15 +417,15 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate template ---
|
# --- Validate template ---
|
||||||
if args.UseTemplate and not os.path.exists(args.UseTemplate):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- ibcmd branch (file infobase only) ---
|
# --- ibcmd branch (file infobase only) ---
|
||||||
@@ -456,10 +452,9 @@ def main():
|
|||||||
print(
|
print(
|
||||||
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
||||||
"— information base was not created",
|
"— information base was not created",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
else:
|
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)
|
print_platform_output(result)
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
@@ -516,10 +511,9 @@ def main():
|
|||||||
print(
|
print(
|
||||||
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
f"Error: exit code 0 but 1Cv8.1CD is missing or empty at {args.InfoBasePath} "
|
||||||
"— information base was not created",
|
"— information base was not created",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
else:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -442,10 +438,10 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Ensure output directory exists ---
|
# --- Ensure output directory exists ---
|
||||||
@@ -456,7 +452,7 @@ def main():
|
|||||||
# --- ibcmd branch (file infobase only) ---
|
# --- ibcmd branch (file infobase only) ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if args.AllExtensions:
|
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)
|
sys.exit(1)
|
||||||
arguments = ["infobase", "config", "save", f"--db-path={args.InfoBasePath}"]
|
arguments = ["infobase", "config", "save", f"--db-path={args.InfoBasePath}"]
|
||||||
if args.Extension:
|
if args.Extension:
|
||||||
@@ -479,9 +475,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
else:
|
||||||
print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr)
|
print(f"Error dumping configuration (code: {exit_code})")
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -529,9 +525,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
print(f"Configuration dumped successfully to: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -440,10 +436,10 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Ensure output directory exists ---
|
# --- Ensure output directory exists ---
|
||||||
@@ -472,9 +468,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Information base dumped successfully to: {args.OutputFile}")
|
print(f"Information base dumped successfully to: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
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)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -516,9 +512,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Information base dumped successfully to: {args.OutputFile}")
|
print(f"Information base dumped successfully to: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -205,7 +205,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -281,14 +279,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -330,14 +326,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -368,7 +364,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -385,7 +381,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -545,10 +541,10 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate Partial mode ---
|
# --- Validate Partial mode ---
|
||||||
@@ -556,7 +552,7 @@ def main():
|
|||||||
# то, что уже напечатал другой навык (например /db-repo update со списком полученных объектов).
|
# то, что уже напечатал другой навык (например /db-repo update со списком полученных объектов).
|
||||||
if args.ObjectsFile:
|
if args.ObjectsFile:
|
||||||
if not os.path.exists(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)
|
sys.exit(1)
|
||||||
with open(args.ObjectsFile, encoding="utf-8-sig") as f:
|
with open(args.ObjectsFile, encoding="utf-8-sig") as f:
|
||||||
from_file = [s.strip() for s in f.read().splitlines()
|
from_file = [s.strip() for s in f.read().splitlines()
|
||||||
@@ -569,7 +565,7 @@ def main():
|
|||||||
if args.Mode == "UpdateInfo":
|
if args.Mode == "UpdateInfo":
|
||||||
# Не «шире/уже», а другая операция: обновление ConfigDumpInfo без выгрузки файлов.
|
# Не «шире/уже», а другая операция: обновление ConfigDumpInfo без выгрузки файлов.
|
||||||
print("Error: -Mode UpdateInfo does not take an object list — it only refreshes "
|
print("Error: -Mode UpdateInfo does not take an object list — it only refreshes "
|
||||||
"ConfigDumpInfo.xml", file=sys.stderr)
|
"ConfigDumpInfo.xml")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if args.Mode in ("Full", "Changes"):
|
if args.Mode in ("Full", "Changes"):
|
||||||
print("[note] перечислены объекты — выгружаются только они; -Mode %s не применён"
|
print("[note] перечислены объекты — выгружаются только они; -Mode %s не применён"
|
||||||
@@ -578,7 +574,7 @@ def main():
|
|||||||
if not args.Mode:
|
if not args.Mode:
|
||||||
args.Mode = "Changes"
|
args.Mode = "Changes"
|
||||||
if args.Mode == "Partial" and not args.Objects:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Create output dir if needed ---
|
# --- Create output dir if needed ---
|
||||||
@@ -589,12 +585,12 @@ def main():
|
|||||||
# --- ibcmd branch (file infobase only; hierarchical Full/Changes) ---
|
# --- ibcmd branch (file infobase only; hierarchical Full/Changes) ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if args.Format == "Plain":
|
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)
|
sys.exit(1)
|
||||||
if args.AllExtensions:
|
if args.AllExtensions:
|
||||||
arguments = ["infobase", "config", "export", "all-extensions", args.ConfigDir, f"--db-path={args.InfoBasePath}"]
|
arguments = ["infobase", "config", "export", "all-extensions", args.ConfigDir, f"--db-path={args.InfoBasePath}"]
|
||||||
elif args.Mode == "UpdateInfo":
|
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)
|
sys.exit(1)
|
||||||
elif args.Mode == "Partial":
|
elif args.Mode == "Partial":
|
||||||
obj_list = [o.strip() for o in args.Objects.split(",") if o.strip()]
|
obj_list = [o.strip() for o in args.Objects.split(",") if o.strip()]
|
||||||
@@ -624,9 +620,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Configuration exported successfully to: {args.ConfigDir}")
|
print(f"Configuration exported successfully to: {args.ConfigDir}")
|
||||||
elif out_missing:
|
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:
|
else:
|
||||||
print(f"Error exporting configuration (code: {exit_code})", file=sys.stderr)
|
print(f"Error exporting configuration (code: {exit_code})")
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -703,9 +699,9 @@ def main():
|
|||||||
print("Dump completed successfully")
|
print("Dump completed successfully")
|
||||||
print(f"Configuration dumped to: {args.ConfigDir}")
|
print(f"Configuration dumped to: {args.ConfigDir}")
|
||||||
elif out_missing:
|
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:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -460,21 +456,21 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate input file ---
|
# --- Validate input file ---
|
||||||
if not os.path.isfile(args.InputFile):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- ibcmd branch (file infobase only) ---
|
# --- ibcmd branch (file infobase only) ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if args.AllExtensions:
|
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)
|
sys.exit(1)
|
||||||
arguments = ["infobase", "config", "load", f"--db-path={args.InfoBasePath}"]
|
arguments = ["infobase", "config", "load", f"--db-path={args.InfoBasePath}"]
|
||||||
if args.Extension:
|
if args.Extension:
|
||||||
@@ -493,7 +489,7 @@ def main():
|
|||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
print(f"Configuration loaded successfully from: {args.InputFile}")
|
print(f"Configuration loaded successfully from: {args.InputFile}")
|
||||||
else:
|
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)
|
sys.exit(result.returncode)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -537,7 +533,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Configuration loaded successfully from: {args.InputFile}")
|
print(f"Configuration loaded successfully from: {args.InputFile}")
|
||||||
else:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -460,15 +456,15 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate input file ---
|
# --- Validate input file ---
|
||||||
if not os.path.isfile(args.InputFile):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- ibcmd branch (file infobase only) ---
|
# --- ibcmd branch (file infobase only) ---
|
||||||
@@ -490,7 +486,7 @@ def main():
|
|||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
print(f"Information base restored successfully from: {args.InputFile}")
|
print(f"Information base restored successfully from: {args.InputFile}")
|
||||||
else:
|
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)
|
sys.exit(result.returncode)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -532,7 +528,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Information base restored successfully from: {args.InputFile}")
|
print(f"Information base restored successfully from: {args.InputFile}")
|
||||||
else:
|
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):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -226,7 +226,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -302,14 +300,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -351,14 +347,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -389,7 +385,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -406,7 +402,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
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"
|
engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8"
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Resolve additional arguments for the selected engine ---
|
# --- Resolve additional arguments for the selected engine ---
|
||||||
@@ -655,19 +651,19 @@ def main():
|
|||||||
|
|
||||||
# --- Validate config dir ---
|
# --- Validate config dir ---
|
||||||
if not os.path.exists(args.ConfigDir):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate Commit mode ---
|
# --- Validate Commit mode ---
|
||||||
if args.Source == "Commit" and not args.CommitRange:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Check git ---
|
# --- Check git ---
|
||||||
try:
|
try:
|
||||||
subprocess.run(["git", "--version"], capture_output=True, text=True, check=True)
|
subprocess.run(["git", "--version"], capture_output=True, text=True, check=True)
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
print("Error: git not found in PATH", file=sys.stderr)
|
print("Error: git not found in PATH")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Get changed files from Git ---
|
# --- Get changed files from Git ---
|
||||||
@@ -746,10 +742,10 @@ def main():
|
|||||||
config_files.append(rel_path)
|
config_files.append(rel_path)
|
||||||
|
|
||||||
if support_skipped:
|
if support_skipped:
|
||||||
print("[ВНИМАНИЕ] Состояние поддержки изменено в коммите, но частично не загружается (исключено):", file=sys.stderr)
|
print("[ВНИМАНИЕ] Состояние поддержки изменено в коммите, но частично не загружается (исключено):")
|
||||||
for sf in support_skipped:
|
for sf in support_skipped:
|
||||||
print(f" - {sf}", file=sys.stderr)
|
print(f" - {sf}")
|
||||||
print(" Смена состояния поддержки применяется только полной загрузкой (db-load-xml -Mode Full).", file=sys.stderr)
|
print(" Смена состояния поддержки применяется только полной загрузкой (db-load-xml -Mode Full).")
|
||||||
|
|
||||||
if len(config_files) == 0:
|
if len(config_files) == 0:
|
||||||
print("No configuration files found in changes")
|
print("No configuration files found in changes")
|
||||||
@@ -773,10 +769,10 @@ def main():
|
|||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
# --- ibcmd branch (file infobase only; import specific files) ---
|
# --- ibcmd branch (file infobase only; import specific files) ---
|
||||||
if args.Format == "Plain":
|
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)
|
sys.exit(1)
|
||||||
if args.AllExtensions:
|
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)
|
sys.exit(1)
|
||||||
arguments = ["infobase", "config", "import", "files"] + config_files
|
arguments = ["infobase", "config", "import", "files"] + config_files
|
||||||
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
|
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)}")
|
print(f"Running: ibcmd {_redact(' '.join(format_args_for_display(arguments, engine)), args.Password, args.UserName)}")
|
||||||
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
||||||
if result.returncode != 0:
|
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)
|
sys.exit(result.returncode)
|
||||||
print(f"Changes loaded successfully ({len(config_files)} files)")
|
print(f"Changes loaded successfully ({len(config_files)} files)")
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
@@ -811,7 +807,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print("Database configuration updated successfully")
|
print("Database configuration updated successfully")
|
||||||
else:
|
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)
|
print_platform_output(ar)
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
@@ -873,7 +869,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print("Load completed successfully")
|
print("Load completed successfully")
|
||||||
else:
|
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 = ""
|
log_content = ""
|
||||||
if os.path.isfile(out_file):
|
if os.path.isfile(out_file):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -226,7 +226,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -302,14 +300,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -351,14 +347,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -389,7 +385,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -406,7 +402,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -624,15 +620,15 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate config dir ---
|
# --- Validate config dir ---
|
||||||
if not os.path.exists(args.ConfigDir):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate Partial mode ---
|
# --- Validate Partial mode ---
|
||||||
@@ -645,13 +641,13 @@ def main():
|
|||||||
if not args.Mode:
|
if not args.Mode:
|
||||||
args.Mode = "Full"
|
args.Mode = "Full"
|
||||||
if args.Mode == "Partial" and not args.Files and not args.ListFile:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- ibcmd branch (file infobase only; hierarchical full-directory import) ---
|
# --- ibcmd branch (file infobase only; hierarchical full-directory import) ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if args.Format == "Plain":
|
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)
|
sys.exit(1)
|
||||||
if args.AllExtensions:
|
if args.AllExtensions:
|
||||||
arguments = ["infobase", "config", "import", "all-extensions", args.ConfigDir, f"--db-path={args.InfoBasePath}"]
|
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)
|
# partial: import specific files (relative to ConfigDir)
|
||||||
if args.ListFile:
|
if args.ListFile:
|
||||||
if not os.path.isfile(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)
|
sys.exit(1)
|
||||||
with open(args.ListFile, encoding="utf-8-sig") as f:
|
with open(args.ListFile, encoding="utf-8-sig") as f:
|
||||||
file_list = [ln.strip() for ln in f if ln.strip()]
|
file_list = [ln.strip() for ln in f if ln.strip()]
|
||||||
@@ -668,7 +664,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
file_list = []
|
file_list = []
|
||||||
if not 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)
|
sys.exit(1)
|
||||||
arguments = ["infobase", "config", "import", "files"] + file_list
|
arguments = ["infobase", "config", "import", "files"] + file_list
|
||||||
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
|
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)}")
|
print(f"Running: ibcmd {_redact(' '.join(format_args_for_display(arguments, engine)), args.Password, args.UserName)}")
|
||||||
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
result = run_ibcmd([v8path] + arguments, bool(args.UserName))
|
||||||
if result.returncode != 0:
|
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)
|
sys.exit(result.returncode)
|
||||||
print(f"Configuration loaded successfully from: {args.ConfigDir}")
|
print(f"Configuration loaded successfully from: {args.ConfigDir}")
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
@@ -708,7 +704,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print("Database configuration updated successfully")
|
print("Database configuration updated successfully")
|
||||||
else:
|
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)
|
print_platform_output(ar)
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
@@ -745,7 +741,7 @@ def main():
|
|||||||
# Build list file
|
# Build list file
|
||||||
if args.ListFile:
|
if args.ListFile:
|
||||||
if not os.path.isfile(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)
|
sys.exit(1)
|
||||||
with open(args.ListFile, encoding="utf-8-sig") as f:
|
with open(args.ListFile, encoding="utf-8-sig") as f:
|
||||||
raw_list = [ln.strip() for ln in f if ln.strip()]
|
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)]
|
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)]
|
file_list = [x for x in raw_list if not support_re.search(x)]
|
||||||
if support_files:
|
if support_files:
|
||||||
print("[ВНИМАНИЕ] Служебные файлы состояния поддержки исключены из частичной загрузки (частично не грузятся):", file=sys.stderr)
|
print("[ВНИМАНИЕ] Служебные файлы состояния поддержки исключены из частичной загрузки (частично не грузятся):")
|
||||||
for sf in support_files:
|
for sf in support_files:
|
||||||
print(f" - {sf}", file=sys.stderr)
|
print(f" - {sf}")
|
||||||
print(" Смена состояния поддержки применяется только полной загрузкой: -Mode Full.", file=sys.stderr)
|
print(" Смена состояния поддержки применяется только полной загрузкой: -Mode Full.")
|
||||||
if not file_list:
|
if not file_list:
|
||||||
print("Error: после исключения служебных файлов поддержки загружать нечего. Для смены поддержки используйте -Mode Full.", file=sys.stderr)
|
print("Error: после исключения служебных файлов поддержки загружать нечего. Для смены поддержки используйте -Mode Full.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
generated_list_file = os.path.join(temp_dir, "load_list.txt")
|
generated_list_file = os.path.join(temp_dir, "load_list.txt")
|
||||||
with open(generated_list_file, "w", encoding="utf-8-sig") as f:
|
with open(generated_list_file, "w", encoding="utf-8-sig") as f:
|
||||||
@@ -819,7 +815,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print("Load completed successfully")
|
print("Load completed successfully")
|
||||||
else:
|
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:
|
if log_content:
|
||||||
print("--- Log ---")
|
print("--- Log ---")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
||||||
"""Работа с хранилищем конфигурации 1С.
|
"""Работа с хранилищем конфигурации 1С.
|
||||||
@@ -132,7 +132,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -224,14 +222,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -254,7 +252,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -271,7 +269,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -117,7 +117,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -193,14 +191,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -225,7 +221,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -260,14 +256,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -327,7 +323,7 @@ def main():
|
|||||||
|
|
||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Build arguments ---
|
# --- Build arguments ---
|
||||||
@@ -377,7 +373,7 @@ def main():
|
|||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
rc = proc.poll()
|
rc = proc.poll()
|
||||||
if rc is not None:
|
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)
|
sys.exit(rc if rc and rc > 0 else 1)
|
||||||
print(f"PID: {proc.pid}")
|
print(f"PID: {proc.pid}")
|
||||||
print("1C:Enterprise launched")
|
print("1C:Enterprise launched")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -205,7 +205,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -281,14 +279,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -330,14 +326,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -368,7 +364,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -385,7 +381,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -586,16 +582,16 @@ def main():
|
|||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- ibcmd branch (file infobase only) ---
|
# --- ibcmd branch (file infobase only) ---
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if args.AllExtensions:
|
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)
|
sys.exit(1)
|
||||||
arguments = ["infobase", "config", "apply", f"--db-path={args.InfoBasePath}", "--force"]
|
arguments = ["infobase", "config", "apply", f"--db-path={args.InfoBasePath}", "--force"]
|
||||||
if args.Dynamic == "+":
|
if args.Dynamic == "+":
|
||||||
@@ -617,7 +613,7 @@ def main():
|
|||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
print("Database configuration updated successfully")
|
print("Database configuration updated successfully")
|
||||||
else:
|
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)
|
sys.exit(result.returncode)
|
||||||
|
|
||||||
# --- Temp dir ---
|
# --- Temp dir ---
|
||||||
@@ -674,7 +670,7 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print("Database configuration updated successfully")
|
print("Database configuration updated successfully")
|
||||||
else:
|
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 = ""
|
log_content = ""
|
||||||
if os.path.isfile(out_file):
|
if os.path.isfile(out_file):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
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)
|
extra_args = resolve_extra_args(engine, v8_extra, ibcmd_extra, arg_hints)
|
||||||
if engine == "ibcmd" and args.InfoBaseServer and args.InfoBaseRef:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Auto-create stub database if no connection specified ---
|
# --- Auto-create stub database if no connection specified ---
|
||||||
@@ -461,14 +457,14 @@ def main():
|
|||||||
stub_cmd += ["-AdditionalIbcmdArguments"] + list(ibcmd_extra)
|
stub_cmd += ["-AdditionalIbcmdArguments"] + list(ibcmd_extra)
|
||||||
result = subprocess.run(stub_cmd, capture_output=False)
|
result = subprocess.run(stub_cmd, capture_output=False)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print("Error: failed to create stub database", file=sys.stderr)
|
print("Error: failed to create stub database")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
args.InfoBasePath = auto_base_path
|
args.InfoBasePath = auto_base_path
|
||||||
auto_created_base = auto_base_path
|
auto_created_base = auto_base_path
|
||||||
|
|
||||||
# --- Validate source file ---
|
# --- Validate source file ---
|
||||||
if not os.path.isfile(args.SourceFile):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Ensure output directory exists ---
|
# --- Ensure output directory exists ---
|
||||||
@@ -502,9 +498,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"External data processor/report built successfully: {args.OutputFile}")
|
print(f"External data processor/report built successfully: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
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)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
# --- Build arguments ---
|
# --- Build arguments ---
|
||||||
@@ -541,9 +537,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Build completed successfully: {args.OutputFile}")
|
print(f"Build completed successfully: {args.OutputFile}")
|
||||||
elif out_missing:
|
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:
|
else:
|
||||||
print(f"Error building (code: {exit_code})", file=sys.stderr)
|
print(f"Error building (code: {exit_code})")
|
||||||
|
|
||||||
if os.path.isfile(out_file):
|
if os.path.isfile(out_file):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -120,7 +120,6 @@ def assert_extra_args(extra, engine, hints):
|
|||||||
print(
|
print(
|
||||||
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
f"Error: '{tok}' is a positional token — pass values as --key=value "
|
||||||
f"({param} cannot extend the ibcmd command)",
|
f"({param} cannot extend the ibcmd command)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for k in owned:
|
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 ""
|
hint = f" (use {hints[k]})" if hints and k in hints else ""
|
||||||
print(
|
print(
|
||||||
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
f"Error: {k} is controlled by the skill and cannot be passed via {param}{hint}",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -196,14 +194,12 @@ def resolve_extra_args(engine, v8_extra, ibcmd_extra, hints):
|
|||||||
print(
|
print(
|
||||||
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
"Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd "
|
||||||
"(use -AdditionalIbcmdArguments)",
|
"(use -AdditionalIbcmdArguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine != "ibcmd" and ibcmd_extra:
|
if engine != "ibcmd" and ibcmd_extra:
|
||||||
print(
|
print(
|
||||||
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
"Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 "
|
||||||
"(use -AdditionalV8Arguments)",
|
"(use -AdditionalV8Arguments)",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
@@ -245,14 +241,14 @@ def resolve_v8path(v8path):
|
|||||||
v8path = max(candidates, key=_version_key)
|
v8path = max(candidates, key=_version_key)
|
||||||
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
print(f"Auto-selected platform {_version_dir(v8path)}: {v8path}")
|
||||||
else:
|
else:
|
||||||
print("Error: 1C executable not found. Specify -V8Path", file=sys.stderr)
|
print("Error: 1C executable not found. Specify -V8Path")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if os.path.isdir(v8path):
|
if os.path.isdir(v8path):
|
||||||
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
# PY-only: на *nix исполняемый называется "1cv8" (без .exe); ibcmd — только явным путём.
|
||||||
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
exe = "1cv8.exe" if os.name == "nt" else "1cv8"
|
||||||
v8path = os.path.join(v8path, exe)
|
v8path = os.path.join(v8path, exe)
|
||||||
if not os.path.isfile(v8path):
|
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)
|
sys.exit(1)
|
||||||
return v8path
|
return v8path
|
||||||
|
|
||||||
@@ -283,7 +279,7 @@ def assert_infobase_exists(path):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
if not os.path.isfile(os.path.join(path, "1Cv8.1CD")):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ def clean_path(value, param=""):
|
|||||||
if len(v) > 3 and v[-1] in "\\/":
|
if len(v) > 3 and v[-1] in "\\/":
|
||||||
v = v[:-1]
|
v = v[:-1]
|
||||||
if '"' in v:
|
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)
|
sys.exit(1)
|
||||||
return v
|
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).
|
that residual case is flagged via IBCMD_NOUSER_HINT (model-facing).
|
||||||
"""
|
"""
|
||||||
if warn_no_user and os.name == "nt" and not has_username:
|
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()
|
sys.stderr.flush()
|
||||||
r = subprocess.run(cmd, input=b"", capture_output=True)
|
r = subprocess.run(cmd, input=b"", capture_output=True)
|
||||||
r.stdout = decode_platform_bytes(r.stdout)
|
r.stdout = decode_platform_bytes(r.stdout)
|
||||||
@@ -448,20 +444,20 @@ def main():
|
|||||||
|
|
||||||
# --- Validate database connection ---
|
# --- Validate database connection ---
|
||||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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.")
|
print("Dump in an empty database loses reference types (CatalogRef, DocumentRef, etc.) irreversibly.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if engine == "ibcmd":
|
if engine == "ibcmd":
|
||||||
if not args.InfoBasePath:
|
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)
|
sys.exit(1)
|
||||||
if args.Format == "Plain":
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate input file ---
|
# --- Validate input file ---
|
||||||
if not os.path.isfile(args.InputFile):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Ensure output directory exists ---
|
# --- Ensure output directory exists ---
|
||||||
@@ -493,9 +489,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"External data processor/report dumped successfully to: {args.OutputDir}")
|
print(f"External data processor/report dumped successfully to: {args.OutputDir}")
|
||||||
elif out_missing:
|
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:
|
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)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
# --- Build arguments ---
|
# --- Build arguments ---
|
||||||
@@ -533,9 +529,9 @@ def main():
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
print(f"Dump completed successfully to: {args.OutputDir}")
|
print(f"Dump completed successfully to: {args.OutputDir}")
|
||||||
elif out_missing:
|
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:
|
else:
|
||||||
print(f"Error dumping (code: {exit_code})", file=sys.stderr)
|
print(f"Error dumping (code: {exit_code})")
|
||||||
|
|
||||||
if os.path.isfile(out_file):
|
if os.path.isfile(out_file):
|
||||||
try:
|
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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -80,7 +80,7 @@ if len(path_list) > 1:
|
|||||||
except Exception as e:
|
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
|
rc = 1
|
||||||
finally:
|
finally:
|
||||||
sys.argv = _saved_argv
|
sys.argv = _saved_argv
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -55,7 +55,7 @@ if not os.path.isabs(rights_path):
|
|||||||
rights_path = os.path.join(os.getcwd(), rights_path)
|
rights_path = os.path.join(os.getcwd(), rights_path)
|
||||||
|
|
||||||
if not os.path.isfile(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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Try to find metadata file for role name/synonym ---
|
# --- Try to find metadata file for role name/synonym ---
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -105,7 +105,7 @@ def load_subsystem_xml(xml_path):
|
|||||||
doc_root = tree.getroot()
|
doc_root = tree.getroot()
|
||||||
sub = doc_root.find("md:Subsystem", NS)
|
sub = doc_root.find("md:Subsystem", NS)
|
||||||
if sub is None:
|
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)
|
sys.exit(1)
|
||||||
return {"Doc": doc_root, "Sub": sub}
|
return {"Doc": doc_root, "Sub": sub}
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ if args.Mode == "tree":
|
|||||||
root_dir = subsystem_path
|
root_dir = subsystem_path
|
||||||
else:
|
else:
|
||||||
if not os.path.isfile(subsystem_path):
|
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)
|
sys.exit(1)
|
||||||
root_xml = subsystem_path
|
root_xml = subsystem_path
|
||||||
|
|
||||||
@@ -488,7 +488,7 @@ if args.Mode == "tree":
|
|||||||
if args.Name:
|
if args.Name:
|
||||||
xml_files = [f for f in xml_files if os.path.splitext(f)[0] == args.Name]
|
xml_files = [f for f in xml_files if os.path.splitext(f)[0] == args.Name]
|
||||||
if not xml_files:
|
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)
|
sys.exit(1)
|
||||||
for i, fname in enumerate(xml_files):
|
for i, fname in enumerate(xml_files):
|
||||||
build_tree_entry(os.path.join(root_dir, fname), "", i == len(xml_files) - 1, True)
|
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
|
# Mode: ci -- CommandInterface.xml
|
||||||
# ============================================================
|
# ============================================================
|
||||||
if os.path.isdir(subsystem_path):
|
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)
|
sys.exit(1)
|
||||||
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
||||||
if not os.path.isfile(subsystem_path):
|
if not os.path.isfile(subsystem_path):
|
||||||
@@ -511,7 +511,7 @@ elif args.Mode == "ci":
|
|||||||
if os.path.isfile(c):
|
if os.path.isfile(c):
|
||||||
subsystem_path = c
|
subsystem_path = c
|
||||||
if not os.path.isfile(subsystem_path):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
parsed = load_subsystem_xml(subsystem_path)
|
parsed = load_subsystem_xml(subsystem_path)
|
||||||
@@ -535,7 +535,7 @@ else:
|
|||||||
elif os.path.isfile(sibling):
|
elif os.path.isfile(sibling):
|
||||||
subsystem_path = sibling
|
subsystem_path = sibling
|
||||||
else:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
|
||||||
@@ -547,7 +547,7 @@ else:
|
|||||||
if os.path.isfile(c):
|
if os.path.isfile(c):
|
||||||
subsystem_path = c
|
subsystem_path = c
|
||||||
if not os.path.isfile(subsystem_path):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
parsed = load_subsystem_xml(subsystem_path)
|
parsed = load_subsystem_xml(subsystem_path)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -147,7 +147,7 @@ def main():
|
|||||||
ver = os.path.basename(os.path.dirname(v8_path))
|
ver = os.path.basename(os.path.dirname(v8_path))
|
||||||
print(f'Auto-selected platform {ver}: {v8_path}')
|
print(f'Auto-selected platform {ver}: {v8_path}')
|
||||||
else:
|
else:
|
||||||
print('Error: платформа 1С не найдена. Укажите -V8Path', file=sys.stderr)
|
print('Error: платформа 1С не найдена. Укажите -V8Path')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif os.path.isfile(v8_path):
|
elif os.path.isfile(v8_path):
|
||||||
v8_path = os.path.dirname(v8_path)
|
v8_path = os.path.dirname(v8_path)
|
||||||
@@ -155,12 +155,12 @@ def main():
|
|||||||
# Validate wsap24.dll
|
# Validate wsap24.dll
|
||||||
wsap_dll = os.path.join(v8_path, 'wsap24.dll')
|
wsap_dll = os.path.join(v8_path, 'wsap24.dll')
|
||||||
if not os.path.exists(wsap_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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Validate connection ---
|
# --- Validate connection ---
|
||||||
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Resolve ApachePath ---
|
# --- Resolve ApachePath ---
|
||||||
@@ -213,7 +213,7 @@ def main():
|
|||||||
zip_url = f'https://www.apachelounge.com{zip_url}'
|
zip_url = f'https://www.apachelounge.com{zip_url}'
|
||||||
print(f'Найдено: {zip_url}')
|
print(f'Найдено: {zip_url}')
|
||||||
else:
|
else:
|
||||||
print('Не удалось определить ссылку автоматически.', file=sys.stderr)
|
print('Не удалось определить ссылку автоматически.')
|
||||||
print(f'Скачайте вручную: {download_page}')
|
print(f'Скачайте вручную: {download_page}')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ def main():
|
|||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'Error: не удалось скачать Apache: {e}', file=sys.stderr)
|
print(f'Error: не удалось скачать Apache: {e}')
|
||||||
print('Скачайте вручную: https://www.apachelounge.com/download/')
|
print('Скачайте вручную: https://www.apachelounge.com/download/')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ def main():
|
|||||||
if found_inner:
|
if found_inner:
|
||||||
inner_dir = found_inner
|
inner_dir = found_inner
|
||||||
else:
|
else:
|
||||||
print('Error: каталог Apache24 не найден в архиве', file=sys.stderr)
|
print('Error: каталог Apache24 не найден в архиве')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
os.makedirs(apache_path, exist_ok=True)
|
os.makedirs(apache_path, exist_ok=True)
|
||||||
@@ -297,7 +297,7 @@ def main():
|
|||||||
app_name = app_name.lower()
|
app_name = app_name.lower()
|
||||||
|
|
||||||
if not app_name:
|
if not app_name:
|
||||||
print('Error: не удалось определить имя публикации. Укажите -AppName', file=sys.stderr)
|
print('Error: не удалось определить имя публикации. Укажите -AppName')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print(f'Публикация: {app_name}')
|
print(f'Публикация: {app_name}')
|
||||||
@@ -340,7 +340,7 @@ def main():
|
|||||||
# --- Update httpd.conf ---
|
# --- Update httpd.conf ---
|
||||||
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
||||||
if not os.path.exists(conf_file):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
with open(conf_file, 'r', encoding='utf-8-sig') as f:
|
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})'
|
holder_name = f'{holder_proc.name()} (PID: {holder_pid})'
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
holder_name = f'PID {holder_pid}'
|
holder_name = f'PID {holder_pid}'
|
||||||
print(f'Error: порт {port} занят процессом {holder_name}', file=sys.stderr)
|
print(f'Error: порт {port} занят процессом {holder_name}')
|
||||||
print('Укажите другой порт: -Port 9090')
|
print('Укажите другой порт: -Port 9090')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ def main():
|
|||||||
if httpd_check:
|
if httpd_check:
|
||||||
print(f'Apache запущен (PID: {httpd_check[0].pid})')
|
print(f'Apache запущен (PID: {httpd_check[0].pid})')
|
||||||
else:
|
else:
|
||||||
print('Apache не удалось запустить', file=sys.stderr)
|
print('Apache не удалось запустить')
|
||||||
# Run config test for diagnostics
|
# Run config test for diagnostics
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -130,7 +130,7 @@ def main():
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
final = get_our_httpd(httpd_exe_norm)
|
final = get_our_httpd(httpd_exe_norm)
|
||||||
if final:
|
if final:
|
||||||
print('Error: не удалось остановить Apache', file=sys.stderr)
|
print('Error: не удалось остановить Apache')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print('Apache остановлен')
|
print('Apache остановлен')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -75,13 +75,13 @@ def main():
|
|||||||
|
|
||||||
# --- Validate params ---
|
# --- Validate params ---
|
||||||
if not args.All and not args.AppName:
|
if not args.All and not args.AppName:
|
||||||
print('Error: укажите -AppName или -All', file=sys.stderr)
|
print('Error: укажите -AppName или -All')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Read httpd.conf ---
|
# --- Read httpd.conf ---
|
||||||
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
|
||||||
if not os.path.exists(conf_file):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
with open(conf_file, 'r', encoding='utf-8-sig') as f:
|
with open(conf_file, 'r', encoding='utf-8-sig') as f:
|
||||||
@@ -161,7 +161,7 @@ def main():
|
|||||||
if check:
|
if check:
|
||||||
print('Apache перезапущен')
|
print('Apache перезапущен')
|
||||||
else:
|
else:
|
||||||
print('Error: Apache не удалось перезапустить', file=sys.stderr)
|
print('Error: Apache не удалось перезапустить')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print('Публикаций не осталось — останавливаю Apache...')
|
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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -89,7 +89,7 @@ elif os.path.isdir(package_path):
|
|||||||
md_path = m
|
md_path = m
|
||||||
|
|
||||||
if not bin_path:
|
if not bin_path:
|
||||||
print(f"Не найден Ext/Package.bin для пути: {package_path}", file=sys.stderr)
|
print(f"Не найден Ext/Package.bin для пути: {package_path}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
doc = _parse_xml(bin_path)
|
doc = _parse_xml(bin_path)
|
||||||
@@ -101,7 +101,7 @@ def local(el):
|
|||||||
|
|
||||||
|
|
||||||
if local(pkg) != "package":
|
if local(pkg) != "package":
|
||||||
print(f"Ожидался корневой <package>, получен <{local(pkg)}>", file=sys.stderr)
|
print(f"Ожидался корневой <package>, получен <{local(pkg)}>")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
target_ns = pkg.get("targetNamespace")
|
target_ns = pkg.get("targetNamespace")
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ Switch-параметры (`-NoValidate`) → `action='store_true'`.
|
|||||||
| `Write-Host "text"` | `print("text")` |
|
| `Write-Host "text"` | `print("text")` |
|
||||||
| `Write-Error "text"` | `print("text", file=sys.stderr)` |
|
| `Write-Error "text"` | `print("text", file=sys.stderr)` |
|
||||||
|
|
||||||
|
> Поток обязателен к соблюдению, а не рекомендателен: харнесс не чередует stdout и stderr,
|
||||||
|
> а группирует их — вердикт, ушедший не в тот поток, печатается ПЕРЕД строками, которые его
|
||||||
|
> объясняют, и причинный порядок вывода переворачивается. Держит инвариант
|
||||||
|
> `tests/skills/check-error-streams.mjs`.
|
||||||
|
|
||||||
## lxml vs stdlib
|
## lxml vs stdlib
|
||||||
|
|
||||||
- **Compile/init скрипты** (строковая сборка): только 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-form-purposes.mjs` | таблица назначений форм в `form-add`: состав видов, свойство «основная форма» и оба порта сходятся с `docs/1c-form-spec.md` |
|
||||||
| `check-positional-binding.mjs` | read-only навыки (`*-info` / `*-validate` / `cfe-diff`): позиционным остаётся только путь ко входу, лишний позиционный аргумент не перезаписывает указанный файл |
|
| `check-positional-binding.mjs` | read-only навыки (`*-info` / `*-validate` / `cfe-diff`): позиционным остаётся только путь ко входу, лишний позиционный аргумент не перезаписывает указанный файл |
|
||||||
| `check-agent-portability.mjs` | исходники навыков не привязаны к конкретному AI-агенту: единственная разрешённая форма — плейсхолдер `${CLAUDE_SKILL_DIR}/`, который разворачивает `scripts/switch.py` |
|
| `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-nonascii-fs.mjs` | `fsutil`: удаление и копирование держат не-ASCII пути (кириллический `%TEMP%`, кириллические имена объектов 1С), обе копии модуля не разошлись |
|
||||||
|
|
||||||
`check-inline-drift.mjs` держит реестр семей внутри себя: у каждой семьи перечислены варианты, у
|
`check-inline-drift.mjs` держит реестр семей внутри себя: у каждой семьи перечислены варианты, у
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const GUARDS = [
|
|||||||
['check-form-purposes.mjs', 'назначения форм в form-add: согласованы со спецификацией и между портами'],
|
['check-form-purposes.mjs', 'назначения форм в form-add: согласованы со спецификацией и между портами'],
|
||||||
['check-positional-binding.mjs', 'read-only навыки: позиционным остаётся только путь ко входу'],
|
['check-positional-binding.mjs', 'read-only навыки: позиционным остаётся только путь ко входу'],
|
||||||
['check-agent-portability.mjs', 'исходники навыков: без привязки к конкретному AI-агенту'],
|
['check-agent-portability.mjs', 'исходники навыков: без привязки к конкретному AI-агенту'],
|
||||||
|
['check-error-streams.mjs', 'сообщения об ошибках: один и тот же поток в обоих портах'],
|
||||||
['check-nonascii-fs.mjs', 'fsutil: удаление и копирование держат не-ASCII пути, копии не разошлись'],
|
['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