try a python script in runner
This commit is contained in:
BIN
scripts/__pycache__/bundle.cpython-313.pyc
Normal file
BIN
scripts/__pycache__/bundle.cpython-313.pyc
Normal file
Binary file not shown.
226
scripts/bundle.py
Executable file
226
scripts/bundle.py
Executable file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
INKSCAPE_NS = "http://www.inkscape.org/namespace/inkscape/extension"
|
||||
ET.register_namespace("", INKSCAPE_NS)
|
||||
|
||||
REPOS = [
|
||||
{
|
||||
"name": "botbox3000",
|
||||
"url": "https://github.com/jondale/botbox3000.git",
|
||||
"branch": "main",
|
||||
"directory": "botbox3000",
|
||||
"inx_file": "boxbot.inx",
|
||||
"id": "org.knoxmakers.botbox",
|
||||
"submenu": ["Knox Makers", "Laser"],
|
||||
},
|
||||
{
|
||||
"name": "km-living-hinge",
|
||||
"url": "https://github.com/KnoxMakers/km-living-hinge.git",
|
||||
"branch": "main",
|
||||
"directory": "km-living-hinge",
|
||||
},
|
||||
{
|
||||
"name": "km-plot",
|
||||
"url": "https://git.knoxmakers.org/KnoxMakers/km-plot.git",
|
||||
"branch": "main",
|
||||
"directory": "km-plot",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def run(cmd: list[str], cwd: str | None = None) -> str:
|
||||
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, check=True)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def has_staged_changes(root: Path) -> bool:
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--cached", "--quiet"],
|
||||
cwd=root,
|
||||
capture_output=True,
|
||||
)
|
||||
return result.returncode != 0
|
||||
|
||||
|
||||
def sync_repo(repo: dict, ext_dir: Path, tmp_dir: Path) -> None:
|
||||
name = repo["name"]
|
||||
url = repo["url"]
|
||||
branch = repo.get("branch", "main")
|
||||
directory = repo["directory"]
|
||||
|
||||
work = tmp_dir / name
|
||||
target = ext_dir / directory
|
||||
|
||||
print(f"==> Sync {name} from {url} ({branch}) -> extensions/{directory}")
|
||||
|
||||
subprocess.run(
|
||||
["git", "clone", "--depth", "1", "--branch", branch, url, str(work)],
|
||||
check=True,
|
||||
)
|
||||
|
||||
if target.exists():
|
||||
shutil.rmtree(target)
|
||||
shutil.copytree(work, target)
|
||||
|
||||
git_dir = target / ".git"
|
||||
if git_dir.exists():
|
||||
shutil.rmtree(git_dir)
|
||||
|
||||
commit = run(["git", "rev-parse", "HEAD"], cwd=str(work))
|
||||
(target / ".upstream_commit").write_text(commit + "\n")
|
||||
(target / ".upstream_url").write_text(url + "\n")
|
||||
(target / ".upstream_branch").write_text(branch + "\n")
|
||||
|
||||
|
||||
def set_extension_id(root: ET.Element, new_id: str) -> bool:
|
||||
ns = {"ink": INKSCAPE_NS}
|
||||
|
||||
id_elem = root.find("ink:id", ns)
|
||||
if id_elem is None:
|
||||
id_elem = root.find("id")
|
||||
|
||||
if id_elem is not None:
|
||||
if id_elem.text != new_id:
|
||||
id_elem.text = new_id
|
||||
return True
|
||||
return False
|
||||
|
||||
name_elem = root.find("ink:name", ns)
|
||||
if name_elem is None:
|
||||
name_elem = root.find("name")
|
||||
|
||||
id_elem = ET.Element("id")
|
||||
id_elem.text = new_id
|
||||
|
||||
if name_elem is not None:
|
||||
idx = list(root).index(name_elem)
|
||||
root.insert(idx + 1, id_elem)
|
||||
else:
|
||||
root.insert(0, id_elem)
|
||||
return True
|
||||
|
||||
|
||||
def set_effects_submenu(root: ET.Element, submenus: list[str]) -> bool:
|
||||
ns = {"ink": INKSCAPE_NS}
|
||||
|
||||
effect_elem = root.find(".//ink:effect", ns)
|
||||
if effect_elem is None:
|
||||
effect_elem = root.find(".//effect")
|
||||
|
||||
if effect_elem is None:
|
||||
return False
|
||||
|
||||
menu_elem = None
|
||||
for tag in ["effects-menu", "effectsmenu"]:
|
||||
menu_elem = effect_elem.find(tag)
|
||||
if menu_elem is not None:
|
||||
break
|
||||
menu_elem = effect_elem.find(f"ink:{tag}", ns)
|
||||
if menu_elem is not None:
|
||||
break
|
||||
|
||||
if menu_elem is None:
|
||||
return False
|
||||
|
||||
menu_elem.clear()
|
||||
menu_elem.tag = "effects-menu"
|
||||
|
||||
parent = menu_elem
|
||||
for i, submenu_name in enumerate(submenus):
|
||||
submenu = ET.SubElement(parent, "submenu")
|
||||
submenu.set("_name", submenu_name)
|
||||
if i < len(submenus) - 1:
|
||||
parent = submenu
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def patch_inx(inx_path: Path, extension_id: str | None, submenus: list[str] | None) -> bool:
|
||||
if not inx_path.exists():
|
||||
print(f"ERROR: expected file not found: {inx_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Patching {inx_path}")
|
||||
|
||||
tree = ET.parse(inx_path)
|
||||
root = tree.getroot()
|
||||
|
||||
modified = False
|
||||
|
||||
if extension_id is not None:
|
||||
if set_extension_id(root, extension_id):
|
||||
modified = True
|
||||
|
||||
if submenus is not None:
|
||||
if set_effects_submenu(root, submenus):
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
ET.indent(tree, space=" ")
|
||||
tree.write(inx_path, encoding="UTF-8", xml_declaration=True)
|
||||
|
||||
content = inx_path.read_text(encoding="utf-8")
|
||||
if not content.endswith("\n"):
|
||||
inx_path.write_text(content + "\n", encoding="utf-8")
|
||||
|
||||
return modified
|
||||
|
||||
|
||||
def process_repo(repo: dict, ext_dir: Path) -> None:
|
||||
inx_file = repo.get("inx_file")
|
||||
extension_id = repo.get("id")
|
||||
submenus = repo.get("submenu")
|
||||
|
||||
if inx_file and (extension_id or submenus):
|
||||
inx_path = ext_dir / repo["directory"] / inx_file
|
||||
patch_inx(inx_path, extension_id, submenus)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
root = Path(__file__).resolve().parent.parent
|
||||
ext_dir = root / "extensions"
|
||||
ext_dir.mkdir(exist_ok=True)
|
||||
|
||||
os.chdir(root)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmp_dir = Path(tmp)
|
||||
|
||||
for repo in REPOS:
|
||||
sync_repo(repo, ext_dir, tmp_dir)
|
||||
|
||||
subprocess.run(["git", "add", "extensions"], check=True)
|
||||
|
||||
if not has_staged_changes(root):
|
||||
print("No bundle changes.")
|
||||
sys.exit(0)
|
||||
|
||||
subprocess.run(["git", "checkout", "extensions"], check=True)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmp_dir = Path(tmp)
|
||||
|
||||
for repo in REPOS:
|
||||
sync_repo(repo, ext_dir, tmp_dir)
|
||||
process_repo(repo, ext_dir)
|
||||
|
||||
subprocess.run(["git", "add", "extensions"], check=True)
|
||||
|
||||
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
||||
subprocess.run(
|
||||
["git", "commit", "-m", f"bundle: update ({date_str})"],
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
EXT_DIR="$ROOT/extensions"
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
mkdir -p "$EXT_DIR"
|
||||
|
||||
# Format: name|repo_url|branch|dest_subdir_under_extensions
|
||||
REPOS=(
|
||||
"botbox3000|https://github.com/jondale/botbox3000.git|main|botbox3000"
|
||||
"km-living-hinge|https://github.com/KnoxMakers/km-living-hinge.git|main|km-living-hinge"
|
||||
"km-plot|https://git.knoxmakers.org/KnoxMakers/km-plot.git|main|km-plot"
|
||||
)
|
||||
|
||||
sync_one() {
|
||||
local name="$1" url="$2" branch="$3" dest="$4"
|
||||
local work="$TMP/$name"
|
||||
local target="$EXT_DIR/$dest"
|
||||
|
||||
echo "==> Sync $name from $url ($branch) -> extensions/$dest"
|
||||
|
||||
git clone --depth 1 --branch "$branch" "$url" "$work"
|
||||
|
||||
rm -rf "$target"
|
||||
mkdir -p "$target"
|
||||
cp -a "$work/." "$target/"
|
||||
rm -rf "$target/.git"
|
||||
|
||||
# Provenance
|
||||
(cd "$work" && git rev-parse HEAD) > "$target/.upstream_commit"
|
||||
echo "$url" > "$target/.upstream_url"
|
||||
echo "$branch" > "$target/.upstream_branch"
|
||||
}
|
||||
|
||||
post_process() {
|
||||
echo "==> Post-processing bundled extensions"
|
||||
|
||||
local inx="$EXT_DIR/botbox3000/boxbot.inx"
|
||||
if [[ ! -f "$inx" ]]; then
|
||||
echo "ERROR: expected file not found: $inx"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Patching $inx"
|
||||
|
||||
# Replace the FIRST <id> ... </id> block, regardless of whitespace/newlines inside.
|
||||
# (GNU sed on Debian supports the 0,ADDR form.)
|
||||
sed -i '0,/<id[[:space:]>]/{
|
||||
/<id[[:space:]>]/,/<\/id>/c\
|
||||
<id>org.knoxmakers.botbox</id>
|
||||
}' "$inx"
|
||||
|
||||
# Replace the FIRST <effectsmenu ...> ... </effectsmenu> block (handles attributes + multiline)
|
||||
sed -i '0,/<effectsmenu[^>]*>/{
|
||||
/<effectsmenu[^>]*>/,/<\/effectsmenu>/c\
|
||||
<effectsmenu>\
|
||||
<submenu _name="Knox Makers">\
|
||||
<submenu _name="Laser"/>\
|
||||
</submenu>\
|
||||
</effectsmenu>
|
||||
}' "$inx"
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
# Sync phase
|
||||
for entry in "${REPOS[@]}"; do
|
||||
IFS='|' read -r name url branch dest <<<"$entry"
|
||||
sync_one "$name" "$url" "$branch" "$dest"
|
||||
done
|
||||
|
||||
# Post-processing phase
|
||||
post_process
|
||||
|
||||
# Commit phase
|
||||
cd "$ROOT"
|
||||
git add extensions
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No bundle changes."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "bundle: update ($(date -u +%Y-%m-%d))"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user