#!/bin/bash set -e if [[ -a ".env" ]]; then source .env fi function help() { echo echo "task [options]" echo echo "commands:" echo # Define column widths CMD_WIDTH=26 OPT_WIDTH=14 DESC_WIDTH=50 COLUMN="| %-${CMD_WIDTH}s | %-${OPT_WIDTH}s | %-${DESC_WIDTH}s |\n" # Print table header printf "$COLUMN" "Command" "Option" "Description" echo "|$(printf '%*s' $((CMD_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((OPT_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((DESC_WIDTH + 2)) '' | tr ' ' '-')|" # Env printf "$COLUMN" "version" "" "Show version of required tools." printf "$COLUMN" "info" "" "Show info about env." printf "$COLUMN" "template-dotenv" "" "Generate .env from .env.template." printf "$COLUMN" "export-kubeconfig" "[user][path]" "Export kubeconfig file for service account." printf "$COLUMN" "load-kubeconfig" "[ctx]" "Load kubeconfig file from pass." printf "$COLUMN" "load-dotenv" "" "Restore content to .env from pass entry." printf "$COLUMN" "save-kubeconfig" "[ctx]" "Save kubeconfig file in pass." printf "$COLUMN" "save-dotenv" "" "Store content of .env in pass entry." # Docs printf "$COLUMN" "install" "" "Install node dependencies." printf "$COLUMN" "docs" "" "Generate Helm chart readmes." printf "$COLUMN" "lint" "" "Lint Helm charts." printf "$COLUMN" "dev-vitepress" "" "Start VitePress dev server." printf "$COLUMN" "build-vitepress" "" "Build site with VitePress." printf "$COLUMN" "preview-vitepress" "" "Server VitePress build." # Host printf "$COLUMN" "start-kind" "" "Start kind Kubernetes cluster." printf "$COLUMN" "load-images" "" "Load Docker images into kind cluster." printf "$COLUMN" "delete-kind" "" "Delete kind Kubernetes cluster." printf "$COLUMN" "start-minikube" "" "Start minikube Kubernetes cluster." printf "$COLUMN" "setup-hosts" "" "Update hosts file." printf "$COLUMN" "forward" "[chart]" "Forward port." # Chart printf "$COLUMN" "list-chart" "" "List Helm charts." printf "$COLUMN" "list-context" "" "List contexts." printf "$COLUMN" "switch-context" "" "Switch kubeconfig file." printf "$COLUMN" "show-context" "" "Show active kubeconfig." printf "$COLUMN" "get-context-alias" "" "Get alias of current context." printf "$COLUMN" "template-values" "[ctx]" "Template values file." printf "$COLUMN" "update-dependencies" "[chart]" "Update dependencies for a chart." printf "$COLUMN" "install-chart" "[chart][ns]" "Install Helm chart." printf "$COLUMN" "upgrade-release" "[chart]" "Upgrade Helm release." printf "$COLUMN" "uninstall-release" "" "Uninstall Helm release." # kubectl printf "$COLUMN" "get-secrets" "[chart]" "Show secrets for chart." printf "$COLUMN" "exec-bash" "[chart]" "Open bash shell in container." printf "$COLUMN" "logs" "[grep]" "Select pods with grep and show logs." printf "$COLUMN" "logs-haproxy-ingress" "" "Show haproxy-ingress logs." printf "$COLUMN" "logs-ingress-nginx" "" "Show ingress-nginx logs." printf "$COLUMN" "list" "" "List Helm deployments." printf "$COLUMN" "get-pods" "" "List all pods." printf "$COLUMN" "login-oc" "" "Refresh the auth token for OpenShift." # Helm printf "$COLUMN" "add-repos" "" "Add the required Helm repos." printf "$COLUMN" "template-manifest" "[chart][ctx]" "Template all charts with Helm." printf "$COLUMN" "package-repo" "[chart]" "Package Helm charts." printf "$COLUMN" "index-repo" "" "Index Helm chart." printf "$COLUMN" "publish-repo" "" "Copy Helm chart files to dist." printf "$COLUMN" "remove-repo" "" "Remove the kubernetes-build repo." printf "$COLUMN" "commit" "" "Commit .tgz files." # LLM printf "$COLUMN" "commit-with-llm" "" "Commit with llm generated commit message." printf "$COLUMN" "update-with-llm" "[glob][prompt]" "Feed files matching glob to LLM and apply changes." # Test printf "$COLUMN" "test-clusterIssuer" "" "Test cluster issuer." printf "$COLUMN" "test-k8s" "[clean]" "Test Kubernetes environment." } # Default vars OC_PROJECT=${OC_PROJECT:="odoo"} CONTAINER_REGISTRY=${CONTAINER_REGISTRY:="mintsystem/"} CONTAINER_CONFIG="$HOME/.docker/$CONTAINER_REGISTRY" # Dynamic vars PASS_ENTRY=$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]') if kubectl &> /dev/null; then CURRENT_CONTEXT=$(kubectl config current-context 2> /dev/null) || CURRENT_CONTEXT="" fi # Import commands function clone-taskfile(){ if [ ! -d "$HOME/taskfile.build" ]; then echo -e "\033[38;5;214mGit\033[0m: Clone taskfile repo" git clone https://git.taskfile.build "$HOME/taskfile.build" else echo -e "\033[38;5;214mGit\033[0m: Pull taskfile repo" git -C "$HOME/taskfile.build" pull fi } if [ -d "$HOME/taskfile.build/bin" ]; then for file in "$HOME/taskfile.build/bin/"*; do if [ -f "$file" ]; then source "$file" fi done fi # Env Commands function version() { minikube version kind version helm version } function info() { echo "Current Context: $CURRENT_CONTEXT" echo "Context Alias: $(get-context-alias)" echo "External IP: $EXTERNAL_IP" } function install() { pnpm install } function setup-hosts() { echo "Adding entries to /etc/hosts ..." grep -qxF '127.0.0.1 knd.local' /etc/hosts || echo "127.0.0.1 knd.local" | sudo tee -a /etc/hosts > /dev/null grep -qxF "127.0.0.1 odoo.knd.local" /etc/hosts || echo "127.0.0.1 odoo.knd.local" | sudo tee -a /etc/hosts > /dev/null grep -qxF "127.0.0.1 odoo.local" /etc/hosts || echo "127.0.0.1 odoo.local" | sudo tee -a /etc/hosts > /dev/null grep -qxF "127.0.0.1 restic.knd.local" /etc/hosts || echo "127.0.0.1 restic.knd.local" | sudo tee -a /etc/hosts > /dev/null grep -qxF "127.0.0.1 restic.local" /etc/hosts || echo "127.0.0.1 restic.local" | sudo tee -a /etc/hosts > /dev/null } function test-k8s() { if [ "$1" = "clean" ]; then echo "Cleanup environment" delete-kind rm values.yaml else start-kind add-repos info install-chart cnpg kubectl wait --namespace cnpg-system --for=condition=Available --timeout=60s deployments/cnpg-cloudnative-pg update-dependencies odoo install-chart odoo install-ingress-nginx fi } function save-kubeconfig() { PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$USER" | tr '[:upper:]' '[:lower:]') KUBECONFIG_FILE="$HOME/.kube/config" if [ -n"$1" ]; then PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$1" | tr '[:upper:]' '[:lower:]') KUBECONFIG_FILE="$HOME/.kube/config.$1" fi if [ -f "$KUBECONFIG_FILE" ]; then echo "Store $KUBECONFIG_FILE file in pass: $PASS_KUBECONFIG_ENTRY" echo -e "\n$(cat $KUBECONFIG_FILE)" | pass insert -m -f "$PASS_KUBECONFIG_ENTRY" else echo "No $KUBECONFIG_FILE file found." fi } function load-kubeconfig() { PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$USER" | tr '[:upper:]' '[:lower:]') KUBECONFIG_FILE="$HOME/.kube/config" if [ -n"$1" ]; then PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$1" | tr '[:upper:]' '[:lower:]') KUBECONFIG_FILE="$HOME/.kube/config.$1" fi if pass find "$PASS_KUBECONFIG_ENTRY" >/dev/null; then echo "Load $KUBECONFIG_FILE file from pass: $PASS_KUBECONFIG_ENTRY" mkdir -p "$HOME/.kube/" pass show "$PASS_KUBECONFIG_ENTRY" | tail -n +2 > "$KUBECONFIG_FILE" else echo "Pass entry not found." fi } export-kubeconfig() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi KUBE_USER=$1 KUBE_OUTPUT_FILE=$2 kubectl config set-cluster default --server=https://k3s.raspberrypi.build --kubeconfig=$KUBE_OUTPUT_FILE kubectl config set-context default --cluster=default --user=$KUBE_USER --kubeconfig=$KUBE_OUTPUT_FILE kubectl config set-credentials "$KUBE_USER" --token=$(kubectl create token $KUBE_USER --duration=87600h) --kubeconfig=$KUBE_OUTPUT_FILE kubectl config use-context default --kubeconfig=$KUBE_OUTPUT_FILE echo "Kubeconfig exported for user $KUBE_USER to file $KUBE_OUTPUT_FILE" } # Docs Commands function docs() { pnpm run generate-readme --values clusterIssuer/values.yaml -r clusterIssuer/README.md pnpm run generate-readme --values odoo/values.yaml -r odoo/README.md pnpm run generate-readme --values postgres/values.yaml -r postgres/README.md pnpm run generate-readme --values vshnPostgres/values.yaml -r vshnPostgres/README.md pnpm run generate-readme --values hugo/values.yaml -r hugo/README.md # pnpm run generate-readme --values deploymentUpdater/values.yaml -r deploymentUpdater/README.md pnpm run generate-readme --values forgejoRunner/values.yaml -r forgejoRunner/README.md } function lint() { helm lint clusterIssuer helm lint odoo helm lint postgres helm lint vshnPostgres helm lint hugo helm lint deploymentUpdater helm lint forgejoRunner } function dev-vitepress() { cp README.md index.md pnpm run docs:dev } function build-vitepress() { cp README.md index.md pnpm run docs:build } function preview-vitepress() { pnpm run docs:preview } # Host Commands function start-kind() { echo "Start kind." kind create cluster } function load-images() { if kind &> /dev/null && kind get clusters | grep -q "kind"; then echo "Kind cluster is available. Checking for local Docker images..." if docker images -q "janikvonrotz/janikvonrotz.ch:latest" > /dev/null; then echo "Loading local Hugo image into Kind cluster..." kind load docker-image "janikvonrotz/janikvonrotz.ch:latest" fi if docker images -q "mintsystem/odoo:18.0.20250725" > /dev/null; then echo "Loading local Odoo image into Kind cluster..." kind load docker-image "mintsystem/odoo:18.0.20250725" fi fi } function delete-kind() { echo "Delete kind." kind delete cluster } function start-minikube() { echo "Start minikube." minikube start } function forward() { if test -z "$1"; then echo "\$1 is empty."; exit; fi "forward-$1" } function forward-postgres() { PORT=5432 echo "Connect to http://localhost:$PORT url in your browser." kubectl port-forward service/postgres $PORT:5432 } function forward-odoo() { PORT=8069 echo "Open http://localhost:$PORT url in your browser." kubectl port-forward service/odoo $PORT:8069 } function forward-hugo() { PORT=1313 echo "Open http://localhost:$PORT url in your browser." kubectl port-forward service/hugo $PORT:1313 } function forward-ingress-nginx() { PORT=8080 echo "Open http://odoo.knd.local:$PORT or http://odoo.local:$PORT url in your browser." kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller $PORT:80 } function forward-haproxy-ingress() { PORT=8080 echo "Open http://odoo.knd.local:$PORT or http://odoo.local:$POR url in your browser." kubectl port-forward -n haproxy-ingress svc/haproxy-ingress $PORT:80 } # Chart Commands function list-chart() { echo "cnpg" echo "k8up" echo "deploymentUpdater" echo "postgresql" echo "hugo" echo "postgres" echo "vshnpostgres" echo "cert-manager" echo "clusterIssuer" echo "ingress-nginx" echo "haproxy-ingress" echo "forgejoRunner" } function list-context() { echo "loc (knd.local)" echo "rpi (k3s.raspberrypi.build)" echo "chk (chk.mintcloud.ch)" echo "axo (axo.mintcloud.ch)" echo "ksi (k8s.sozialinfo.ch)" } get-context-alias() { echo "$(basename "$(readlink "$HOME/.kube/config")" | cut -d '.' -f 2)" } function switch-context() { if test -z "$1"; then echo "\$1 is empty."; exit; fi LINK_NAME="$HOME/.kube/config" echo "Remove $LINK_NAME" rm "$LINK_NAME" || true TARGET="$HOME/.kube/config.$1" touch "$TARGET" echo "Setup symlink from $LINK_NAME to $TARGET" ln -s "$TARGET" "$LINK_NAME" chmod 0644 "$TARGET" } function show-context() { ls -l "$HOME/.kube/config" } function template-values { ALIAS="$1" if [ -z "$ALIAS" ]; then ALIAS="$(get-context-alias)" fi export ACME_EMAIL export INFOMANIAK_API_TOKEN export FORGEJO_INSTANCE_TOKEN if [ -n "$ALIAS" ]; then echo "Template values.yaml from values.$ALIAS.yaml.template" envsubst < "values.$ALIAS.yaml.template" > "values.yaml" else echo "Template values.yaml from values.yaml.template" envsubst < "values.yaml.template" > "values.yaml" fi } function update-dependencies() { if test -z "$1"; then echo "\$1 is empty."; exit; fi helm dependency update "$1" } function install-chart() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if [ "$(get-context-alias)" = "axo" ]; then echo "Set oc project to $OC_PROJECT." oc project "$OC_PROJECT" fi template-values RELEASE_NAME="$1" if [ -n "$2" ]; then RELEASE_NAME="$2" fi RELEASE_NAME="$(echo "$RELEASE_NAME" | tr '[:upper:]' '[:lower:]')" if [[ "$1" =~ (hugo|odoo|postgres|vshnPostgres|deploymentUpdater|forgejoRunner) ]]; then echo "Install chart $1 as release $RELEASE_NAME." helm install "$RELEASE_NAME" "$1" \ --values ./values.yaml else "install-$1" fi } function install-k8up() { echo "Install k8up-io/k8up." helm install k8up k8up-io/k8up } function install-cnpg() { echo "Install cnpg/cloudnative-pg." helm upgrade --install cnpg \ --namespace cnpg-system \ --create-namespace \ cnpg/cloudnative-pg } function install-postgresql() { echo "Install bitnami/postgresql." helm install postgresql bitnami/postgresql --values odoo/values.yaml } function install-ingress-nginx() { if [ -n "$EXTERNAL_IP" ]; then echo "Install ingress-nginx/ingress-nginx with external IP $EXTERNAL_IP:" helm install ingress-nginx ingress-nginx/ingress-nginx \ --create-namespace --namespace ingress-nginx \ --set controller.service.externalIPs[0]=$EXTERNAL_IP else echo "Install ingress-nginx/ingress-nginx:" helm install ingress-nginx ingress-nginx/ingress-nginx \ --create-namespace --namespace ingress-nginx fi } function install-cert-manager() { echo "Install jetstack/cert-manager." helm install \ cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.18.2 \ --set crds.enabled=true \ --set config.featureGates.ACMEHTTP01IngressPathTypeExact=false # https://github.com/Infomaniak/cert-manager-webhook-infomaniak echo "Apply Infomaniak webhook" kubectl apply -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml } function install-clusterIssuer() { echo "Install clusterIssuer." helm install \ clusterIssuer clusterIssuer \ --namespace cert-manager \ --values ./values.yaml } function test-clusterIssuer() { echo "Apply certificate." cat < manifest.yaml elif [ "$1" = "clusterIssuer" ]; then helm --debug template clusterIssuer clusterIssuer \ --namespace cert-manager \ --values ./values.yaml > manifest.yaml fi } function list() { helm list } function package-repo() { if [ -n "$1" ]; then echo "Package $1 Helm chart." helm package "$1" else echo "Package all Helm charts." index-repo helm package clusterIssuer helm package postgres helm package vshnPostgres helm package odoo helm package hugo fi } function commit() { git config user.name 'Mint Bot' git config user.email 'bot@mint-system.ch' git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/Mint-System/Kubernetes-Build" git add *.tgz git commit -m "feat(helm): package revision update" git push || echo "No changes to commit" } function index-repo() { echo "Update index.yaml" helm repo index . } function publish-repo() { build-vitepress cp *.tgz .vitepress/dist cp index.yaml .vitepress/dist } function add-repos(){ echo "Add kubernetes-build." helm repo add kubernetes-build https://kubernetes.build echo "Add ingress-nginx." helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx echo "Add haproxy-ingress." helm repo add haproxy-ingress https://haproxy-ingress.github.io/charts echo "Add jetstack." helm repo add jetstack https://charts.jetstack.io echo "Add bitnami." helm repo add bitnami https://charts.bitnami.com/bitnami echo "Add cnpg." helm repo add cnpg https://cloudnative-pg.github.io/charts echo "Add k8up." helm repo add k8up-io https://k8up-io.github.io/k8up } function remove-repo(){ echo "Remove the kubernetes-build repo." helm repo remove kubernetes-build } if declare -f "$1" > /dev/null; then "$1" "${@:2}" else case "$1" in *) echo "Unknown command: $1" help exit 1 ;; esac fi