generated from alphane/template
rewrote dev command to python, and added prod
This commit is contained in:
@@ -1,295 +1,253 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
class DevHelper
|
|
||||||
# Support dashes in command names
|
# Support dashes in command names
|
||||||
COMMAND_TO_METHOD = {
|
COMMAND_TO_METHOD = {
|
||||||
"ts-node" => :ts_node,
|
"ts-node": "ts_node",
|
||||||
"check-types" => :check_types,
|
"check-types": "check_types",
|
||||||
"bash-completions" => :bash_completions,
|
"bash-completions": "bash_completions",
|
||||||
"plantuml-to-png" => :plantuml_to_png,
|
"plantuml-to-png": "plantuml_to_png",
|
||||||
}
|
}
|
||||||
METHOD_TO_COMMAND = COMMAND_TO_METHOD.invert
|
METHOD_TO_COMMAND = {method: command for command, method in COMMAND_TO_METHOD.items()}
|
||||||
|
|
||||||
REPLACE_PROCESS = "replace_process"
|
|
||||||
WAIT_FOR_PROCESS = "wait_for_process"
|
WAIT_FOR_PROCESS = "wait_for_process"
|
||||||
|
|
||||||
# External Interface
|
|
||||||
def self.call(*args)
|
class DevHelper:
|
||||||
new.call(*args)
|
def __init__(self):
|
||||||
end
|
self.last_exit_status = 0
|
||||||
|
|
||||||
# Core logic
|
# Core logic
|
||||||
def call(*args, **kwargs)
|
def call(self, *args, **kwargs):
|
||||||
command = args[0]
|
if not args:
|
||||||
method = COMMAND_TO_METHOD.fetch(command, command)
|
return self.compose(*args, **kwargs)
|
||||||
if args.length.positive? && respond_to?(method)
|
|
||||||
public_send(method, *args.drop(1), **kwargs)
|
|
||||||
else
|
|
||||||
compose(*args, **kwargs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def compose(*args, **kwargs)
|
command, rest = args[0], args[1:]
|
||||||
command = compose_command(*args, **kwargs)
|
method_name = COMMAND_TO_METHOD.get(command, command)
|
||||||
puts "Running: #{command}" unless kwargs[:slient]
|
method = getattr(self, method_name, None)
|
||||||
|
if callable(method) and not method_name.startswith("_") and method_name != "call":
|
||||||
|
return method(*rest, **kwargs)
|
||||||
|
return self.compose(*args, **kwargs)
|
||||||
|
|
||||||
case kwargs[:execution_mode]
|
def compose(self, *args, **kwargs):
|
||||||
when WAIT_FOR_PROCESS
|
command = self._compose_command(*args, **kwargs)
|
||||||
wait_for_process_with_logging(command)
|
if not kwargs.get("silent"):
|
||||||
else
|
print(f"Running: {command}")
|
||||||
exec(command)
|
|
||||||
end
|
if kwargs.get("execution_mode") == WAIT_FOR_PROCESS:
|
||||||
end
|
self._wait_for_process_with_logging(command)
|
||||||
|
else:
|
||||||
|
os.execv("/bin/sh", ["/bin/sh", "-c", command])
|
||||||
|
|
||||||
# Primary command wrappers
|
# Primary command wrappers
|
||||||
def build(*args, **kwargs)
|
def build(self, *args, **kwargs):
|
||||||
compose(%w[build], *args, **kwargs)
|
self.compose("build", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def compile(*args, **kwargs)
|
def compile(self, *args, **kwargs):
|
||||||
run(*%w[api npm run build], execution_mode: WAIT_FOR_PROCESS)
|
self.run("api", "npm", "run", "build", execution_mode=WAIT_FOR_PROCESS)
|
||||||
exit($?.exitstatus) unless $?.success?
|
if self.last_exit_status != 0:
|
||||||
run(*%w[web npm run build])
|
sys.exit(self.last_exit_status)
|
||||||
end
|
self.run("web", "npm", "run", "build")
|
||||||
|
|
||||||
def up(*args, **kwargs)
|
def up(self, *args, **kwargs):
|
||||||
compose(*%w[up --remove-orphans], *args, **kwargs)
|
self.compose("up", "--remove-orphans", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def down(*args, **kwargs)
|
def down(self, *args, **kwargs):
|
||||||
compose(*%w[down --remove-orphans], *args, **kwargs)
|
self.compose("down", "--remove-orphans", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def logs(*args, **kwargs)
|
def logs(self, *args, **kwargs):
|
||||||
compose(*%w[logs -f], *args, **kwargs)
|
self.compose("logs", "-f", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def run(*args, **kwargs)
|
def run(self, *args, **kwargs):
|
||||||
compose(*%w[run --rm], *args, **kwargs)
|
self.compose("run", "--rm", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def ps(*args, **kwargs)
|
def ps(self, *args, **kwargs):
|
||||||
compose(*%w[ps], *args, **kwargs)
|
self.compose("ps", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
# Custom helpers
|
# Custom helpers
|
||||||
def api(*args, **kwargs)
|
def api(self, *args, **kwargs):
|
||||||
run(*%w[api], *args, **kwargs)
|
self.run("api", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def web(*args, **kwargs)
|
def web(self, *args, **kwargs):
|
||||||
run(*%w[web], *args, **kwargs)
|
self.run("web", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def check_types(*args, **kwargs)
|
def check_types(self, *args, **kwargs):
|
||||||
run(*%w[api npm run check-types], *args, **kwargs)
|
self.run("api", "npm", "run", "check-types", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def test(*args, **kwargs)
|
def test(self, *args, **kwargs):
|
||||||
service = args[0]
|
service = args[0] if args else None
|
||||||
if service == "api"
|
if service == "api":
|
||||||
test_api(*args.drop(1), **kwargs)
|
self.test_api(*args[1:], **kwargs)
|
||||||
elsif service == "web"
|
elif service == "web":
|
||||||
test_web(*args.drop(1), **kwargs)
|
self.test_web(*args[1:], **kwargs)
|
||||||
else
|
else:
|
||||||
test_api(*args, **kwargs)
|
self.test_api(*args, **kwargs)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api(*args, **kwargs)
|
def test_api(self, *args, **kwargs):
|
||||||
reformat_project_relative_path_filter_for_vitest!(args, "api/")
|
args = self._reformat_project_relative_path_filter_for_vitest(list(args), "api/")
|
||||||
run(*%w[test_api npm run test], *args, **kwargs)
|
self.run("test_api", "npm", "run", "test", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def test_web(*args, **kwargs)
|
def test_web(self, *args, **kwargs):
|
||||||
reformat_project_relative_path_filter_for_vitest!(args, "web/")
|
args = self._reformat_project_relative_path_filter_for_vitest(list(args), "web/")
|
||||||
run(*%w[test_web npm run test], *args, **kwargs)
|
self.run("test_web", "npm", "run", "test", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def sqlcmd(*args, **kwargs)
|
def sqlcmd(self, *args, **kwargs):
|
||||||
db_host = ENV.fetch('DB_HOST', 'localhost')
|
db_host = os.environ.get("DB_HOST", "localhost")
|
||||||
db_user = ENV.fetch('DB_USER', 'sa')
|
db_user = os.environ.get("DB_USER", "sa")
|
||||||
db_pass = ENV.fetch('DB_PASS', '1m5ecure!')
|
db_pass = os.environ.get("DB_PASS", "1m5ecure!")
|
||||||
db_name = ENV.fetch('DB_NAME', 'YHSI')
|
db_name = os.environ.get("DB_NAME", "YHSI")
|
||||||
compose(
|
self.compose(
|
||||||
*%w[exec db /opt/mssql-tools/bin/sqlcmd],
|
"exec",
|
||||||
*%W[-U #{db_user}],
|
"db",
|
||||||
*%W[-P #{db_pass}],
|
"/opt/mssql-tools/bin/sqlcmd",
|
||||||
*%W[-H #{db_host}],
|
"-U",
|
||||||
*%W[-d #{db_name}],
|
db_user,
|
||||||
'-I', # enable quoted identifiers, e.g. "table"."column"
|
"-P",
|
||||||
|
db_pass,
|
||||||
|
"-H",
|
||||||
|
db_host,
|
||||||
|
"-d",
|
||||||
|
db_name,
|
||||||
|
"-I", # enable quoted identifiers, e.g. "table"."column"
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
end
|
|
||||||
|
|
||||||
def db(*args, **kwargs)
|
def db(self, *args, **kwargs):
|
||||||
compose(*%w[exec db], *args, **kwargs)
|
self.compose("exec", "db", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def debug
|
def debug(self, *args, **kwargs):
|
||||||
api_container_id = container_id("api")
|
container_id = self._container_id("api")
|
||||||
puts "Waiting for breakpoint to trigger..."
|
print("Waiting for breakpoint to trigger...")
|
||||||
puts "'ctrl-c' to exit."
|
print("'ctrl-c' to exit.")
|
||||||
command = "docker attach --detach-keys ctrl-c #{api_container_id}"
|
command = f"docker attach --detach-keys ctrl-c {container_id}"
|
||||||
puts "Running: #{command}"
|
print(f"Running: {command}")
|
||||||
exec(command)
|
os.execv("/bin/sh", ["/bin/sh", "-c", command])
|
||||||
exit 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def npm(*args, **kwargs)
|
def npm(self, *args, **kwargs):
|
||||||
run(*%w[api npm], *args, **kwargs)
|
self.run("api", "npm", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def ts_node(*args, **kwargs)
|
def ts_node(self, *args, **kwargs):
|
||||||
run(*%w[api npm run ts-node], *args, **kwargs)
|
self.run("api", "npm", "run", "ts-node", *args, **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def knex(*args, **kwargs)
|
def knex(self, *args, **kwargs):
|
||||||
if RUBY_PLATFORM =~ /linux/
|
if sys.platform.startswith("linux"):
|
||||||
run(*%w[api npm run knex], *args, execution_mode: WAIT_FOR_PROCESS, **kwargs)
|
self.run("api", "npm", "run", "knex", *args, execution_mode=WAIT_FOR_PROCESS, **kwargs)
|
||||||
|
|
||||||
file_or_directory = "#{project_root}/api/src/db/migrations"
|
file_or_directory = os.path.join(self._project_root(), "api/src/db/migrations")
|
||||||
exit(0) unless take_over_needed?(file_or_directory)
|
if not self._take_over_needed(file_or_directory):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
ownit file_or_directory
|
self.ownit(file_or_directory)
|
||||||
else
|
else:
|
||||||
run(*%w[api npm run knex], *args, **kwargs)
|
self.run("api", "npm", "run", "knex", *args, **kwargs)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def migrate(*args, **kwargs)
|
def migrate(self, *args, **kwargs):
|
||||||
action = args[0]
|
action = args[0] if args else None
|
||||||
knex("migrate:#{action}", *args.drop(1), **kwargs)
|
self.knex(f"migrate:{action}", *args[1:], **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def seed(*args, **kwargs)
|
def seed(self, *args, **kwargs):
|
||||||
action = args[0]
|
action = args[0] if args else None
|
||||||
knex("seed:#{action}", *args.drop(1), **kwargs)
|
self.knex(f"seed:{action}", *args[1:], **kwargs)
|
||||||
end
|
|
||||||
|
|
||||||
def ownit(*args, **kwargs)
|
def ownit(self, *args, **kwargs):
|
||||||
file_or_directory = args[0]
|
file_or_directory = args[0] if args else None
|
||||||
raise ScriptError, "Must provide a file or directory path." if file_or_directory.nil?
|
if file_or_directory is None:
|
||||||
|
raise ValueError("Must provide a file or directory path.")
|
||||||
|
|
||||||
if RUBY_PLATFORM =~ /linux/
|
if sys.platform.startswith("linux"):
|
||||||
puts "Take ownership of the file or directory? #{file_or_directory}"
|
print(f"Take ownership of the file or directory? {file_or_directory}")
|
||||||
exec("sudo chown -R #{user_id}:#{group_id} #{file_or_directory}")
|
command = f"sudo chown -R {self._user_id()}:{self._group_id()} {file_or_directory}"
|
||||||
else
|
os.execv("/bin/sh", ["/bin/sh", "-c", command])
|
||||||
raise NotImplementedError, "Not implement for platform #{RUBY_PLATFORM}"
|
else:
|
||||||
end
|
raise NotImplementedError(f"Not implemented for platform {sys.platform}")
|
||||||
end
|
|
||||||
|
|
||||||
def plantuml_to_png(*args, **kwargs)
|
def plantuml_to_png(self, *args, **kwargs):
|
||||||
file_path = args.pop
|
args = list(args)
|
||||||
raise ScriptError, "Must provide a file path." if file_path.nil?
|
if not args:
|
||||||
|
raise ValueError("Must provide a file path.")
|
||||||
|
file_path = args.pop()
|
||||||
|
|
||||||
png_path = file_path.gsub(/\.(wsd|pu|puml|plantuml|uml)$/, ".png")
|
png_path = re.sub(r"\.(wsd|pu|puml|plantuml|uml)$", ".png", file_path)
|
||||||
|
|
||||||
command = <<~BASH
|
command = f"curl {' '.join(args)} --data-binary @'{file_path}' http://localhost:9999/png > '{png_path}'"
|
||||||
curl #{args.join(" ")} \
|
|
||||||
--data-binary @'#{file_path}' \
|
|
||||||
http://localhost:9999/png > '#{png_path}'
|
|
||||||
BASH
|
|
||||||
|
|
||||||
puts "Running: #{command}"
|
print(f"Running: {command}")
|
||||||
exec(command)
|
os.execv("/bin/sh", ["/bin/sh", "-c", command])
|
||||||
end
|
|
||||||
|
|
||||||
def bash_completions
|
def bash_completions(self, *args, **kwargs):
|
||||||
completions =
|
completions = sorted(
|
||||||
public_methods(false)
|
METHOD_TO_COMMAND.get(name, name)
|
||||||
.reject { |word| %i[call].include?(word) }
|
for name in vars(DevHelper)
|
||||||
.map { |word| METHOD_TO_COMMAND.fetch(word, word) }
|
if not name.startswith("_") and name != "call" and callable(getattr(DevHelper, name))
|
||||||
puts completions
|
)
|
||||||
end
|
print(" ".join(completions))
|
||||||
|
|
||||||
private
|
# Private helpers
|
||||||
|
def _wait_for_process_with_logging(self, command):
|
||||||
|
process = subprocess.Popen(
|
||||||
|
["/bin/sh", "-c", f"{command} 2>&1"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
for line in process.stdout:
|
||||||
|
print(line, end="")
|
||||||
|
process.wait()
|
||||||
|
self.last_exit_status = process.returncode
|
||||||
|
|
||||||
def wait_for_process_with_logging(command)
|
def _container_id(self, container_name, *args, **kwargs):
|
||||||
IO.popen("#{command} 2>&1") do |io|
|
command = self._compose_command("ps", "-q", container_name, *args, **kwargs)
|
||||||
until io.eof?
|
print(f"Running: {command}")
|
||||||
line = io.gets
|
result = subprocess.run(["/bin/sh", "-c", command], stdout=subprocess.PIPE, text=True)
|
||||||
puts line
|
container_id = result.stdout.strip()
|
||||||
end
|
print(f"Container id is: {container_id}")
|
||||||
end
|
return container_id
|
||||||
end
|
|
||||||
|
|
||||||
def container_id(container_name, *args, **kwargs)
|
def _service_running(self, container_name):
|
||||||
command = compose_command(*%w[ps -q], container_name, *args, **kwargs)
|
self.ps("-q", "--status=running", execution_mode=WAIT_FOR_PROCESS, silent=True)
|
||||||
puts "Running: #{command}"
|
return self.last_exit_status == 0
|
||||||
id_of_container = `#{command}`.chomp
|
|
||||||
puts "Container id is: #{id_of_container}"
|
|
||||||
id_of_container
|
|
||||||
end
|
|
||||||
|
|
||||||
def service_running?(container_name)
|
def _compose_command(self, *args, **kwargs):
|
||||||
ps(*%w[-q --status=running], execution_mode: WAIT_FOR_PROCESS, slient: true) != ""
|
environment = kwargs.get("environment", "development")
|
||||||
end
|
return f"cd {self._project_root()} && docker compose -f docker-compose.{environment}.yml {' '.join(args)}"
|
||||||
|
|
||||||
def compose_command(*args, **kwargs)
|
def _project_root(self):
|
||||||
environment = kwargs.fetch(:environment, "development")
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
"cd #{project_root} && docker compose -f docker-compose.#{environment}.yml #{args.join(" ")}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def project_root
|
def _take_over_needed(self, file_or_directory):
|
||||||
@project_root ||= File.absolute_path("#{__dir__}/..")
|
result = subprocess.run(
|
||||||
end
|
["/bin/sh", "-c", f"find {file_or_directory} -not -user {self._user_id()} -print -quit | grep -q ."],
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
def take_over_needed?(file_or_directory)
|
def _user_id(self):
|
||||||
files_owned_by_others =
|
if not sys.platform.startswith("linux"):
|
||||||
system("find #{file_or_directory} -not -user #{user_id} -print -quit | grep -q .")
|
raise NotImplementedError(f"Not implemented for platform {sys.platform}")
|
||||||
files_owned_by_others
|
return subprocess.run(["id", "-u"], stdout=subprocess.PIPE, text=True).stdout.strip()
|
||||||
end
|
|
||||||
|
|
||||||
def user_id
|
def _group_id(self):
|
||||||
unless RUBY_PLATFORM =~ /linux/
|
if not sys.platform.startswith("linux"):
|
||||||
raise NotImplementedError, "Not implement for platform #{RUBY_PLATFORM}"
|
raise NotImplementedError(f"Not implemented for platform {sys.platform}")
|
||||||
end
|
return subprocess.run(["id", "-g"], stdout=subprocess.PIPE, text=True).stdout.strip()
|
||||||
|
|
||||||
`id -u`.strip
|
def _reformat_project_relative_path_filter_for_vitest(self, args, prefix):
|
||||||
end
|
if args and args[0].startswith(prefix):
|
||||||
|
src_path_prefix = f"{prefix}src/"
|
||||||
|
|
||||||
def group_id
|
if args[0].startswith(src_path_prefix):
|
||||||
unless RUBY_PLATFORM =~ /linux/
|
|
||||||
raise NotImplementedError, "Not implement for platform #{RUBY_PLATFORM}"
|
|
||||||
end
|
|
||||||
|
|
||||||
`id -g`.strip
|
|
||||||
end
|
|
||||||
|
|
||||||
def reformat_project_relative_path_filter_for_vitest!(args, prefix)
|
|
||||||
if args.length.positive? && args[0].start_with?(prefix)
|
|
||||||
src_path_prefix = "#{prefix}src/"
|
|
||||||
test_path_regex = Regexp.escape(prefix)
|
|
||||||
src_path_regex = Regexp.escape(src_path_prefix)
|
|
||||||
|
|
||||||
if args[0].start_with?(src_path_prefix)
|
|
||||||
# TODO: handle other file types
|
# TODO: handle other file types
|
||||||
args[0] = args[0].gsub(/^#{src_path_regex}/, "tests/").gsub(/\.ts$/, ".test.ts")
|
args[0] = re.sub(r"\.ts$", ".test.ts", re.sub(f"^{re.escape(src_path_prefix)}", "tests/", args[0]))
|
||||||
else
|
else:
|
||||||
args[0] = args[0].gsub(/^#{test_path_regex}/, "")
|
args[0] = re.sub(f"^{re.escape(prefix)}", "", args[0])
|
||||||
end
|
|
||||||
|
|
||||||
puts "Reformatted path filter from project relative to service relative for vitest."
|
print("Reformatted path filter from project relative to service relative for vitest.")
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Only execute main function when file is executed
|
return args
|
||||||
DevHelper.call(*ARGV) if $PROGRAM_NAME == __FILE__
|
|
||||||
|
|
||||||
## Dev completions
|
|
||||||
# https://iridakos.com/programming/2018/03/01/bash-programmable-completion-tutorial
|
|
||||||
# _dev_completions () {
|
|
||||||
# local dev_command_path="$(which dev)"
|
|
||||||
# local dev_function_names
|
|
||||||
# dev_function_names="$(ruby "$dev_command_path" bash_completions)"
|
|
||||||
# # COMP_WORDS: an array of all the words typed after the name of the program the compspec belongs to
|
|
||||||
# # COMP_CWORD: an index of the COMP_WORDS array pointing to the word the current cursor is at - in other words, the index of the word the cursor was when the tab key was pressed
|
|
||||||
# # COMP_LINE: the current command line
|
|
||||||
# COMPREPLY=($(compgen -W "$dev_function_names" "${COMP_WORDS[$COMP_CWORD]}"))
|
|
||||||
# }
|
|
||||||
|
|
||||||
# complete -F _dev_completions dev
|
if __name__ == "__main__":
|
||||||
# complete -W "allow" direnv
|
DevHelper().call(*sys.argv[1:])
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Support dashes in command names
|
||||||
|
COMMAND_TO_METHOD = {}
|
||||||
|
METHOD_TO_COMMAND = {method: command for command, method in COMMAND_TO_METHOD.items()}
|
||||||
|
|
||||||
|
WAIT_FOR_PROCESS = "wait_for_process"
|
||||||
|
|
||||||
|
|
||||||
|
class ProdHelper:
|
||||||
|
def __init__(self):
|
||||||
|
self.last_exit_status = 0
|
||||||
|
|
||||||
|
# Core logic
|
||||||
|
def call(self, *args, **kwargs):
|
||||||
|
if not args:
|
||||||
|
return self.compose(*args, **kwargs)
|
||||||
|
|
||||||
|
command, rest = args[0], args[1:]
|
||||||
|
method_name = COMMAND_TO_METHOD.get(command, command)
|
||||||
|
method = getattr(self, method_name, None)
|
||||||
|
if callable(method) and not method_name.startswith("_") and method_name != "call":
|
||||||
|
return method(*rest, **kwargs)
|
||||||
|
return self.compose(*args, **kwargs)
|
||||||
|
|
||||||
|
def compose(self, *args, **kwargs):
|
||||||
|
command = self._compose_command(*args, **kwargs)
|
||||||
|
if not kwargs.get("silent"):
|
||||||
|
print(f"Running: {command}")
|
||||||
|
|
||||||
|
if kwargs.get("execution_mode") == WAIT_FOR_PROCESS:
|
||||||
|
self._wait_for_process_with_logging(command)
|
||||||
|
else:
|
||||||
|
os.execv("/bin/sh", ["/bin/sh", "-c", command])
|
||||||
|
|
||||||
|
# Primary command wrappers
|
||||||
|
def up(self, *args, **kwargs):
|
||||||
|
self.compose("up", "--remove-orphans", *args, **kwargs)
|
||||||
|
|
||||||
|
def down(self, *args, **kwargs):
|
||||||
|
self.compose("down", "--remove-orphans", *args, **kwargs)
|
||||||
|
|
||||||
|
def logs(self, *args, **kwargs):
|
||||||
|
self.compose("logs", "-f", *args, **kwargs)
|
||||||
|
|
||||||
|
def ps(self, *args, **kwargs):
|
||||||
|
self.compose("ps", *args, **kwargs)
|
||||||
|
|
||||||
|
def bash_completions(self, *args, **kwargs):
|
||||||
|
completions = sorted(
|
||||||
|
METHOD_TO_COMMAND.get(name, name)
|
||||||
|
for name in vars(ProdHelper)
|
||||||
|
if not name.startswith("_") and name != "call" and callable(getattr(ProdHelper, name))
|
||||||
|
)
|
||||||
|
print(" ".join(completions))
|
||||||
|
|
||||||
|
# Private helpers
|
||||||
|
def _wait_for_process_with_logging(self, command):
|
||||||
|
process = subprocess.Popen(
|
||||||
|
["/bin/sh", "-c", f"{command} 2>&1"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
for line in process.stdout:
|
||||||
|
print(line, end="")
|
||||||
|
process.wait()
|
||||||
|
self.last_exit_status = process.returncode
|
||||||
|
|
||||||
|
def _compose_command(self, *args, **kwargs):
|
||||||
|
environment = kwargs.get("environment", "production")
|
||||||
|
return f"cd {self._project_root()} && docker compose -f docker-compose.{environment}.yml {' '.join(args)}"
|
||||||
|
|
||||||
|
def _project_root(self):
|
||||||
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ProdHelper().call(*sys.argv[1:])
|
||||||
Reference in New Issue
Block a user