mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-31 23:40:51 +03:00
Значение с пробелом без кавычек разрывалось на токены, и лишние молча растекались по свободным позиционным параметрам. На документированной форме вызова (powershell -NoProfile -File): form-compile.ps1 -JsonPath a.json -OutputPath b.xml -Purpose Форма списка → Purpose=[Форма] ObjectPath=[списка], ошибки НЕТ Навык отрабатывал успешно с усечённым значением; реальный триггер — путь с пробелом в -EmitDsl или -ObjectPath, результат уезжал не туда молча. У навыков с парой -DefinitionFile/-Operation осколок уезжал в -DefinitionFile, и навык жаловался на параметр, которого в команде не было. Расхождение портов: py на том же вызове отвечает «unrecognized arguments: списка» — там все аргументы объявлены опциями. Правка выравнивает PS по py, python не менялся. 41 навык получает [CmdletBinding(PositionalBinding=$false)] и ни одного позиционного параметра. Без Position=0 (в отличие от read-only навыков): «главный» параметр механически не выводится — у meta-edit первым объявлен -DefinitionFile, а путь к объекту вторым, — а ошибка выбора тестами не ловится, раннер передаёт только именованные флаги. Позиционной формы вызова нет ни в одной из 140 строк SKILL.md, внутренние вызовы навыков друг из друга тоже именованные. check-positional-binding.mjs расширен на пишущие навыки статической проверкой. Поведенческая (канареечный файл) остаётся только у read-only: у web-stop и db-run все параметры Mandatory=$false, связывание прошло бы и тело выполнилось — гард остановил бы Apache и запустил 1С. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
142 lines
5.2 KiB
Python
142 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
# web-stop v1.2 — Stop Apache HTTP Server
|
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
|
|
|
"""
|
|
Остановка Apache HTTP Server.
|
|
Сначала пытается graceful shutdown, при неудаче — принудительная остановка.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
import psutil
|
|
|
|
# Регистронезависимый ввод — паритет с PS1: в PowerShell имена параметров и [ValidateSet]
|
|
# регистр не различают, в argparse совпадение точное.
|
|
def ci_parse_args(parser, argv=None):
|
|
"""parse_args по правилам PS: имена параметров и значения choices регистронезависимы."""
|
|
argv = list(sys.argv[1:] if argv is None else argv)
|
|
names = {s.lower(): s for a in parser._actions for s in a.option_strings}
|
|
for i, tok in enumerate(argv):
|
|
if tok.startswith('-') and tok.lower() in names:
|
|
argv[i] = names[tok.lower()]
|
|
# choices — зеркало [ValidateSet]; канонизируем ДО разбора, иначе argparse отвергнет регистр
|
|
choice_map = {}
|
|
for a in parser._actions:
|
|
if a.choices:
|
|
for s in a.option_strings:
|
|
choice_map[s] = {str(c).lower(): c for c in a.choices}
|
|
for i in range(len(argv) - 1):
|
|
m = choice_map.get(argv[i])
|
|
if m and argv[i + 1].lower() in m:
|
|
argv[i + 1] = m[argv[i + 1].lower()]
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
|
|
def get_our_httpd(httpd_exe_norm):
|
|
"""Filter httpd processes by our ApachePath."""
|
|
result = []
|
|
if not httpd_exe_norm:
|
|
return result
|
|
for p in psutil.process_iter(['pid', 'name', 'exe']):
|
|
try:
|
|
if p.info['name'] and 'httpd' in p.info['name'].lower():
|
|
if p.info['exe'] and os.path.normcase(os.path.normpath(p.info['exe'])) == httpd_exe_norm:
|
|
result.append(p)
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
pass
|
|
return result
|
|
|
|
|
|
def get_all_httpd():
|
|
"""Get all httpd processes."""
|
|
result = []
|
|
for p in psutil.process_iter(['pid', 'name', 'exe']):
|
|
try:
|
|
if p.info['name'] and 'httpd' in p.info['name'].lower():
|
|
result.append(p)
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
pass
|
|
return result
|
|
|
|
|
|
def main():
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
sys.stderr.reconfigure(encoding="utf-8")
|
|
parser = argparse.ArgumentParser(description='Stop Apache HTTP Server', allow_abbrev=False)
|
|
parser.add_argument('-ApachePath', type=str, default='', help='Apache root (default: tools\\apache24)')
|
|
args = ci_parse_args(parser)
|
|
|
|
# --- Resolve ApachePath ---
|
|
apache_path = args.ApachePath
|
|
if not apache_path:
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(script_dir))))
|
|
apache_path = os.path.join(project_root, 'tools', 'apache24')
|
|
|
|
# --- Helper: normalize httpd exe path ---
|
|
httpd_exe = os.path.join(apache_path, 'bin', 'httpd.exe')
|
|
if os.path.exists(httpd_exe):
|
|
httpd_exe_norm = os.path.normcase(os.path.normpath(os.path.realpath(httpd_exe)))
|
|
else:
|
|
httpd_exe_norm = os.path.normcase(os.path.normpath(httpd_exe))
|
|
|
|
# --- Check process (only our Apache) ---
|
|
httpd_proc = get_our_httpd(httpd_exe_norm)
|
|
if not httpd_proc:
|
|
foreign = get_all_httpd()
|
|
if foreign:
|
|
print('Наш Apache не запущен')
|
|
print(f'[WARN] Обнаружен сторонний Apache (PID: {foreign[0].pid})')
|
|
else:
|
|
print('Apache не запущен')
|
|
sys.exit(0)
|
|
|
|
pids = ', '.join(str(p.pid) for p in httpd_proc)
|
|
print(f'Останавливаю Apache (PID: {pids})...')
|
|
|
|
# --- Stop our processes ---
|
|
for p in httpd_proc:
|
|
try:
|
|
p.kill()
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
pass
|
|
|
|
# --- Wait for shutdown ---
|
|
max_wait = 5
|
|
elapsed = 0
|
|
while elapsed < max_wait:
|
|
time.sleep(1)
|
|
elapsed += 1
|
|
check = get_our_httpd(httpd_exe_norm)
|
|
if not check:
|
|
print('Apache остановлен')
|
|
print('Публикации сохранены. Перезапуск: /web-publish <база> Удаление: /web-unpublish --all')
|
|
sys.exit(0)
|
|
|
|
# --- Fallback: force kill ---
|
|
remaining = get_our_httpd(httpd_exe_norm)
|
|
if remaining:
|
|
print('Принудительная остановка...')
|
|
for p in remaining:
|
|
try:
|
|
p.kill()
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
pass
|
|
time.sleep(1)
|
|
final = get_our_httpd(httpd_exe_norm)
|
|
if final:
|
|
print('Error: не удалось остановить Apache', file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print('Apache остановлен')
|
|
print('Публикации сохранены. Перезапуск: /web-publish <база> Удаление: /web-unpublish --all')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|