Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dameyerdave committed Jun 26, 2024
0 parents commit 4d3a653
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Package Application with Pyinstaller

on:
push:
tags:
- "*"

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Package Application
uses: JackMcKew/[email protected]
with:
path: src

- uses: actions/upload-artifact@v2
with:
name: name-of-artifact
path: src/dist/linux
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/old
/build
/dist
*.spec
Pipfile

15 changes: 15 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"editorconfig.editorconfig",
"vue.volar",
"wayou.vscode-todo-highlight"
],
"unwantedRecommendations": [
"octref.vetur",
"hookyqr.beautify",
"dbaeumer.jshint",
"ms-vscode.vscode-typescript-tslint-plugin"
]
}
21 changes: 21 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
"typescript.tsdk": "node_modules/typescript/lib",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "never"
}
},
"[makefile]": {
"editor.insertSpaces": false,
"editor.detectIndentation": false
},
"cSpell.words": ["KEYCLOAK"]
}
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
default: dist/dcc/dcc

clean:
@rm -rf build dist

dist/dcc/dcc: clean
@pyinstaller dcc.py

run:
@dist/dcc/dcc
152 changes: 152 additions & 0 deletions dcc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import click
import sh
from sys import argv
from helpers import TableOutput as T, Message as M, Executor as E
import json
from sys import exit
from os.path import isfile, isdir
from os import environ, getcwd
from dotenv import load_dotenv

docker = sh.Command("docker")
bash = sh.Command("bash")


def __bake_command(command_line: str):
return (command_line).split()


def __handle_output(output: str) -> str:
# We remove the last \n
return output.rstrip("\n")


def __containers(filter: str = None):
_filter = ""
if filter:
_filter = f"--filter name={filter}"
cmd = docker.bake(*__bake_command("ps -a %s --format {{.Names}}" % _filter))
return __handle_output(cmd()).split("\n")


def __inspect(filter: str = None):
inspection = []
for cont in __containers(filter):
cmd = docker.bake(*__bake_command("inspect %s" % cont))
inspection.append(json.loads(__handle_output(cmd()))[0])
return inspection


def __prepare_compose_command(docker_arguments):
if not isfile("docker-compose.yml"):
M.error("There is no docker-compose.yml file in this directory!")
exit(1)
dcc_env = None
env = {
"GIT_VERSION": "?.?.?",
"GIT_BRANCH": "N/A",
"GIT_LASTCOMMITDATE": "N/A",
"GIT_COMMITHASH": "N/A",
"UID": 0,
"GID": 0,
"UNAME": "root",
}
if isfile(".env"):
load_dotenv(".env")
dcc_env = environ.get("DCC_ENV")
M.debug(f"Running DCC environment {dcc_env}.")
else:
M.warn(f"No .env file found in current directory: {getcwd()}")
if isdir(".git"):
if E.success("git rev-parse --is-inside-work-tree"):
env["GIT_VERSION"] = E.run("git describe --always")
env["GIT_BRANCH"] = E.run("git rev-parse --abbrev-ref HEAD")
env["GIT_LASTCOMMITDATE"] = E.run("git log -1 --format=%cI")
env["GIT_COMMITHASH"] = E.run("git rev-parse HEAD")
M.debug("Git repository detected.")
else:
M.warn("Not a git repository.")

env["UID"] = E.run("id -u")
env["GID"] = E.run("id -g")
env["UNAME"] = E.run("whoami")
environ.update(env)

docker_files = ["docker-compose.yml"]
if dcc_env:
docker_files.append(f"docker-compose.{dcc_env}.yml")
if isfile("docker-compose.override.yml"):
docker_files.append("docker-compose.override.yml")
command = (
f"docker compose -f {' -f '.join(docker_files)} {' '.join(docker_arguments)}"
)
M.debug(f"Command: {command}")
return command


def __execute_compose_command(command: str):
try:
bash("-c", command, _fg=True)
except Exception:
M.error(f"Error executing command '{command}'.")
# traceback.print_exc()


@click.command()
@click.argument("filter", required=False)
def ls(filter: str = None):
_filter = ""
if filter:
_filter = f"--filter name={filter}"
cmd = docker.bake(
*__bake_command(
"ps -a %s -a --format {{.Names}}#{{.Status}}#{{.Image}}" % _filter
)
)
output = __handle_output(cmd())
T.out(output, headers=("Name", "Status", "Image"))


