GiteaPC/giteapc.py

149 lines
5.3 KiB
Python

#! /usr/bin/env python3
"""
GiteaPC is an automated installer/updated for repositories of the Planète Casio
Gitea forge (https://gitea.planet-casio.com/). It is mainly used to set up
installs of the add-in development fxSDK.
"""
# Install prefix (inserted at compile-time)
PREFIX = "%PREFIX%"
# Program version (inserted at compile-time)
VERSION = "%VERSION%"
import sys
sys.path.append(PREFIX + "/lib/giteapc")
import giteapc.repo
from giteapc.util import *
from giteapc.config import REPO_FOLDER, PREFIX_FOLDER
import getopt
import sys
import os
# TODO:
# * @ or ~ shortcut for remote repositories to avoid the -r option?
# * build, install: repository updates
# * NetworkError for cURL
# * Handle dependencies
# * Test update logic
usage_string = """
usage: {R}giteapc{_} [{R}list{_}|{R}fetch{_}|{R}show{_}|{R}build{_}|{R}install{_}|{R}uninstall{_}] [{g}{i}ARGS...{_}]
GiteaPC is a tool to automatically clone, install and update repositories from
the Planète Casio Gitea forge. In the following commands, each {g}{i}REPOSITORY{_}
is either a full name like "Lephenixnoir/sh-elf-gcc", or a short name like
"sh-elf-gcc" when there is no ambiguity.
{R}giteapc list{_} [{R}-r{_}] [{g}{i}PATTERN{_}]
Lists all repositories on this computer. With -r, lists repositories on the
forge. A wildcard pattern can be specified to filter the results.
{R}giteapc fetch{_} [{R}--https{_}|{R}--ssh{_}] [{R}-u{_}] [{R}-f{_}] [{g}{i}REPOSITORY{_}[{R}@{_}{g}{i}VERSION{_}]{g}{i}...{_}]
Clones or fetches a repository and its dependencies. If no repository is
specified, fetches all the local repositories. HTTPS or SSH can be selected
when cloning (HTTPS by default). With -u, pulls after fetching.
{R}giteapc show{_} [{R}-r{_}] [{R}-p{_}] {g}{i}REPOSITORY...{_}
Shows the branches and tags (versions) for the specified local repositories.
With -r, show information for remote repositories on the forge.
With -p, just print the path of local repositories (useful in scripts).
{R}giteapc build{_} [{R}-i{_}] [{R}-u{_}] [{R}--skip-configure{_}] {g}{i}REPOSITORY{_}[{R}@{_}{g}{i}VERSION{_}][{R}:{_}{g}{i}CONFIG{_}]{g}{i}...{_}
Configures and builds a local repository. A specific configuration can be
requested. With -i, also installs if build is successful. --skip-configure
builds without configuring (useful for rebuilds). With -u, pulls the current
branch before building (update mode).
{R}giteapc install{_} [{R}--https{_}|{R}--ssh{_}] [{R}-u{_}] {g}{i}REPOSITORY{_}[{R}@{_}{g}{i}VERSION{_}][{R}:{_}{g}{i}CONFIG{_}]{g}{i}...{_}
Shortcut to clone (or fetch), build, and install a repository.
{R}giteapc uninstall{_} [{R}-k{_}] {g}{i}REPOSITORY...{_}
Uninstalls the build products of the specified repositories and removes the
source files. With -k, keeps the source files.
{W}Important folders{_}
{R}$GITEAPC_HOME{_} or {R}$XDG_DATA_HOME/giteapc{_} or {R}$HOME/.local/share/giteapc{_}
{W}->{_} {b}{}{_}
Storage for GiteaPC repositories.
{R}$GITEAPC_PREFIX{_} or {R}$HOME/.local{_}
{W}->{_} {b}{}{_}
Default install prefix.
""".format(REPO_FOLDER, PREFIX_FOLDER, **colors()).strip()
def usage(exitcode=None):
print(usage_string, file=sys.stderr)
if exitcode is not None:
sys.exit(exitcode)
commands = {
"list": {
"function": giteapc.repo.list,
"args": "remote:-r,--remote",
},
"fetch": {
"function": giteapc.repo.fetch,
"args": "use_ssh:--ssh use_https:--https force:-f,--force "+\
"update:-u,--update",
},
"show": {
"function": giteapc.repo.show,
"args": "remote:-r,--remote path:-p,--path-only",
},
"build": {
"function": giteapc.repo.build,
"args": "install:-i,--install skip_configure:--skip-configure "+\
"update:-u,--update",
},
"install": {
"function": giteapc.repo.install,
"args": "use_ssh:--ssh use_https:--https update:-u,--update",
},
"uninstall": {
"function": giteapc.repo.uninstall,
"args": "keep:-k,--keep",
},
}
def main(argv, commands):
# Help, version, and invocations without a proper command name
if "-h" in argv or "--help" in argv:
usage(0)
if "-v" in argv or "--version" in argv:
print(f"GiteaPC {VERSION}")
return 0
if argv == [] or argv[0] not in commands:
usage(1)
args = commands[argv[0]].get("args","").split()
args = [x.split(":",1) for x in args]
args = [(name,forms.split(",")) for name, forms in args]
single = ""
double = []
for name, forms in args:
for f in forms:
if f.startswith("--"):
double.append(f[2:])
elif f[0] == "-" and len(f) == 2:
single += f[1]
else:
raise Exception("invalid argument format o(x_x)o")
# Parse arguments
try:
opts, data = getopt.gnu_getopt(argv[1:], single, double)
opts = { name: True if val == "" else val for (name,val) in opts }
except getopt.GetoptError as e:
return fatal(e)
options = {}
for (o,val) in opts.items():
for (name, forms) in args:
if o in forms:
options[name] = val
try:
return commands[argv[0]]["function"](*data, **options)
except Error as e:
return fatal(e)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:], commands))