mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-30 14:05:53 +03:00
Auto-build: codeassistant (powershell) from 6188749
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
# v8-xsd-fetch v1.0 — Download 1C platform XSD schemas (dump format) into .v8-xsd
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Загрузка XSD-схем формата выгрузки 1С по версиям формата
|
||||
|
||||
.DESCRIPTION
|
||||
Скачивает schemas/designer/<версия>/*.xsd из репозитория yellow-hammer/namespace-forest
|
||||
в каталог схем проекта: -OutPath, иначе xsdPath из .v8-project.json, иначе .v8-xsd
|
||||
рядом с .v8-project.json (нет его — в текущем каталоге).
|
||||
|
||||
.EXAMPLE
|
||||
.\v8-xsd-fetch.ps1 -Versions "2.20,2.21"
|
||||
|
||||
.EXAMPLE
|
||||
.\v8-xsd-fetch.ps1 -List
|
||||
#>
|
||||
|
||||
[CmdletBinding(PositionalBinding=$false)]
|
||||
param(
|
||||
# Списком одной строкой: powershell -File не связывает массивы
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Versions,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$OutPath,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Ref = "main",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]$Force,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]$List
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$repo = "yellow-hammer/namespace-forest"
|
||||
$repoUrl = "https://github.com/$repo"
|
||||
|
||||
# Текст README — в templates/ навыка, одна копия на оба порта
|
||||
$readmeTemplate = Join-Path $PSScriptRoot "../templates/README.md"
|
||||
|
||||
function Find-V8Project([string]$startDir) {
|
||||
$d = $startDir
|
||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||
$pj = Join-Path $d ".v8-project.json"
|
||||
if (Test-Path $pj) { return $pj }
|
||||
$parent = [System.IO.Path]::GetDirectoryName($d)
|
||||
if ($parent -eq $d) { break }
|
||||
$d = $parent
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Fail([string]$msg) {
|
||||
Write-Host "Error: $msg" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Версии: проверяем до сети ---
|
||||
$wanted = @()
|
||||
if ($Versions) {
|
||||
foreach ($v in ($Versions -split '[,;\s]+')) {
|
||||
if (-not $v) { continue }
|
||||
if ($v -notmatch '^\d+\.\d+$') { Fail "неверная версия формата '$v' (ожидается вида 2.20)" }
|
||||
if ($wanted -notcontains $v) { $wanted += $v }
|
||||
}
|
||||
}
|
||||
|
||||
# --- Каталог схем ---
|
||||
$cwd = (Get-Location).Path
|
||||
$pj = Find-V8Project $cwd
|
||||
$target = $null
|
||||
if ($OutPath) {
|
||||
$target = if ([System.IO.Path]::IsPathRooted($OutPath)) { $OutPath } else { Join-Path $cwd $OutPath }
|
||||
} elseif ($pj) {
|
||||
$pjDir = [System.IO.Path]::GetDirectoryName($pj)
|
||||
try { $cfg = Get-Content -LiteralPath $pj -Raw -Encoding UTF8 | ConvertFrom-Json } catch { $cfg = $null }
|
||||
if ($cfg -and $cfg.xsdPath) {
|
||||
$target = if ([System.IO.Path]::IsPathRooted($cfg.xsdPath)) { $cfg.xsdPath } else { Join-Path $pjDir $cfg.xsdPath }
|
||||
} else {
|
||||
$target = Join-Path $pjDir ".v8-xsd"
|
||||
}
|
||||
} else {
|
||||
$target = Join-Path $cwd ".v8-xsd"
|
||||
}
|
||||
$target = [System.IO.Path]::GetFullPath($target)
|
||||
if (Test-Path -LiteralPath $target -PathType Leaf) { Fail "каталог схем '$target' — это файл" }
|
||||
|
||||
# --- Состав репозитория ---
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.Headers.Add("User-Agent", "v8-xsd-fetch")
|
||||
$wc.Encoding = [System.Text.Encoding]::UTF8
|
||||
try {
|
||||
$tree = $wc.DownloadString("https://api.github.com/repos/$repo/git/trees/$([uri]::EscapeDataString($Ref))?recursive=1") | ConvertFrom-Json
|
||||
} catch {
|
||||
Fail "не удалось получить состав $repo@${Ref}: $($_.Exception.Message)`nСкачайте вручную каталоги schemas/designer/<версия> из $repoUrl в '$target'"
|
||||
}
|
||||
$commit = $tree.sha
|
||||
|
||||
$byVersion = @{}
|
||||
foreach ($node in $tree.tree) {
|
||||
if ($node.type -ne 'blob') { continue }
|
||||
$m = [regex]::Match($node.path, '^schemas/designer/(\d+\.\d+)/([^/]+\.xsd)$')
|
||||
if (-not $m.Success) { continue }
|
||||
$v = $m.Groups[1].Value
|
||||
if (-not $byVersion.ContainsKey($v)) { $byVersion[$v] = New-Object System.Collections.ArrayList }
|
||||
[void]$byVersion[$v].Add(@{ path = $node.path; name = $m.Groups[2].Value })
|
||||
}
|
||||
$available = @($byVersion.Keys | Sort-Object { [version]$_ })
|
||||
if ($available.Count -eq 0) { Fail "в $repo@$Ref нет каталогов schemas/designer/<версия>" }
|
||||
|
||||
if ($List) {
|
||||
Write-Host "Версии формата в $repo@${Ref}:"
|
||||
foreach ($v in $available) {
|
||||
$mark = if (Test-Path -LiteralPath (Join-Path $target $v) -PathType Container) { " (есть локально)" } else { "" }
|
||||
Write-Host " $v — $($byVersion[$v].Count) схем$mark"
|
||||
}
|
||||
Write-Host "Каталог схем: $target"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($wanted.Count -eq 0) { $wanted = $available }
|
||||
$missing = @($wanted | Where-Object { $available -notcontains $_ })
|
||||
if ($missing.Count -gt 0) {
|
||||
Fail "версии $($missing -join ', ') нет в $repo@$Ref (есть: $($available -join ', '))"
|
||||
}
|
||||
|
||||
# --- Загрузка ---
|
||||
if (-not (Test-Path -LiteralPath $target)) { New-Item -ItemType Directory -Path $target -Force | Out-Null }
|
||||
$fetched = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
||||
|
||||
# Происхождение — по версиям: уже лежащие файлы без -Force не перекачиваются, поэтому коммит
|
||||
# версии обновляем, только когда её файлы действительно скачаны в этом запуске
|
||||
$srcPath = Join-Path $target "source.json"
|
||||
$srcVers = @{}
|
||||
if (Test-Path -LiteralPath $srcPath) {
|
||||
try {
|
||||
$old = Get-Content -LiteralPath $srcPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($old.versions -is [PSCustomObject]) {
|
||||
foreach ($pr in $old.versions.PSObject.Properties) {
|
||||
$srcVers[$pr.Name] = @{ ref = "$($pr.Value.ref)"; commit = "$($pr.Value.commit)"; fetched = "$($pr.Value.fetched)" }
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
$totalNew = 0
|
||||
foreach ($v in ($wanted | Sort-Object { [version]$_ })) {
|
||||
$dir = Join-Path $target $v
|
||||
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
||||
$got = 0; $kept = 0
|
||||
foreach ($f in $byVersion[$v]) {
|
||||
$dest = Join-Path $dir $f.name
|
||||
if ((Test-Path -LiteralPath $dest) -and -not $Force) { $kept++; continue }
|
||||
# Файлы берём по коммиту, а не по ветке: весь набор — из одного состояния репозитория
|
||||
$url = "https://raw.githubusercontent.com/$repo/$commit/$($f.path)"
|
||||
try {
|
||||
$bytes = $wc.DownloadData($url)
|
||||
} catch {
|
||||
Fail "не удалось скачать $($f.path): $($_.Exception.Message)"
|
||||
}
|
||||
[System.IO.File]::WriteAllBytes($dest, $bytes)
|
||||
$got++
|
||||
}
|
||||
$totalNew += $got
|
||||
if ($got -gt 0) { $srcVers[$v] = @{ ref = $Ref; commit = $commit; fetched = $fetched } }
|
||||
$line = " ${v}: скачано $got"
|
||||
if ($kept -gt 0) { $line += ", уже было $kept" }
|
||||
Write-Host $line
|
||||
}
|
||||
|
||||
# --- README и source.json ---
|
||||
$readmePath = Join-Path $target "README.md"
|
||||
if (-not (Test-Path -LiteralPath $readmePath) -and (Test-Path -LiteralPath $readmeTemplate)) {
|
||||
Copy-Item -LiteralPath $readmeTemplate -Destination $readmePath
|
||||
}
|
||||
$localVersions = @(Get-ChildItem -LiteralPath $target -Directory |
|
||||
Where-Object { $_.Name -match '^\d+\.\d+$' } |
|
||||
ForEach-Object { $_.Name } | Sort-Object { [version]$_ })
|
||||
# Руками, а не ConvertTo-Json: форматирование PS 5.1 не совпало бы с py-портом.
|
||||
# Версия без записи о происхождении (скопирована вручную) — с пустыми полями.
|
||||
function Esc-Json([string]$s) { return ($s -replace '\\', '\\' -replace '"', '\"') }
|
||||
$verLines = @()
|
||||
foreach ($v in $localVersions) {
|
||||
$e = if ($srcVers.ContainsKey($v)) { $srcVers[$v] } else { @{ ref = ''; commit = ''; fetched = '' } }
|
||||
$verLines += " `"$v`": { `"ref`": `"$(Esc-Json $e.ref)`", `"commit`": `"$(Esc-Json $e.commit)`", `"fetched`": `"$(Esc-Json $e.fetched)`" }"
|
||||
}
|
||||
$verBlock = if ($verLines.Count -gt 0) { "{`n" + ($verLines -join ",`n") + "`n }" } else { "{}" }
|
||||
$srcJson = "{`n `"repository`": `"$repoUrl`",`n `"versions`": $verBlock`n}`n"
|
||||
$enc = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($srcPath, $srcJson, $enc)
|
||||
|
||||
Write-Host "Схемы: $target (скачано файлов: $totalNew)"
|
||||
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
# v8-xsd-fetch v1.0 — Download 1C platform XSD schemas (dump format) into .v8-xsd
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
"""Загрузка XSD-схем формата выгрузки 1С по версиям формата.
|
||||
|
||||
Скачивает schemas/designer/<версия>/*.xsd из репозитория yellow-hammer/namespace-forest
|
||||
в каталог схем проекта: -OutPath, иначе xsdPath из .v8-project.json, иначе .v8-xsd
|
||||
рядом с .v8-project.json (нет его — в текущем каталоге).
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
sys.stderr.reconfigure(encoding="utf-8")
|
||||
|
||||
REPO = "yellow-hammer/namespace-forest"
|
||||
REPO_URL = "https://github.com/" + REPO
|
||||
TIMEOUT = 60
|
||||
|
||||
# Текст README — в templates/ навыка, одна копия на оба порта
|
||||
README_TEMPLATE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "templates", "README.md")
|
||||
|
||||
|
||||
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 _sg_find_v8project(start_dir):
|
||||
d = start_dir
|
||||
for _ in range(20):
|
||||
if not d:
|
||||
break
|
||||
pj = os.path.join(d, ".v8-project.json")
|
||||
if os.path.isfile(pj):
|
||||
return pj
|
||||
parent = os.path.dirname(d)
|
||||
if parent == d:
|
||||
break
|
||||
d = parent
|
||||
return None
|
||||
|
||||
|
||||
def fail(msg):
|
||||
print("Error: " + msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def version_key(v):
|
||||
return tuple(int(x) for x in v.split("."))
|
||||
|
||||
|
||||
def http_get(url):
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "v8-xsd-fetch"})
|
||||
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
|
||||
return resp.read()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Download 1C platform XSD schemas into .v8-xsd", allow_abbrev=False)
|
||||
# Списком одной строкой — паритет с PS: powershell -File не связывает массивы
|
||||
parser.add_argument("-Versions", default="")
|
||||
parser.add_argument("-OutPath", default="")
|
||||
parser.add_argument("-Ref", default="main")
|
||||
parser.add_argument("-Force", action="store_true")
|
||||
parser.add_argument("-List", action="store_true")
|
||||
args = ci_parse_args(parser)
|
||||
ref = args.Ref
|
||||
|
||||
# --- Версии: проверяем до сети ---
|
||||
wanted = []
|
||||
if args.Versions:
|
||||
for v in re.split(r"[,;\s]+", args.Versions):
|
||||
if not v:
|
||||
continue
|
||||
if not re.match(r"^\d+\.\d+$", v):
|
||||
fail("неверная версия формата '%s' (ожидается вида 2.20)" % v)
|
||||
if v not in wanted:
|
||||
wanted.append(v)
|
||||
|
||||
# --- Каталог схем ---
|
||||
cwd = os.getcwd()
|
||||
pj = _sg_find_v8project(cwd)
|
||||
if args.OutPath:
|
||||
target = args.OutPath if os.path.isabs(args.OutPath) else os.path.join(cwd, args.OutPath)
|
||||
elif pj:
|
||||
pj_dir = os.path.dirname(pj)
|
||||
try:
|
||||
with open(pj, encoding="utf-8-sig") as f:
|
||||
cfg = json.load(f)
|
||||
except Exception:
|
||||
cfg = None
|
||||
xsd_path = cfg.get("xsdPath") if isinstance(cfg, dict) else None
|
||||
if xsd_path:
|
||||
target = xsd_path if os.path.isabs(xsd_path) else os.path.join(pj_dir, xsd_path)
|
||||
else:
|
||||
target = os.path.join(pj_dir, ".v8-xsd")
|
||||
else:
|
||||
target = os.path.join(cwd, ".v8-xsd")
|
||||
target = os.path.abspath(target)
|
||||
if os.path.isfile(target):
|
||||
fail("каталог схем '%s' — это файл" % target)
|
||||
|
||||
# --- Состав репозитория ---
|
||||
try:
|
||||
tree = json.loads(http_get("https://api.github.com/repos/%s/git/trees/%s?recursive=1"
|
||||
% (REPO, urllib.parse.quote(ref, safe=""))).decode("utf-8"))
|
||||
except Exception as e:
|
||||
fail("не удалось получить состав %s@%s: %s\nСкачайте вручную каталоги schemas/designer/<версия> из %s в '%s'"
|
||||
% (REPO, ref, e, REPO_URL, target))
|
||||
commit = tree.get("sha", "")
|
||||
|
||||
by_version = {}
|
||||
for node in tree.get("tree", []):
|
||||
if node.get("type") != "blob":
|
||||
continue
|
||||
m = re.match(r"^schemas/designer/(\d+\.\d+)/([^/]+\.xsd)$", node.get("path", ""))
|
||||
if not m:
|
||||
continue
|
||||
by_version.setdefault(m.group(1), []).append({"path": node["path"], "name": m.group(2)})
|
||||
available = sorted(by_version.keys(), key=version_key)
|
||||
if not available:
|
||||
fail("в %s@%s нет каталогов schemas/designer/<версия>" % (REPO, ref))
|
||||
|
||||
if args.List:
|
||||
print("Версии формата в %s@%s:" % (REPO, ref))
|
||||
for v in available:
|
||||
mark = " (есть локально)" if os.path.isdir(os.path.join(target, v)) else ""
|
||||
print(" %s — %d схем%s" % (v, len(by_version[v]), mark))
|
||||
print("Каталог схем: " + target)
|
||||
sys.exit(0)
|
||||
|
||||
if not wanted:
|
||||
wanted = list(available)
|
||||
missing = [v for v in wanted if v not in available]
|
||||
if missing:
|
||||
fail("версии %s нет в %s@%s (есть: %s)" % (", ".join(missing), REPO, ref, ", ".join(available)))
|
||||
|
||||
# --- Загрузка ---
|
||||
os.makedirs(target, exist_ok=True)
|
||||
fetched = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Происхождение — по версиям: уже лежащие файлы без -Force не перекачиваются, поэтому коммит
|
||||
# версии обновляем, только когда её файлы действительно скачаны в этом запуске
|
||||
src_path = os.path.join(target, "source.json")
|
||||
src_vers = {}
|
||||
if os.path.isfile(src_path):
|
||||
try:
|
||||
with open(src_path, encoding="utf-8-sig") as fh:
|
||||
old = json.load(fh)
|
||||
if isinstance(old.get("versions"), dict):
|
||||
for k, e in old["versions"].items():
|
||||
e = e if isinstance(e, dict) else {}
|
||||
src_vers[k] = {"ref": str(e.get("ref") or ""), "commit": str(e.get("commit") or ""),
|
||||
"fetched": str(e.get("fetched") or "")}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
total_new = 0
|
||||
for v in sorted(wanted, key=version_key):
|
||||
vdir = os.path.join(target, v)
|
||||
os.makedirs(vdir, exist_ok=True)
|
||||
got = kept = 0
|
||||
for f in by_version[v]:
|
||||
dest = os.path.join(vdir, f["name"])
|
||||
if os.path.exists(dest) and not args.Force:
|
||||
kept += 1
|
||||
continue
|
||||
# Файлы берём по коммиту, а не по ветке: весь набор — из одного состояния репозитория
|
||||
url = "https://raw.githubusercontent.com/%s/%s/%s" % (REPO, commit, f["path"])
|
||||
try:
|
||||
data = http_get(url)
|
||||
except Exception as e:
|
||||
fail("не удалось скачать %s: %s" % (f["path"], e))
|
||||
with open(dest, "wb") as out:
|
||||
out.write(data)
|
||||
got += 1
|
||||
total_new += got
|
||||
if got > 0:
|
||||
src_vers[v] = {"ref": ref, "commit": commit, "fetched": fetched}
|
||||
line = " %s: скачано %d" % (v, got)
|
||||
if kept > 0:
|
||||
line += ", уже было %d" % kept
|
||||
print(line)
|
||||
|
||||
# --- README и source.json ---
|
||||
readme_path = os.path.join(target, "README.md")
|
||||
if not os.path.exists(readme_path) and os.path.isfile(README_TEMPLATE):
|
||||
shutil.copyfile(README_TEMPLATE, readme_path)
|
||||
local_versions = sorted((d for d in os.listdir(target)
|
||||
if re.match(r"^\d+\.\d+$", d) and os.path.isdir(os.path.join(target, d))),
|
||||
key=version_key)
|
||||
# Руками, а не json.dumps: форматирование совпадает с PS-мастером.
|
||||
# Версия без записи о происхождении (скопирована вручную) — с пустыми полями.
|
||||
def esc_json(s):
|
||||
return s.replace("\\", "\\\\").replace('"', '\\"')
|
||||
ver_lines = []
|
||||
for v in local_versions:
|
||||
e = src_vers.get(v) or {"ref": "", "commit": "", "fetched": ""}
|
||||
ver_lines.append(' "%s": { "ref": "%s", "commit": "%s", "fetched": "%s" }'
|
||||
% (v, esc_json(e["ref"]), esc_json(e["commit"]), esc_json(e["fetched"])))
|
||||
ver_block = "{\n" + ",\n".join(ver_lines) + "\n }" if ver_lines else "{}"
|
||||
src_json = '{\n "repository": "%s",\n "versions": %s\n}\n' % (REPO_URL, ver_block)
|
||||
with open(src_path, "w", encoding="utf-8", newline="\n") as out:
|
||||
out.write(src_json)
|
||||
|
||||
print("Схемы: %s (скачано файлов: %d)" % (target, total_new))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user