Create a script to keep the sourcecode up-to-date

The update.py script will push changes to a git repo when changes are
found. See the README for more details.
This commit is contained in:
David Kruger 2025-05-30 14:06:47 -07:00
parent 99c2b8bea3
commit bacc21e0db
3 changed files with 120 additions and 10 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.swp
*.swo

View File

@ -8,3 +8,23 @@ Given the main JS file URL, the source code can be downloaded and mapped using:
``` ```
For convenience you can run `python update.py` For convenience you can run `python update.py`
## Syncing with git repo
The script is designed to keep the contents of a git repo up-to-date with the
current files. This can be paired with a cronjob to keep an up-to-date version
of the code someplace.
To ensure the `push` phase of the update works as expected, it is recommended
to provide an SSH URL for the repository, as we can leverage an SSH key for a
password-less authentication.
The following example would keep the latest code extracted from dndbeyond.com
stored in the `ddb_src` directory on the `master` branch of the given git repo.
If changes are found they are automatically pushed to the repository,
```
python update.py \
--directory "ddb_src" \
--git-repo "ssh://git@git.example.com/test/dndbeyond_src.git"
--git-branch "master"
```

108
update.py
View File

@ -3,8 +3,12 @@ import argparse
import gzip import gzip
import html.parser import html.parser
import logging import logging
import pathlib
import re import re
import shutil
import subprocess import subprocess
import tempfile
import typing
import urllib.request import urllib.request
## The URL of a dndbeyond character which won't be deleted ## The URL of a dndbeyond character which won't be deleted
@ -21,6 +25,25 @@ def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Utility for scanning and converting videos." description="Utility for scanning and converting videos."
) )
parser.add_argument(
"-d",
"--directory",
required=True,
help="The destination directory to put the files (required)",
)
parser.add_argument(
"-r",
"--git-repo",
type=str,
help="Store the code in the directory of this git repo",
)
parser.add_argument(
"-b",
"--git-branch",
type=str,
default="master",
help="Git branch to push to",
)
parser.add_argument( parser.add_argument(
"-u", "-u",
"--character-url", "--character-url",
@ -43,16 +66,11 @@ def main():
char_data = download_character() char_data = download_character()
parser = MainJSExtractor() parser = MainJSExtractor()
parser.feed(char_data.decode("utf-8")) parser.feed(char_data.decode("utf-8"))
print(f"Found URL: {parser.js_url}") logging.debug(f"Found URL: {parser.js_url}")
subprocess.check_call( if args.git_repo is None:
[ download_src(args.directory, parser.js_url)
SOURCEMAPPER_BIN, else:
"-output", update_repo(args.git_repo, args.git_branch, args.directory, parser.js_url)
"ddb_main",
"-jsurl",
parser.js_url,
]
)
def download_character() -> bytes: def download_character() -> bytes:
@ -95,5 +113,75 @@ class MainJSExtractor(html.parser.HTMLParser):
self.js_url = attr_val self.js_url = attr_val
def download_src(output_dir: str, js_url: str) -> None:
subprocess.run(
[
SOURCEMAPPER_BIN,
"-output",
output_dir,
"-jsurl",
js_url,
],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
def update_repo(git_repo: str, git_branch: str, output_dir: str, js_url: str) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
logging.debug(f"Cloning repo {git_repo} to {tmpdir}")
git_sparse_clone(tmpdir, git_repo, git_branch, output_dir)
checkout_output_dir = pathlib.Path(tmpdir) / output_dir
logging.debug(f"Updating source code in {checkout_output_dir}")
if checkout_output_dir.exists():
shutil.rmtree(str(checkout_output_dir))
download_src(str(checkout_output_dir), js_url)
git_commit_all(tmpdir, "New source found from dndbeyond.com")
def git_sparse_clone(
work_tree: str, git_repo: str, git_branch: str, sparse_dir: str
) -> None:
# Clone without checkout first so we can do a sparse checkout
git_empty_checkout(work_tree, git_repo)
git_setup_sparse_checkout(work_tree, sparse_dir)
git_checkout(work_tree, git_branch)
def git_empty_checkout(work_tree: str, git_repo: str) -> None:
run_git_cmd(["clone", "--no-checkout", git_repo, work_tree], None)
def git_setup_sparse_checkout(work_tree: str, sparse_dir: str) -> None:
run_git_cmd(["sparse-checkout", "set", "--no-cone", sparse_dir], work_tree)
def git_checkout(work_tree: str, git_branch: str) -> None:
run_git_cmd(["checkout", git_branch], work_tree)
def git_commit_all(work_tree: str, commit_msg: str) -> None:
run_git_cmd(["add", "--all"], work_tree)
git_status = run_git_cmd(["status", "--porcelain=v1"], work_tree)
if len(git_status.stdout) > 0:
logging.debug(f"Changes found, comitting")
run_git_cmd(["commit", "-m", commit_msg], work_tree)
run_git_cmd(["push"], work_tree)
else:
logging.debug("No changes found")
def run_git_cmd(args: typing.List[str], work_tree: str) -> subprocess.CompletedProcess:
git_cmd = ["/usr/bin/git"]
if work_tree is not None:
git_dir = f"{work_tree}/.git"
git_cmd += [
f"--git-dir={git_dir}",
f"--work-tree={work_tree}",
]
return subprocess.run(git_cmd + args, check=True, capture_output=True)
if __name__ == "__main__": if __name__ == "__main__":
main() main()