@click.command()
@click.argument("filter", required=False)
def po(filter: str = None):
output = ""
for cont in __containers(filter):
cmd = docker.bake(*__bake_command("port %s" % cont))
sep = "\n"
output += f"{cont}#{', '.join(__handle_output(cmd()).split(sep))}\n"
T.out(__handle_output(output), headers=("Name", "Ports"))


@click.command()
@click.argument("filter", required=False)
def rp(filter: str = None):
output = ""
for i in __inspect(filter):
output += (
f"{i['Name'].lstrip('/')}#{i['HostConfig']['RestartPolicy']['Name']}\n"
)
T.out(__handle_output(output), headers=("Name", "Restart"))


@click.command()
@click.argument("container")
def sh(container: str):
__execute_compose_command(
__prepare_compose_command(["exec", container, "/usr/bin/env bash"])
)


def dcc(docker_arguments):
__execute_compose_command(__prepare_compose_command(docker_arguments))


if __name__ == "__main__":
if len(argv) < 2:
M.error("Please specify a command.")
exit(1)

if argv[1] in globals().keys():
globals()[argv[1]](argv[2:])
else:
dcc(argv[1:])
160 changes: 160 additions & 0 deletions helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"""
Helpers
====================================
Helper classes and functions
"""

import os
import shlex
from subprocess import run
from enum import Enum
from yachalk import chalk
from rich.console import Console
from rich.table import Table


class ReturnCode(Enum):
OK = 0


class Executor:
"""
Class to execute shell commands
"""

@classmethod
def success(cls, command: str, env: dict = None) -> bool:
ret, _ = cls.__run(command, env=env)
return ret == ReturnCode.OK.value

@classmethod
def run(cls, command: str, env: dict = None) -> str:
_, output = cls.__run(command, env=env)
return cls.__handle_output(output)

@classmethod
def __run(cls, command: str, env: dict = None) -> tuple[int, str]:
"""
Runs a shell command with the default environment.
If env is given it is _UPDATED_ to the default environment.
"""
_env = os.environ.copy()
if env:
_env.update(env)
p = run(shlex.split(command), capture_output=True, env=_env)
return p.returncode, cls.__handle_output(p.stdout.decode())

@classmethod
def __handle_output(cls, output: str) -> str:
# We remove the last \n
return output.rstrip("\n")


class Message:
"""
Class to output colored messages to the console
"""

@classmethod
def info(cls, message):
print(chalk.green_bright.bold(message))

@classmethod
def warn(cls, message):
print(chalk.yellow_bright.bold(message))

@classmethod
def error(cls, message):
print(chalk.red_bright.bold(message))

@classmethod
def debug(cls, message):
print(chalk.blue_bright.bold(message))


class TableOutput:
"""
Class to output text in table format
"""

console = Console()

@classmethod
def out(
cls,
data: str | list,
sep: str = "#",
headers: tuple[str] = None,
show_lines=False,
):
table = Table(
show_header=(headers is not None), show_lines=show_lines, show_edge=False
)
if headers:
for header in headers:
table.add_column(header)

if isinstance(data, str):
data = data.split("\n")

for line in data:
if isinstance(line, str):
table.add_row(*line.split(sep))
elif isinstance(line, list):
table.add_row(*line)

cls.console.print(table)


####################
# OLD CODE
####################


# class Interactive():
# """
# Execute a command interactively with pseudo terminal
# found at https://stackoverflow.com/questions/41542960/run-interactive-bash-with-popen-and-a-dedicated-tty-python
# """

# def __init__(self, command: str = '/bin/bash', env: dict = None):
# self.command = command
# self.env = os.environ.copy()
# if env:
# self.env.update(env)
# self.process = None

# def run(self):
# old_tty = termios.tcgetattr(sys.stdin)
# tty.setraw(sys.stdin.fileno())

# master, slave = pty.openpty()

# try:
# self.process = Popen(
# shlex.split(self.command),
# preexec_fn=os.setsid,
# stdin=slave,
# stdout=slave,
# stderr=slave,
# env=self.env)

# while True:
# r, _, _ = select.select([sys.stdin, master], [], [])
# if sys.stdin in r:
# d = os.read(sys.stdin.fileno(), 10240)
# os.write(master, d)
# elif master in r:
# o = os.read(master, 10240)
# if o:
# os.write(sys.stdout.fileno(), o)

# if self.process.poll() is not None:
# sys.stdout.flush()
# break
# else:
# sleep(0.1)

# finally:
# # restore tty settings back
# termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
Loading

0 comments on commit 4d3a653

Please sign in to comment.