236 lines
5.5 KiB
Python
Executable File
236 lines
5.5 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# Copyright (C) 2023 Sofus Albert Høgsbro Rose
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import sys
|
|
if not all([
|
|
sys.version_info.major == 3,
|
|
sys.version_info.minor in [9, 10, 11, 12, 13],
|
|
]):
|
|
sys.exit(1)
|
|
|
|
from pathlib import Path
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import contextlib
|
|
|
|
####################
|
|
# - Constants
|
|
####################
|
|
SCRIPT_PATH = Path(__file__).resolve().parent
|
|
|
|
CMD_DEPENDENCIES = [
|
|
'podman',
|
|
'git',
|
|
'pre-commit'
|
|
]
|
|
|
|
IMAGE_NAME = "site-support"
|
|
IMAGE_VERSION = ("0", "0", "1")
|
|
|
|
REGISTRY_HOST = "git.sofus.io"
|
|
REGISTRY_USER = "so-rose"
|
|
REGISTRY_PASSWORD = subprocess.check_output(
|
|
['pass', 'services/home/git.sofus.io/container-registry-token']
|
|
).decode('ascii').strip()
|
|
|
|
####################
|
|
# - Help Text
|
|
####################
|
|
def action_help() -> None:
|
|
print("""This script provides one-click development, CI, and deployment.
|
|
|
|
Usage:
|
|
echo -e "./run.py [OPTION] [EXTRAS]"
|
|
|
|
The following commands must be available:
|
|
podman
|
|
=> The project is developed and run in podman containers.
|
|
git
|
|
=> This project uses git for versioning, and collaboration.
|
|
pre-commit
|
|
=> Enforces that certain checks pass at each commit.
|
|
|
|
Options:
|
|
./run.py
|
|
=> Equivilant to run.py help.
|
|
|
|
./run.py -h|h|help
|
|
=> Shows this text.
|
|
|
|
Options, Run Locally:
|
|
./run.py app
|
|
=> Will run the app on port 8787, for local development.
|
|
|
|
Options, Check:
|
|
./run.py check
|
|
=> Will run all checks, including static analysis and tests.
|
|
|
|
./run.py analyze-quality [OPTIONS]
|
|
=> Will run the static code-quality analysis suite.
|
|
=> OPTIONS will be passed directly to the tool (ruff).
|
|
|
|
./run.py analyze-types [OPTIONS]
|
|
=> Will run the static type checking suite.
|
|
=> OPTIONS will be passed directly to the tool (mypy).
|
|
|
|
./run.py analyze-security
|
|
=> Will run the static security analysis suite.
|
|
|
|
./run.py analyze-format [--overwrite] [OPTIONS]
|
|
=> Will run the code formatter in read-only omde.
|
|
=> '--overwrite' will cause the formater to reformat all files.
|
|
=> OPTIONS will be passed directly to the tool (tan).
|
|
|
|
./run.py test
|
|
=> Will run all Markdown Python snippets as tests.
|
|
|
|
Options, Build & Deploy:
|
|
./run.py build
|
|
=> Will build, tag and upload a docker image appropriately.
|
|
|
|
./run.py build-release
|
|
=> Will build, tag and upload a docker image appropriate for release.
|
|
|
|
Options, Housekeeping:
|
|
./run.py clean
|
|
=> Will delete all data caused by this project's presence on your system.
|
|
""")
|
|
|
|
|
|
####################
|
|
# - Utilities
|
|
####################
|
|
@contextlib.contextmanager
|
|
def cd_script_dir() -> None:
|
|
cwd_orig = Path.cwd()
|
|
|
|
os.chdir(SCRIPT_PATH)
|
|
try:
|
|
yield
|
|
finally:
|
|
os.chdir(cwd_orig)
|
|
|
|
def get_git_revision_hash(short = True) -> str:
|
|
commit_id = subprocess.check_output(
|
|
['git', 'rev-parse', 'HEAD']
|
|
).decode('ascii').strip()
|
|
|
|
return commit_id[:16] if short else commit_id
|
|
|
|
def cmd_exists(cmd: str) -> bool:
|
|
"""Checks if a command exists. Supports Linux, Mac, Windows.
|
|
"""
|
|
return shutil.which(cmd) is not None
|
|
|
|
|
|
|
|
####################
|
|
# - Actions - Build
|
|
####################
|
|
def action_build(release = False) -> None:
|
|
tag = get_git_revision_hash() if release else "dev"
|
|
subprocess.run([
|
|
"podman", "build",
|
|
".",
|
|
|
|
# :<commit_id> - Tag Commit ID
|
|
"--tag",
|
|
f"{IMAGE_NAME}:{tag}",
|
|
|
|
])
|
|
|
|
def action_publish() -> None:
|
|
action_build(release = True)
|
|
|
|
# Login to Registry
|
|
subprocess.run([
|
|
"podman", "login", REGISTRY_HOST,
|
|
"--username", REGISTRY_USER,
|
|
"--password", REGISTRY_PASSWORD,
|
|
])
|
|
|
|
## TODO: Ensure git tag matches.
|
|
|
|
# Tag & Publish Image @ :<commit_id>
|
|
subprocess.run([
|
|
"podman", "image", "push",
|
|
f"{IMAGE_NAME}:dev",
|
|
f"{REGISTRY_HOST}/{REGISTRY_USER}/{IMAGE_NAME}:{get_git_revision_hash()}",
|
|
])
|
|
|
|
# Publish Image @ :M, :M.m, :M.m.p
|
|
for image_tag in [
|
|
f"{IMAGE_NAME}:{'.'.join(IMAGE_VERSION)}",
|
|
f"{IMAGE_NAME}:{'.'.join(IMAGE_VERSION[:2])}",
|
|
f"{IMAGE_NAME}:{IMAGE_VERSION[0]}",
|
|
]:
|
|
# Tag Image
|
|
subprocess.run([
|
|
"podman", "tag",
|
|
f"{IMAGE_NAME}:dev",
|
|
image_tag
|
|
])
|
|
|
|
# Publish Image
|
|
subprocess.run([
|
|
"podman", "image", "push",
|
|
image_tag,
|
|
f"{REGISTRY_HOST}/{REGISTRY_USER}/{image_tag}",
|
|
])
|
|
|
|
|
|
####################
|
|
# - Actions - Run
|
|
####################
|
|
def action_app() -> None:
|
|
action_build()
|
|
subprocess.run([
|
|
"podman", "run", "--rm", "-it",
|
|
"--publish", "8787:8787",
|
|
f"{IMAGE_NAME}:{get_git_revision_hash()}",
|
|
])
|
|
|
|
|
|
|
|
|
|
####################
|
|
# - Script
|
|
####################
|
|
if __name__ == "__main__":
|
|
# Check Available Commands
|
|
for cmd in CMD_DEPENDENCIES:
|
|
if not cmd_exists(cmd) :
|
|
print("One or more dependencies are not installed. Please see --help.")
|
|
sys.exit(1)
|
|
|
|
with cd_script_dir():
|
|
if len(sys.argv) > 1:
|
|
{
|
|
"build": action_build,
|
|
"publish": action_publish,
|
|
|
|
"app": action_app,
|
|
"dev": lambda: print("TBD"),
|
|
|
|
"help": action_help,
|
|
"-h": action_help,
|
|
"--help": action_help,
|
|
}[sys.argv[1]]()
|
|
else:
|
|
action_help()
|