use std use primitives/io/interface.nu [_ansi _print] use primitives/infra/init.nu [get-provisioning-use-sops get-workspace-path get-provisioning-infra-path] use primitives/io/error.nu [throw-error] def find_file [start_path: string, match_path: string, only_first: bool] { mut found_path = "" mut search_path = $start_path let home_root = ($env.HOME | path dirname) while $found_path == "" and $search_path != "/" and $search_path != $home_root { if $search_path == "" { break } let res = if $only_first { (^find $search_path -type f -name $match_path -print -quit | complete) } else { (^find $search_path -type f -name $match_path err> (if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | complete) } if $res.exit_code == 0 { $found_path = ($res.stdout | str trim) } $search_path = ($search_path | path dirname) } $found_path } export def run_cmd_sops [task: string, cmd: string, source_path: string, error_exit: bool] { let str_cmd = $"-($cmd)" let use_sops_value = (get-provisioning-use-sops | into string) let res = if ($use_sops_value | str contains "age") { if $env.SOPS_AGE_RECIPIENTS? != null { (with-env {SOPS_AGE_KEY_FILE: (get-sops-age-key-file)} { do {^sops $str_cmd --config (find-sops-key) --age $env.SOPS_AGE_RECIPIENTS $source_path} | complete }) } else { if $error_exit { (throw-error $"πŸ›‘ Sops with age error" $"(_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green)($source_path)(_ansi reset)" "on_sops decrypt" --span (metadata $task).span) } else { _print $"πŸ›‘ Sops with age error (_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green_bold)($source_path)(_ansi reset)" return "" } } } else { (^sops $str_cmd --config (find-sops-key) $source_path | complete) } if $res.exit_code != 0 { if $error_exit { (throw-error $"πŸ›‘ Sops error" $"(_ansi red)($source_path)(_ansi reset) ($res.stdout)" $"on_sops ($task)" --span (metadata $res).span) } else { _print $"πŸ›‘ Sops error (_ansi red)($source_path)(_ansi reset) ($res.exit_code)" return "" } } $res.stdout } export def on_sops [ task: string source_path: string output_path?: string ...args --check (-c) --error_exit --quiet ] { match $task { "sed" => { if (is_sops_file $source_path) { ^sops $source_path } else { (throw-error $"πŸ›‘ File (_ansi green_italic)($source_path)(_ansi reset) exists" $"No (_ansi yellow_bold)sops(_ansi reset) content found " "on_sops sed" --span (metadata $source_path).span) } } "is_sops" | "i" => { return (is_sops_file $source_path) } "encrypt" | "encode" | "e" => { if not ($source_path | path exists) { if not $quiet { _print $"πŸ›‘ No file ($source_path) found to decrypt with sops " } return "" } if (is_sops_file $source_path) { if not $quiet { _print $"πŸ›‘ File ($source_path) alredy with sops " } return (open -r $source_path) } let result = (run_cmd_sops "encrypt" "e" $source_path $error_exit) if ($output_path | is-not-empty) { $result | save -f $output_path if not $quiet { _print $"Result saved in ($output_path) " } } return $result } "generate" | "gen" | "g" => { generate_sops_file $source_path $output_path $quiet } "decrypt" | "decode" | "d" => { if not ($source_path | path exists) { if not $quiet { _print $"πŸ›‘ No file ($source_path) found to decrypt with sops " } return "" } if not (is_sops_file $source_path) { if not $quiet { _print $"πŸ›‘ File ($source_path) does not have sops info " } return (open -r $source_path) } let result = (run_cmd_sops "decrypt" "d" $source_path $error_exit) if ($output_path | is-not-empty) { $result | save -f $output_path if not $quiet { _print $"Result saved in ($output_path) " } } return $result } _ => { (throw-error $"πŸ›‘ Option " $"(_ansi red)($task)(_ansi reset) undefined") return "" } } } export def generate_sops_file [source_path: string, target_path: string, quiet: bool] { let result = (on_sops "encrypt" $source_path --error_exit) if result == "" { _print $"πŸ›‘ File ($source_path) not sops generated" return false } $result | save -f $target_path if not $quiet { _print $"($source_path) generated for 'sops' " } true } export def generate_sops_settings [mode: string, target: string, file: string] { _print "" } export def edit_sop [items: list] { _print "" } export def is_sops_file [target: string] { if not ($target | path exists) { (throw-error $"πŸ›‘ File (_ansi green_italic)($target)(_ansi reset)" $"(_ansi red_bold)Not found(_ansi reset)" $"is_sops_file ($target)" --span (metadata $target).span) } let file_sops = (open $target --raw) if ($file_sops | find "sops" | length) == 0 { return false } if ($file_sops | find "ENC[" | length) == 0 { return false } true } export def decode_sops_file [source: string, target: string, quiet: bool] { if $quiet { on_sops "decrypt" $source --quiet } else { on_sops "decrypt" $source } | save --force $target } export def get_def_sops [current_path: string] { let use_sops = (get-provisioning-use-sops) if ($use_sops | is-empty) { return "" } let start_path = if ($current_path | path exists) { $current_path } else { $"((get-workspace-path))/($current_path)" } let sops_file = "sops.yaml" mut provisioning_sops = (find_file $start_path $sops_file true) if $provisioning_sops == "" and ($env.HOME | path join ".config" | path join "provisioning" | path join $sops_file | path exists) { $provisioning_sops = ($env.HOME | path join ".config" | path join "provisioning" | path join $sops_file) } if $provisioning_sops == "" and ($env.HOME | path join ".provisioning" | path join $sops_file | path exists) { $provisioning_sops = ($env.HOME | path join ".provisioning" | path join $sops_file) } if $provisioning_sops == "" { _print $"❗Error no (_ansi red_bold)($sops_file)(_ansi reset) file for secure operations found " exit 1 } ($provisioning_sops | default "") } export def get_def_age [current_path: string] { let use_sops = (get-provisioning-use-sops | into string) if not ($use_sops | str contains "age") { return "" } let kage_file = ".kage" let start_path = if ($current_path | path exists) { $current_path } else { ((get-provisioning-infra-path) | path join $current_path) } let provisioning_kage = (find_file $start_path $kage_file true) let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".config" | path join "provisioning " | path join $kage_file | path exists) { ($env.HOME | path join ".config" | path join "provisioning " | path join $kage_file) } else { $provisioning_kage } let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".provisioning " | path join $kage_file | path exists) { ($env.HOME | path join ".provisioning " | path join $kage_file) } else { $provisioning_kage } let workspace_path = (get-workspace-path) let provisioning_kage = if $provisioning_kage == "" and ($workspace_path | is-not-empty) and (($workspace_path | path join ".provisioning" | path join $kage_file) | path exists) { ($workspace_path | path join ".provisioning" | path join $kage_file) } else { $provisioning_kage } if $provisioning_kage == "" { _print $"❗Error no (_ansi red_bold)($kage_file)(_ansi reset) file for secure operations found " exit 1 } ($provisioning_kage | default "") } export def find-sops-key [] { let from_env = ($env.PROVISIONING_SOPS? | default "") if ($from_env | is-not-empty) { return $from_env } let search_path = ($env.CURRENT_KLOUD_PATH? | default ($env.PROVISIONING_WORKSPACE_PATH? | default $env.PWD)) get_def_sops $search_path } export def get-sops-age-key-file [] { $env.SOPS_AGE_KEY_FILE? | default ($env.PROVISIONING_KAGE? | default "") }