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