init: A cli to run openDesk pipelines
This commit is contained in:
commit
3cc79d3bb5
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.envrc
|
||||
.direnv
|
||||
__*
|
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1750134718,
|
||||
"narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
88
flake.nix
Normal file
88
flake.nix
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
with pkgs;
|
||||
let
|
||||
py-typer = python313.pkgs.buildPythonPackage rec {
|
||||
pname = "typer";
|
||||
version = "0.16.0";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "fastapi";
|
||||
repo = "typer";
|
||||
tag = version;
|
||||
hash = "sha256-WB9PIxagTHutfk3J+mNTVK8bC7TMDJquu3GLBQgaras=";
|
||||
};
|
||||
|
||||
build-system = [ python313Packages.pdm-backend ];
|
||||
|
||||
dependencies = [
|
||||
python313Packages.click
|
||||
python313Packages.typing-extensions
|
||||
# Build includes the standard optional by default
|
||||
# https://github.com/tiangolo/typer/blob/0.12.3/pyproject.toml#L71-L72
|
||||
] ++ optional-dependencies.standard;
|
||||
|
||||
optional-dependencies = {
|
||||
standard = [
|
||||
python313Packages.rich
|
||||
python313Packages.shellingham
|
||||
];
|
||||
};
|
||||
|
||||
nativeCheckInputs =
|
||||
[
|
||||
python313Packages.coverage # execs coverage in tests
|
||||
python313Packages.pytest-xdist
|
||||
python313Packages.pytestCheckHook
|
||||
writableTmpDirAsHomeHook
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
procps
|
||||
];
|
||||
|
||||
disabledTests =
|
||||
[
|
||||
"test_scripts"
|
||||
# Likely related to https://github.com/sarugaku/shellingham/issues/35
|
||||
# fails also on Linux
|
||||
"test_show_completion"
|
||||
"test_install_completion"
|
||||
]
|
||||
++ lib.optionals (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isAarch64) [
|
||||
"test_install_completion"
|
||||
];
|
||||
|
||||
pythonImportsCheck = [ "typer" ];
|
||||
|
||||
meta = {
|
||||
description = "Library for building CLI applications";
|
||||
homepage = "https://typer.tiangolo.com/";
|
||||
changelog = "https://github.com/tiangolo/typer/releases/tag/${version}";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = with lib.maintainers; [ winpat ];
|
||||
};
|
||||
};
|
||||
|
||||
pythonEnv = python313.withPackages (p: [
|
||||
p.python-gitlab py-typer
|
||||
]);
|
||||
in
|
||||
{
|
||||
devShells.default = mkShell rec {
|
||||
packages = [
|
||||
pythonEnv
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
169
od-cli.py
Executable file
169
od-cli.py
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
# TODOs:
|
||||
# - Log for past runs (id/link to pipeline)
|
||||
# - Stop / Restart Pipeline
|
||||
import gitlab
|
||||
import typer
|
||||
import os
|
||||
|
||||
from enum import StrEnum
|
||||
from typing_extensions import Annotated
|
||||
from typing import List
|
||||
|
||||
app = typer.Typer()
|
||||
gl = gitlab.Gitlab(
|
||||
url=os.environ.get("OD_GL_URL", ""), private_token=os.environ.get("OD_GL_TOKEN", "")
|
||||
)
|
||||
MASTER_PASSWORD = os.environ.get(
|
||||
"OD_MASTER_PASSWORD", "sovereign-workplace"
|
||||
)
|
||||
USER = os.environ.get("OD_USER", "od-user")
|
||||
GL_USER = os.environ.get("OD_GL_USER", "od-gl-user")
|
||||
GL_PROJECT = os.environ.get("OD_GL_PROJECT", "1317")
|
||||
|
||||
class Clusters(StrEnum):
|
||||
qa = "qa"
|
||||
run = "run"
|
||||
|
||||
class Apps(StrEnum):
|
||||
all = "all"
|
||||
none = "none"
|
||||
migrations = "migrations"
|
||||
services = "services"
|
||||
ums = "ums"
|
||||
collabora = "collabora"
|
||||
cryptpad = "cryptpad"
|
||||
element = "element"
|
||||
ox = "ox"
|
||||
xwiki = "xwiki"
|
||||
nextcloud = "nextcloud"
|
||||
openproject = "openproject"
|
||||
jitsi = "jitsi"
|
||||
notes = "notes"
|
||||
|
||||
|
||||
@app.command()
|
||||
def pipelines(n=15, username=GL_USER):
|
||||
# gl.enable_debug()
|
||||
opendesk = gl.projects.get(1317)
|
||||
pipelines = opendesk.pipelines.list(iterator=True, username=username)
|
||||
|
||||
# Show last N pipelines
|
||||
for i, p in enumerate(pipelines):
|
||||
if i > int(n):
|
||||
break
|
||||
match p.status:
|
||||
case "success":
|
||||
status = "✔"
|
||||
case "failed":
|
||||
status = "❌"
|
||||
case "running":
|
||||
status = "🕑"
|
||||
case _:
|
||||
status = p.status
|
||||
print(
|
||||
f"[{p.created_at[:-5].replace('T', ' ')}]-({p.ref}) {status}: {p.web_url}"
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def pipeline(pid: str):
|
||||
opendesk = gl.projects.get()
|
||||
pipeline = opendesk.pipelines.get(pid)
|
||||
variables = pipeline.variables.list(get_all=True)
|
||||
print(variables)
|
||||
|
||||
|
||||
def _new_pipeline(ref: str, variables: str):
|
||||
variables = _parse_variables(variables)
|
||||
opendesk = gl.projects.get(1317)
|
||||
new_pipeline = opendesk.pipelines.create({"ref": ref, "variables": variables})
|
||||
print(new_pipeline)
|
||||
|
||||
|
||||
@app.command()
|
||||
def new_pipeline(
|
||||
ref: str,
|
||||
cluster: Annotated[Clusters, typer.Option(case_sensitive=False)],
|
||||
namespace: str = f"{USER}-py-ce",
|
||||
test: bool = False,
|
||||
test_branch: str = "develop",
|
||||
ee: bool = False,
|
||||
env_stop: bool = True,
|
||||
debug: bool = True,
|
||||
default_accounts: bool = True,
|
||||
deploy: Annotated[List[Apps], typer.Option(case_sensitive=False)] = [Apps.none],
|
||||
):
|
||||
if test:
|
||||
debug = False
|
||||
|
||||
variables = [
|
||||
f"CLUSTER:{cluster}",
|
||||
f"NAMESPACE:{namespace}",
|
||||
f"MASTER_PASSWORD_WEB_VAR:{MASTER_PASSWORD}",
|
||||
f"ENV_STOP_BEFORE:{_tf_to_yn(env_stop)}",
|
||||
f"RUN_TESTS:{_tf_to_yn(test)}",
|
||||
f"TESTS_BRANCH:{test_branch}",
|
||||
f"DEBUG_ENABLED:{_tf_to_yn(debug)}",
|
||||
f"CREATE_DEFAULT_ACCOUNTS:{_tf_to_yn(default_accounts)}",
|
||||
f"OPENDESK_ENTERPRISE:{'true' if ee else 'false'}",
|
||||
]
|
||||
|
||||
if Apps.none in deploy:
|
||||
pass
|
||||
elif Apps.all in deploy and len(deploy) == 1:
|
||||
variables.append("DEPLOY_ALL_COMPONENTS:yes")
|
||||
elif Apps.all in deploy and len(deploy) > 1:
|
||||
print("You cannot deploy 'all' but also specify specific apps at the same time")
|
||||
exit(1)
|
||||
else:
|
||||
variables += [
|
||||
f"DEPLOY_{app.value.upper()}:'yes'"
|
||||
for app in deploy
|
||||
]
|
||||
|
||||
print(variables)
|
||||
|
||||
_new_pipeline(ref, ",".join(variables))
|
||||
|
||||
|
||||
def _parse_variables(var_str: str) -> list[dict[str, str]]:
|
||||
parts = var_str.split(",")
|
||||
return [{"key": k, "value": v} for k, v in (p.strip().split(":") for p in parts)]
|
||||
|
||||
|
||||
def _yn_to_tf(yn: str | bool) -> bool:
|
||||
if type(yn) is bool:
|
||||
return yn
|
||||
else:
|
||||
return True if yn == "yes" else False
|
||||
|
||||
|
||||
def _tf_to_yn(tf: bool) -> str:
|
||||
if type(tf) is not bool:
|
||||
return tf
|
||||
else:
|
||||
return "yes" if tf else "no"
|
||||
|
||||
|
||||
@app.command()
|
||||
def self_test():
|
||||
_test_yn()
|
||||
_test_tf()
|
||||
|
||||
|
||||
def _test_yn():
|
||||
should = [False, False, False, False, False, True, True]
|
||||
for yn, tf in zip([False, "no", "nope", "y", "n", "yes", True], should):
|
||||
assert _yn_to_tf(yn) == tf, f"{yn} != {tf} but is {_yn_to_tf(yn)}"
|
||||
|
||||
|
||||
def _test_tf():
|
||||
should = ["no", "yes"]
|
||||
for tf, yn in zip([False, True], should):
|
||||
assert _tf_to_yn(tf) == yn, f"{tf} != {yn} but is {_tf_to_yn(tf)}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gl.auth()
|
||||
app()
|
Loading…
x
Reference in New Issue
Block a user