Compare commits

...

30 Commits

Author SHA1 Message Date
1ad50ebcf8 v0.8.1 WIP
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-02-20 14:53:44 -06:00
c483a1056f v0.8.0
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline was successful
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/release/publish-docs Pipeline was successful
2025-02-15 23:00:10 -06:00
3b9f569310 CI config changed
Some checks failed
ci/woodpecker/push/publish-docs Pipeline failed
2025-02-15 22:54:11 -06:00
843be7968b CI config changed 2025-02-15 22:50:44 -06:00
d477d850ac CI config changed 2025-02-15 22:46:49 -06:00
8eb3229af7 v0.8.0 2025-02-15 22:29:19 -06:00
d89a208bbd v0.8.0 2025-02-15 22:28:01 -06:00
0d28d6afcf breaking changes to keys 2025-02-15 22:27:11 -06:00
7c42a9a7cd v0.7.8
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
2025-02-14 14:46:10 -06:00
31339fb4d8 v0.7.7
All checks were successful
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 14:31:24 -06:00
c642e827f5 v0.7.6
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 14:18:02 -06:00
a328239021 v0.7.5
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 13:39:18 -06:00
4ee60184bf v0.7.4
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 13:11:08 -06:00
161ad31577 v0.7.3
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 12:54:05 -06:00
7c5f4a95da v0.7.2
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 12:21:21 -06:00
4981acbf9d update CI configs 2025-02-14 12:19:21 -06:00
932d5c380f v0.7.1
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline was successful
2025-02-14 11:56:46 -06:00
f84d76badf updat GitHube workflow file
All checks were successful
ci/woodpecker/push/publish-docs Pipeline was successful
2025-02-11 23:05:08 -06:00
6ee6e10621 v0.7.0 update docs
All checks were successful
ci/woodpecker/push/publish-docs Pipeline was successful
2025-02-11 22:00:10 -06:00
127d38c076 v0.7.0 make changes to release script 2025-02-11 21:48:40 -06:00
0218dee76d v0.7.0 change GoReleaser file
Some checks failed
ci/woodpecker/push/publish-docs Pipeline failed
2025-02-11 21:47:23 -06:00
67a1eab908 v0.7.0 make changes to release script
Some checks failed
ci/woodpecker/push/publish-docs Pipeline failed
ci/woodpecker/tag/gitea Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline failed
2025-02-11 21:37:42 -06:00
c618ca33f8 v0.7.0 make changes to release script
Some checks failed
ci/woodpecker/push/publish-docs Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline is pending
ci/woodpecker/tag/gitea Pipeline failed
2025-02-11 21:30:13 -06:00
6e7d912fa2 v0.7.0 bump version
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline is pending
ci/woodpecker/push/publish-docs Pipeline failed
ci/woodpecker/tag/gitea Pipeline failed
2025-02-11 21:19:57 -06:00
b90d1958b2 [WIP] v0.7.0 fix comments
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-02-11 12:19:09 -06:00
c187fbb735 [WIP] v0.7.0 almost ready to release
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-02-10 17:00:02 -06:00
c3de4386ab [WIP] v0.7.0 almost ready to release
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-02-10 16:55:54 -06:00
e20141043c Merge branch 'remoteResources' into develop
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-02-08 16:14:37 -06:00
11ec1a98d8 [WIP] v0.7.0 almost ready to release 2025-02-08 15:17:34 -06:00
5d3c265ce9 add comments
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-01-23 16:47:43 -06:00
63 changed files with 916 additions and 445 deletions

View File

@ -1,3 +0,0 @@
kind: Added
body: '[feat]: package `packageOperation` option `checkVersion` implemented'
time: 2025-01-11T21:15:46.207199643-06:00

View File

@ -1,3 +0,0 @@
kind: Added
body: user management added - see docs
time: 2025-01-11T21:18:13.182822019-06:00

View File

@ -1,3 +0,0 @@
kind: Added
body: Support for remote config sources. Only config file and list can be used for now.
time: 2025-01-13T23:12:48.383700682-06:00

View File

@ -1,3 +0,0 @@
kind: Added
body: Cache functionality - still a WIP
time: 2025-01-28T15:35:24.512485671-06:00

View File

@ -1,3 +0,0 @@
kind: Changed
body: Internal refactoring of config setup
time: 2025-01-13T23:10:07.215735108-06:00

View File

@ -1,3 +0,0 @@
kind: Changed
body: Formatting and sending for notifications
time: 2025-01-13T23:16:22.260458782-06:00

View File

@ -1,3 +0,0 @@
kind: Changed
body: "name of `configfetcher` to `remotefetcher`"
time: 2025-01-28T15:42:04.282668058-06:00

View File

@ -1,3 +0,0 @@
kind: Fixed
body: Parsing of remote URLs when determining list config file path
time: 2025-01-28T15:38:06.957506929-06:00

16
.changes/v0.7.0.md Normal file
View File

@ -0,0 +1,16 @@
## v0.7.0 - 2025-02-11
### Added
* [feat]: package `packageOperation` option `checkVersion` implemented
* user management added - see docs
* Support for remote config sources. Only config file and list can be used for now.
* Cache functionality - still a WIP
* Flag `--s3-endpoint` for config file fetching from S3
### Changed
* Internal refactoring of config setup
* Formatting and sending for notifications
* name of `configfetcher` to `remotefetcher`
* Flags that took comma-separated lists now have to be passed multiple times for each argument.
* Hosts passed to `exec host` now checked against default SSH config files
### Fixed
* Parsing of remote URLs when determining list config file path
* Incorrect error notification template value

3
.changes/v0.7.1.md Normal file
View File

@ -0,0 +1,3 @@
## v0.7.1 - 2025-02-14
### Fixed
* Incorrect local config file loading logic caused files to not be detected

3
.changes/v0.7.2.md Normal file
View File

@ -0,0 +1,3 @@
## v0.7.2 - 2025-02-14
### Fixed
* CI configs

3
.changes/v0.7.3.md Normal file
View File

@ -0,0 +1,3 @@
## v0.7.3 - 2025-02-14
### Changed
* GoReleaser configs

5
.changes/v0.7.4.md Normal file
View File

@ -0,0 +1,5 @@
## v0.7.4 - 2025-02-14
### Changed
* CI configs
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected

5
.changes/v0.7.5.md Normal file
View File

@ -0,0 +1,5 @@
## v0.7.5 - 2025-02-14
### Changed
* CI configs
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected

4
.changes/v0.7.6.md Normal file
View File

@ -0,0 +1,4 @@
## v0.7.6 - 2025-02-14
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
* CI configs

4
.changes/v0.7.7.md Normal file
View File

@ -0,0 +1,4 @@
## v0.7.7 - 2025-02-14
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
* CI configs

4
.changes/v0.7.8.md Normal file
View File

@ -0,0 +1,4 @@
## v0.7.8 - 2025-02-14
### Fixed
* Github CI config
* v0.7.1: Incorrect local config file loading logic caused files to not be detected

6
.changes/v0.8.0.md Normal file
View File

@ -0,0 +1,6 @@
## v0.8.0 - 2025-02-15
### Changed
* Breaking: `cmd-lists` key changed to `cmdLists`
* Properly load list config
* Config file loading properly errors
* CI Configs

View File

@ -15,26 +15,26 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: '1.20'
go-version: '1.23'
cache: true
# More assembly might be required: Docker logins, GPG, etc. It all depends
# on your needs.
- name: Get tag
id: tag
uses: dawidd6/action-get-tag@v1
- uses: olegtarasov/get-tag@v2.1.4
id: tagName
with:
# Optionally strip `v` prefix
strip_v: false
- uses: goreleaser/goreleaser-action@v4
# tagRegex: "foobar-(.*)" # Optional. Returns specified group text as tag name. Full tag string is returned if regex is not defined.
tagRegexGroup: 1 # Optional. Default is 1.
- uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --release-notes=".changes/${{steps.tag.outputs.tag}}.md" -f .goreleaser/github.yml --clean
version: 2.7.0
args: release --release-notes=".changes/${{ env.GIT_TAG_NAME }}.md" -f .goreleaser/github.yml --clean
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_TOKEN }}
GIT_TAG_NAME: ${{ steps.tagName.outputs.tag }}

7
.gitignore vendored
View File

@ -3,3 +3,10 @@ dist/
.codegpt
*.log
*.sh
*.yaml
*.yml
+.changie.yaml
+.changes/

View File

@ -6,7 +6,6 @@ before:
builds:
- env:
- CGO_ENABLED=0
- GOPROXY=https://goproxy.io
goos:
- freebsd
- linux
@ -16,7 +15,7 @@ builds:
- arm64
archives:
- format: tar.gz
- formats: tar.gz
# this name template makes the OS and Arch compatible with the results of uname.
name_template: >-
{{ .ProjectName }}_{{ .Version }}_
@ -28,7 +27,7 @@ archives:
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
formats: [zip]
checksum:
name_template: 'checksums.txt'
snapshot:

View File

@ -1,4 +1,3 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
version: 2
before:
@ -17,7 +16,7 @@ builds:
- arm64
archives:
- format: tar.gz
- formats: tar.gz
# this name template makes the OS and Arch compatible with the results of uname.
name_template: >-
{{ .ProjectName }}_{{ .Version }}_
@ -29,7 +28,7 @@ archives:
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
formats: [zip]
checksum:
name_template: 'checksums.txt'
snapshot:

View File

@ -1,11 +1,20 @@
name: goreleaser release
steps:
release:
image: goreleaser/goreleaser
golang:
image: golang:1.23
commands:
- go mod tidy
- go install github.com/goreleaser/goreleaser/v2@v2.7.0
- goreleaser release -f .goreleaser/gitea.yml --release-notes=".changes/$(go run backy.go version -V).md"
secrets: [ gitea_token ]
environment:
GITEA_TOKEN:
from_secret: gitea_token
when:
event: tag
# release:
# image: goreleaser/goreleaser
# commands:
when:
- event: tag

View File

@ -23,7 +23,14 @@ steps:
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' | DISPLAY=":0.0" SSH_ASKPASS=~/.ssh/.print_ssh_password setsid ssh-add -
- rsync -atv --delete --progress public/ backy@backy.cybershell.xyz:docs
- rsync -atv --delete --progress vangen/ backy@backy.cybershell.xyz:vangen-go
secrets: [ ssh_host_key, ssh_deploy_key, ssh_passphrase ]
environment:
SSH_HOST_KEY:
from_secret: ssh_host_key
SSH_DEPLOY_KEY:
from_secret: ssh_deploy_key
SSH_PASSPHRASE:
from_secret: ssh_passphrase
when:
- branch: master
- path: 'docs/**'

View File

@ -6,10 +6,72 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v0.8.0 - 2025-02-15
### Changed
* Breaking: `cmd-lists` key changed to `cmdLists`
* Properly load list config
* Config file loading properly errors
* CI Configs
## v0.7.8 - 2025-02-14
### Fixed
* Github CI config
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
## v0.7.7 - 2025-02-14
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
* CI configs
## v0.7.6 - 2025-02-14
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
* CI configs
## v0.7.5 - 2025-02-14
### Changed
* CI configs
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
## v0.7.4 - 2025-02-14
### Changed
* CI configs
### Fixed
* v0.7.1: Incorrect local config file loading logic caused files to not be detected
## v0.7.3 - 2025-02-14
### Changed
* GoReleaser configs
## v0.7.2 - 2025-02-14
### Fixed
* CI configs
## v0.7.1 - 2025-02-14
### Fixed
* Incorrect local config file loading logic caused files to not be detected
## v0.7.0 - 2025-02-11
### Added
* [feat]: package `packageOperation` option `checkVersion` implemented
* user management added - see docs
* Support for remote config sources. Only config file and list can be used for now.
* Cache functionality - still a WIP
* Flag `--s3-endpoint` for config file fetching from S3
### Changed
* Internal refactoring of config setup
* Formatting and sending for notifications
* name of `configfetcher` to `remotefetcher`
* Flags that took comma-separated lists now have to be passed multiple times for each argument.
* Hosts passed to `exec host` now checked against default SSH config files
### Fixed
* Parsing of remote URLs when determining list config file path
* Incorrect error notification template value
## v0.6.1 - 2025-01-04
### Fixed
* When running a list, hooks now run explicitly after the command executes. Fixed panic due to improper logic.
## v0.6.0 - 2025-01-04
### Added
* Command Type Package - allows one to perform package operations [docs](https://backy.cybershell.xyz/config/packages/)

View File

@ -8,13 +8,14 @@
"settings": {
"cSpell.words": [
"Cmds",
"remotefetcher",
"CMDSTDOUT",
"knadh",
"koanf",
"mattn",
"maunium",
"mautrix",
"nikoksr",
"remotefetcher",
"Strs"
]
}

View File

@ -12,8 +12,8 @@ import (
var (
backupCmd = &cobra.Command{
Use: "backup [--lists=list1,list2,... | -l list1, list2,...]",
Short: "Runs commands defined in config file.",
Use: "backup [--lists=list1 --lists list2 ... | -l list1 -l list2 ...]",
Short: "Runs commands defined in config file. Use -l flag multiple times to run multiple lists.",
Long: "Backup executes commands defined in config file.\nUse the --lists or -l flag to execute the specified lists. If not flag is not given, all lists will be executed.",
Run: Backup,
}
@ -23,13 +23,14 @@ var (
var cmdLists []string
func init() {
parseS3Config()
backupCmd.Flags().StringSliceVarP(&cmdLists, "lists", "l", nil, "Accepts comma-separated names of command lists to execute.")
backupCmd.Flags().StringArrayVarP(&cmdLists, "lists", "l", nil, "Accepts comma-separated names of command lists to execute.")
}
func Backup(cmd *cobra.Command, args []string) {
backyConfOpts := backy.NewOpts(cfgFile, backy.AddCommandLists(cmdLists))
backyConfOpts := backy.NewOpts(cfgFile, backy.AddCommandLists(cmdLists), backy.SetLogFile(logFile), backy.SetCmdStdOut(cmdStdOut))
backyConfOpts.InitConfig()
backyConfOpts.ReadConfig()

View File

@ -16,8 +16,9 @@ var (
)
func cron(cmd *cobra.Command, args []string) {
parseS3Config()
opts := backy.NewOpts(cfgFile, backy.CronEnabled())
opts := backy.NewOpts(cfgFile, backy.EnableCron(), backy.SetLogFile(logFile), backy.SetCmdStdOut(cmdStdOut))
opts.InitConfig()
opts.ReadConfig()

View File

@ -23,17 +23,16 @@ var (
func init() {
execCmd.AddCommand(hostExecCommand)
hostExecCommand.Flags().StringSliceVarP(&hostsList, "hosts", "m", nil, "Accepts comma-separated names of hosts.")
hostExecCommand.Flags().StringSliceVarP(&cmdList, "commands", "c", nil, "Accepts comma-separated names of commands.")
}
func execute(cmd *cobra.Command, args []string) {
parseS3Config()
if len(args) < 1 {
logging.ExitWithMSG("Please provide a command to run. Pass --help to see options.", 1, nil)
}
opts := backy.NewOpts(cfgFile, backy.AddCommands(args), backy.SetLogFile(logFile))
opts := backy.NewOpts(cfgFile, backy.AddCommands(args), backy.SetLogFile(logFile), backy.SetCmdStdOut(cmdStdOut))
opts.InitConfig()
opts.ReadConfig()
opts.ExecuteCmds()

View File

@ -8,19 +8,25 @@ import (
var (
hostExecCommand = &cobra.Command{
Use: "host [--commands=command1,command2, ... | -c command1,command2, ...] [--hosts=host1,hosts2, ... | -m host1,host2, ...] ",
Use: "host [--command=command1 --command=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] ",
Short: "Runs command defined in config file on the hosts in order specified.",
Long: "Host executes specified commands on the hosts defined in config file.\nUse the --commands or -c flag to choose the commands.",
Run: Host,
}
)
// Holds command list to run
// Holds list of hosts to run commands on
var hostsList []string
// Holds command list to run
var cmdList []string
func init() {
hostExecCommand.Flags().StringArrayVarP(&hostsList, "hosts", "m", nil, "Accepts space-separated names of hosts. Specify multiple times for multiple hosts.")
hostExecCommand.Flags().StringArrayVarP(&cmdList, "command", "c", nil, "Accepts space-separated names of commands. Specify multiple times for multiple commands.")
parseS3Config()
}
// cli input should be hosts and commands. Hosts are defined in config files.
@ -29,7 +35,7 @@ func init() {
// 2. stdin (on command line) (TODO)
func Host(cmd *cobra.Command, args []string) {
backyConfOpts := backy.NewOpts(cfgFile, backy.SetLogFile(logFile))
backyConfOpts := backy.NewOpts(cfgFile, backy.SetLogFile(logFile), backy.SetCmdStdOut(cmdStdOut))
backyConfOpts.InitConfig()
backyConfOpts.ReadConfig()
@ -38,13 +44,19 @@ func Host(cmd *cobra.Command, args []string) {
if hostsList == nil {
logging.ExitWithMSG("error: hosts must be specified", 1, &backyConfOpts.Logger)
}
// host is only checked when we read the SSH File
// so a check may not be needed here
for _, h := range hostsList {
// check if h exists in the config file
_, hostFound := backyConfOpts.Hosts[h]
if !hostFound {
// check if h exists in the SSH config file
hostFoundInConfig, s := backy.CheckIfHostHasHostName(h)
if !hostFoundInConfig {
logging.ExitWithMSG("host "+h+" not found", 1, &backyConfOpts.Logger)
}
// create host with hostname and host
backyConfOpts.Hosts[h] = &backy.Host{Host: h, HostName: s}
}
}
if cmdList == nil {
logging.ExitWithMSG("error: commands must be specified", 1, &backyConfOpts.Logger)

View File

@ -6,16 +6,29 @@ package cmd
import (
"git.andrewnw.xyz/CyberShell/backy/pkg/backy"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/spf13/cobra"
)
var (
listCmd = &cobra.Command{
Use: "list [--list=list1,list2,... | -l list1, list2,...] [ -cmd cmd1 cmd2 cmd3...]",
Use: "list [command]",
Short: "Lists commands, lists, or hosts defined in config file.",
Long: "Backup lists commands or groups defined in config file.\nUse the --lists or -l flag to list the specified lists. If not flag is not given, all lists will be executed.",
Run: List,
}
listCmds = &cobra.Command{
Use: "cmds [cmd1 cmd2 cmd3...]",
Short: "Lists commands, lists, or hosts defined in config file.",
Long: "Backup lists commands or groups defined in config file.\nUse the --lists or -l flag to list the specified lists. If not flag is not given, all lists will be executed.",
Run: ListCmds,
}
listCmdLists = &cobra.Command{
Use: "lists [list1 list2 ...]",
Short: "Lists commands, lists, or hosts defined in config file.",
Long: "Backup lists commands or groups defined in config file.\nUse the --lists or -l flag to list the specified lists. If not flag is not given, all lists will be executed.",
Run: ListCmdLists,
}
)
@ -23,26 +36,51 @@ var listsToList []string
var cmdsToList []string
func init() {
listCmd.Flags().StringSliceVarP(&listsToList, "lists", "l", nil, "Accepts comma-separated names of command lists to list.")
listCmd.Flags().StringSliceVarP(&cmdsToList, "cmds", "c", nil, "Accepts comma-separated names of commands to list.")
listCmd.AddCommand(listCmds, listCmdLists)
}
func List(cmd *cobra.Command, args []string) {
func ListCmds(cmd *cobra.Command, args []string) {
// setup based on whats passed in:
// - cmds
// - lists
// - if none, list all commands
if cmdLists != nil {
if len(args) > 0 {
cmdsToList = args
} else {
logging.ExitWithMSG("Error: list cmds subcommand needs commands to list", 1, nil)
}
opts := backy.NewOpts(cfgFile)
parseS3Config()
opts := backy.NewOpts(cfgFile, backy.SetLogFile(logFile))
opts.InitConfig()
opts.ReadConfig()
opts.ListCommand("rm-sn-db")
for _, v := range cmdsToList {
opts.ListCommand(v)
}
}
func ListCmdLists(cmd *cobra.Command, args []string) {
parseS3Config()
if len(args) > 0 {
listsToList = args
} else {
logging.ExitWithMSG("Error: lists subcommand needs lists", 1, nil)
}
opts := backy.NewOpts(cfgFile, backy.SetLogFile(logFile))
opts.InitConfig()
opts.ReadConfig()
for _, v := range listsToList {
opts.ListCommandList(v)
}
}

View File

@ -15,7 +15,9 @@ var (
// Used for flags.
cfgFile string
verbose bool
cmdStdOut bool
logFile string
s3Endpoint string
rootCmd = &cobra.Command{
Use: "backy",
@ -33,11 +35,17 @@ func Execute() {
}
func init() {
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", "", "log file to write to")
rootCmd.PersistentFlags().BoolVar(&cmdStdOut, "cmdStdOut", false, "Pass to print command output to stdout")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "f", "", "config file to read from")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Sets verbose level")
rootCmd.PersistentFlags().StringVar(&s3Endpoint, "s3-endpoint", "", "Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.")
rootCmd.AddCommand(backupCmd, execCmd, cronCmd, versionCmd, listCmd)
}
func parseS3Config() {
if s3Endpoint != "" {
os.Setenv("S3_ENDPOINT", s3Endpoint)
}
}

View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
)
const versionStr = "0.6.1"
const versionStr = "0.8.0"
var (
versionCmd = &cobra.Command{

View File

@ -14,7 +14,7 @@ Usage:
backy [command]
Available Commands:
backup Runs commands defined in config file.
backup Runs commands defined in config file. Use -l flag multiple times to run multiple lists.
completion Generate the autocompletion script for the specified shell
cron Starts a scheduler that runs lists defined in config file.
exec Runs commands defined in config file in order given.
@ -25,6 +25,8 @@ Available Commands:
Flags:
-f, --config string config file to read from
-h, --help help for backy
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
Use "backy [command] --help" for more information about a command.
@ -39,14 +41,16 @@ Backup executes commands defined in config file.
Use the --lists or -l flag to execute the specified lists. If not flag is not given, all lists will be executed.
Usage:
backy backup [--lists=list1,list2,... | -l list1, list2,...] [flags]
backy backup [--lists=list1 --lists list2 ... | -l list1 -l list2 ...] [flags]
Flags:
-h, --help help for backup
-l, --lists strings Accepts comma-separated names of command lists to execute.
-l, --lists stringArray Accepts comma-separated names of command lists to execute.
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
```
@ -63,6 +67,8 @@ Flags:
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
```
@ -83,6 +89,8 @@ Flags:
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
Use "backy exec [command] --help" for more information about a command.
@ -95,15 +103,17 @@ Host executes specified commands on the hosts defined in config file.
Use the --commands or -c flag to choose the commands.
Usage:
backy exec host [--commands=command1,command2, ... | -c command1,command2, ...] [--hosts=host1,hosts2, ... | -m host1,host2, ...] [flags]
backy exec host [--command=command1 --command=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] [flags]
Flags:
-c, --commands strings Accepts comma-separated names of commands.
-c, --command stringArray Accepts space-separated names of commands. Specify multiple times for multiple commands.
-h, --help help for host
-m, --hosts strings Accepts comma-separated names of hosts.
-m, --hosts stringArray Accepts space-separated names of hosts. Specify multiple times for multiple hosts.
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
```
@ -122,6 +132,8 @@ Flags:
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
```
@ -141,5 +153,7 @@ Flags:
Global Flags:
-f, --config string config file to read from
--log-file string log file to write to
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
-v, --verbose Sets verbose level
```

View File

@ -10,6 +10,13 @@ This is the section on the config file.
To use a specific file:
```backy [command] -f /path/to/file```
You can also use a remote file:
```
backy [command] -f `s3/http source`
```
See remote resources docs for specific info.
If you leave the config path blank, the following paths will be searched in order:
1. `./backy.yml`

View File

@ -2,7 +2,7 @@
title: "Command Lists"
weight: 2
description: >
This page tells you how to get use command lists.
This page tells you how to use command lists.
---
Command lists are for executing commands in sequence and getting notifications from them.
@ -11,8 +11,16 @@ The top-level object key can be anything you want but not the same as another.
Lists can go in a separate file. Command lists should be in a separate file if:
1. key 'cmd-lists.file' is found
2. hosts.yml or hosts.yaml is found in the same directory as the backy config file
1. key 'cmdLists.file' is specified
2. lists.yml or lists.yaml is found in the same directory as the backy config file (this includes remote config files as of v0.7.0)
{{% notice info %}}
The lists file is also checked in remote resources.
The lists file is ignored under the following condition:
If a remote config file is specified (on the command-line using `-f`) and the lists file is not found in the same directory, the lists file is assumed to not exist.
{{% /notice %}}
```yaml {lineNos="true" wrap="true" title="yaml"}
test2:
@ -62,14 +70,14 @@ Name is optional. If name is not defined, name will be the object's map key.
Backy also has a cron mode, so one can run `backy cron` and start a process that schedules jobs to run at times defined in the configuration file.
Adding `cron: 0 0 1 * * *` to a `cmd-lists` object will schedule the list at 1 in the morning. See [https://crontab.guru/](https://crontab.guru/) for reference.
Adding `cron: 0 0 1 * * *` to a `cmdLists` object will schedule the list at 1 in the morning. See [https://crontab.guru/](https://crontab.guru/) for reference.
{{% notice tip %}}
Note: Backy uses the second field of cron, so add anything except `*` to the beginning of a regular cron expression.
{{% /notice %}}
```yaml {lineNos="true" wrap="true" title="yaml"}
cmd-lists:
cmdLists:
  docker-container-backup: # this can be any name you want
    # all commands have to be defined
    order:

View File

@ -4,9 +4,7 @@ description: Commands are just that, commands
weight: 1
---
The yaml top-level map can be any string.
The top-level name must be unique.
### Example Config
@ -43,8 +41,8 @@ Values available for this section **(case-sensitive)**:
| --- | --- | --- | --- |
| `cmd` | Defines the command to execute | `string` | yes |
| `Args` | Defines the arguments to the command | `[]string` | no |
| `environment` | Defines evironment variables for the command | `[]string` | no |
| `type` | May be `scriptFile`, `script`, or `package`. Runs script from local machine on remote. `Package` is the only one that can be run on local and remote hosts. | `string` | no |
| `environment` | Defines environment variables for the command | `[]string` | no |
| `type` | See documentation further down the page. Additional fields may be required. | `string` | no |
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no |
| `host` | If not specified, the command will execute locally. | `string` | no |
| `scriptEnvFile` | When type is `scriptFile` or `script`, this file is prepended to the input. | `string` | no |
@ -108,13 +106,14 @@ This is useful for specifying environment variables or other things so they don'
### type
May be `scriptFile` or `script`. Runs script from local machine on remote host passed to the SSH session as standard input.
The following options are available:
If `type` is `script`, `cmd` is used as the script.
If `type` is `scriptFile`, cmd must be a script file.
If `type` is `package`, there are additional fields that must be specified.
| name | description |
| --- | --- |
| script | `cmd` is used as the script |
| scriptFile | Can only be run on a host. `cmd` is read and used as the script, and `scriptEnvFile` can be used to add env variables |
| package | Run package operations. See [dedicated page](/config/packages) for configuring package commands |
| user | Run user operations. See [dedicated page](/config/user-commands) for configuring package commands |
### environment
@ -122,7 +121,7 @@ The environment variables support expansion:
- using escaped values `$VAR` or `${VAR}`
For now, the variables have to be defined in an `.env` file in the same directory as the config file.
For now, the variables have to be defined in an `.env` file in the same directory that the program is run from.
If using it with host specified, the SSH server has to be configured to accept those env variables.

View File

@ -10,8 +10,8 @@ This is dedicated to `package` commands. The command `type` field must be `packa
| --- | --- | --- | --- |
| `packageName` | The name of a package to be modified. | `string` | yes |
| `packageManager` | The name of the package manger to be used. | `string` | yes |
| `packageOperation` | The type of operation to be perform. | `string` | yes |
| `packageVersion` | The version of a package to be modified. | `string` | no |
| `packageOperation` | The type of operation to perform. | `string` | yes |
| `packageVersion` | The version of a package. | `string` | no |
#### example
@ -35,6 +35,7 @@ The following package operations are supported:
- `install`
- `remove`
- `upgrade`
- `checkVersion`
#### packageManager
@ -46,11 +47,11 @@ The following package managers are recognized:
#### package command args
You can add additional arguments using the standard `Args` key. This is useful for adding more packages.
You can add additional arguments using the standard `Args` key. This is useful for adding more packages, yet it does not work with `checkVersion`.
### Development
The PackageManager interface provides an easy to enforce functions and options. There are two interfaces, `PackageManager` and `ConfigurablePackageManager` in the directory `pkg/pkgman`. Go's import-cycle "feature" caused me to implement functional options using a third interface. `PackageManagerOption`is a function that takes an interface.
The PackageManager interface provides an easy way to enforce functions and options. There are two interfaces, `PackageManager` and `ConfigurablePackageManager` in the directory `pkg/pkgman`. Go's import-cycle "feature" caused me to implement functional options using a third interface. `PackageManagerOption`is a function that takes an interface.
#### PackageManager

View File

@ -0,0 +1,62 @@
---
title: "User commands"
weight: 2
description: This is dedicated to user commands.
---
This is dedicated to `user` commands. The command `type` field must be `user`. User is a type that allows one to perform user operations. There are several additional options available when `type` is `user`:
| name | notes | type | required |
| --- | --- | --- | --- |
| `userName` | The name of a user to be configured. | `string` | yes |
| `userOperation` | The type of operation to perform. | `string` | yes |
| `userID` | The user ID to use. | `string` | yes |
| `userGroups` | The groups the user should be added to. | `[]string` | yes |
| `userShell` | The shell for the user. | `string` | yes |
| `userHome` | The user's home directory. | `string` | no |
#### example
The following is an example of a package command:
```yaml
addUser:
name: add user backy with custom home dir
type: user
userName: backy
userHome: /opt/backy
userOperation: add
host: some-host
```
#### userOperation
The following package operations are supported:
- `add`
- `remove`
- `modify`
- `password`
- `checkIfExists`
### Development
The UserManager interface provides an way easy to add new commands. There is one interface `Usermanager` in directory `pkg/usermanager`.
#### UserManager
```go
// UserManager defines the interface for user management operations.
// All functions but one return a string for the command and any args.
type UserManager interface {
AddUser(username, homeDir, shell string, isSystem bool, groups, args []string) (string, []string)
RemoveUser(username string) (string, []string)
ModifyUser(username, homeDir, shell string, groups []string) (string, []string)
// Modify password uses chpasswd for Linux systems to build the command to change the password
// Should return a password as the last argument
// TODO: refactor when adding more systems instead of Linux
ModifyPassword(username, password string) (string, *strings.Reader, string)
UserExists(username string) (string, []string)
}
```

View File

@ -0,0 +1,24 @@
---
title: "Hosts"
weight: 2
description: >
This page tells you how to use hosts.
---
| Key | Description | Type | Required |
|----------------------|---------------------------------------------------------------|----------|----------|
| `OS` | Operating system of the host (used for package commands) | `string` | no |
| `config` | Path to the SSH config file | `string` | no |
| `host` | Specifies the `Host` ssh_config(5) directive | `string` | yes |
| `hostname` | Hostname of the host | `string` | no |
| `knownhostsfile` | Path to the known hosts file | `string` | no |
| `port` | Port number to connect to | `uint16` | no |
| `proxyjump` | Proxy jump hosts, comma-separated | `string` | no |
| `password` | Password for SSH authentication | `string` | no |
| `privatekeypath` | Path to the private key file | `string` | no |
| `privatekeypassword` | Password for the private key file | `string` | no |
| `user` | Username for SSH authentication | `string` | no |
## exec host subcommand
Backy has a subcommand `exec host`. This subcommand takes the flags of `-m host1 -m host2`. For now these hosts need to be defined in the config file.

View File

@ -5,6 +5,7 @@ description: >
This page tells you how to get set up Backy notifications.
---
Notifications are only configurable for command lists, as of right now.
Notifications can be sent on command list completion and failure.

View File

@ -0,0 +1,17 @@
---
title: "Remote resources"
weight: 2
description: This is dedicated to configuring remote resources.
---
Remote resources can be used for a lot of things, including config files and scripts.
## Config file
For the main config file to be fetched remotely, pass the URL using `-f [url]`.
If using S3, you should use the s3 protocol URI: `s3://bucketName/key/path`. You will also need to set the env variable `S3_ENDPOINT` to the appropriate value. The flag `--s3-endpoint` can be used to override this value or to set this value, if not already set.
## Scripts
Scripts will be coming later.

View File

@ -48,7 +48,7 @@ commands:
To execute groups of commands in sequence, use a list configuration.
```yaml
cmd-lists:
cmdLists:
cmds-to-run: # this can be any name you want
# all commands have to be defined in the commands section
order:
@ -97,7 +97,7 @@ hosts:
The notifications object can have two forms.
For more, [see the notification object documentation](/config/notifications). The top-level map key is id that has to be referenced by the `cmd-lists` key `notifications`.
For more, [see the notification object documentation](/config/notifications). The top-level map key is id that has to be referenced by the `cmdLists` key `notifications`.
```yaml
notifications:

101
go.mod
View File

@ -1,92 +1,91 @@
module git.andrewnw.xyz/CyberShell/backy
go 1.21
go 1.23
toolchain go1.22.2
toolchain go1.23.6
replace git.andrewnw.xyz/CyberShell/backy => /home/andrew/Projects/backy
require (
github.com/aws/aws-sdk-go-v2/config v1.18.27
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0
github.com/go-co-op/gocron v1.33.1
github.com/hashicorp/vault/api v1.10.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.76.0
github.com/go-co-op/gocron v1.37.0
github.com/hashicorp/vault/api v1.15.0
github.com/joho/godotenv v1.5.1
github.com/kevinburke/ssh_config v1.2.0
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/rawbytes v0.1.0
github.com/knadh/koanf/v2 v2.0.1
github.com/mattn/go-isatty v0.0.19
github.com/nikoksr/notify v0.41.0
github.com/knadh/koanf/v2 v2.1.2
github.com/mattn/go-isatty v0.0.20
github.com/minio/minio-go/v7 v7.0.84
github.com/mitchellh/go-homedir v1.1.0
github.com/nikoksr/notify v1.3.0
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.30.0
github.com/rs/zerolog v1.33.0
github.com/sethvargo/go-password v0.3.1
github.com/spf13/cobra v1.7.0
golang.org/x/crypto v0.31.0
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.16.0
mvdan.cc/sh/v3 v3.7.0
maunium.net/go/mautrix v0.23.0
mvdan.cc/sh/v3 v3.10.0
)
require (
github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tidwall/gjson v1.16.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
go.mau.fi/util v0.0.0-20230906155759-14bad39a8718 // indirect
go.mau.fi/util v0.8.4 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.3.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
maunium.net/go/maulogger/v2 v2.4.1 // indirect
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.10.0 // indirect
)

248
go.sum
View File

@ -1,118 +1,103 @@
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA=
github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw=
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk=
github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 h1:zAxi9p3wsZMIaVCdoiQp2uZ9k1LsZvmAnoTBeZPXom0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8/go.mod h1:3XkePX5dSaxveLAYY7nsbsZZrKxCyEuE5pM4ziFxyGg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 h1:OIHj/nAhVzIXGzbAE+4XmZ8FPvro3THr6NlqErJc3wY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32/go.mod h1:LiBEsDo34OJXqdDlRGsilhlIiXR7DL+6Cx2f4p1EgzI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.6 h1:cCBJaT7EeEojpJ4s7wTDbhZlHVJOgNHN7iw6qVurGaw=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.6/go.mod h1:WYH1ABybY7JK9TITPnk6ZlP7gQB8psI4c9qDmMsnLSA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 h1:OBsrtam3rk8NfBEq7OLOMm5HtQ9Yyw32X4UQMya/wjw=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13/go.mod h1:3U4gFA5pmoCOja7aq4nSaIAGbaOHv2Yl2ug018cmC+Q=
github.com/aws/aws-sdk-go-v2/service/s3 v1.76.0 h1:ehvUZNVrGA1Usa6yYo8A8pUqrigRelWXSbcCqYpRLeI=
github.com/aws/aws-sdk-go-v2/service/s3 v1.76.0/go.mod h1:KuLNrwYJFaC2AVZ+CVVc12k9NyqwgWsoNNHjwqF6QNk=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-co-op/gocron v1.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c=
github.com/go-co-op/gocron v1.33.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=
github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=
github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -122,70 +107,67 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E=
github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ=
github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE=
github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs=
github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@ -193,39 +175,30 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
go.mau.fi/util v0.0.0-20230906155759-14bad39a8718 h1:hmm5bZqE0M8+Uvys0HJPCSbAIZIwYtTkBKYPjAWHuMM=
go.mau.fi/util v0.0.0-20230906155759-14bad39a8718/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ=
go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@ -233,15 +206,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.16.0 h1:iUqCzJE2yqBC1ddAK6eAn159My8rLb4X8g4SFtQh2Dk=
maunium.net/go/mautrix v0.16.0/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4=
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
maunium.net/go/mautrix v0.23.0 h1:HNlR19eew5lvrNSL2muhExaGhYdaGk5FfEiA82QqUP4=
maunium.net/go/mautrix v0.23.0/go.mod h1:AGnnaz3ylGikUo1I1MJVn9QLsl2No1/ZNnGDyO0QD5s=
mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4=
mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY=

View File

@ -34,15 +34,17 @@ var Sprintf = fmt.Sprintf
func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([]string, error) {
var (
outputArr []string
ArgsStr string
ArgsStr string // concatenating the arguments
cmdOutBuf bytes.Buffer
cmdOutWriters io.Writer
errSSH error
envVars = environmentVars{
file: command.Env,
env: command.Environment,
}
outputArr []string // holds the output strings returned by processes
)
// Get the command type
@ -53,13 +55,13 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
ArgsStr += fmt.Sprintf(" %s", v)
}
// print the user's password if it is updated
if command.Type == "user" {
if command.UserOperation == "password" {
cmdCtxLogger.Info().Str("password", command.UserPassword).Msg("user password to be updated")
}
}
var errSSH error
// is host defined
if command.Host != nil {
outputArr, errSSH = command.RunCmdSSH(cmdCtxLogger, opts)
@ -236,7 +238,8 @@ func notifyError(logger zerolog.Logger, templates *msgTemplates, list *CmdList,
"CmdsRan": cmdsRan,
"CmdOutput": outStructArr,
"Err": err,
"Command": cmd.Name,
"CmdName": cmd.Name,
"Command": cmd.Cmd,
"Args": cmd.Args,
}
var errMsg bytes.Buffer

View File

@ -48,26 +48,29 @@ func (opts *ConfigOpts) InitConfig() {
backyKoanf := koanf.New(".")
opts.ConfigFilePath = strings.TrimSpace(opts.ConfigFilePath)
metadataFile := "hashMetadataSample.yml"
// metadataFile := "hashMetadataSample.yml"
cacheDir := homeCacheDir
// Load metadata from file
opts.CachedData, err = remotefetcher.LoadMetadataFromFile(metadataFile)
opts.CachedData, err = remotefetcher.LoadMetadataFromFile(path.Join(backyHomeConfDir, "cache.yml"))
if err != nil {
fmt.Println("Error loading metadata:", err)
panic(err)
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
}
// Initialize cache with loaded metadata
cache, err := remotefetcher.NewCache(metadataFile, cacheDir)
cache, err := remotefetcher.NewCache(path.Join(backyHomeConfDir, "cache.yml"), cacheDir)
if err != nil {
fmt.Println("Error initializing cache:", err)
panic(err)
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
}
// Populate cache with loaded metadata
for _, data := range opts.CachedData {
cache.AddDataToStore(data.Hash, *data)
if err := cache.AddDataToStore(data.Hash, *data); err != nil {
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
}
}
opts.Cache, err = remotefetcher.NewCache(path.Join(backyHomeConfDir, "cache.yml"), backyHomeConfDir)
@ -75,24 +78,22 @@ func (opts *ConfigOpts) InitConfig() {
logging.ExitWithMSG(fmt.Sprintf("error initializing cache: %v", err), 1, nil)
}
// Initialize the fetcher
println("Creating new fetcher for source", opts.ConfigFilePath)
fetcher, err := remotefetcher.NewConfigFetcher(opts.ConfigFilePath, opts.Cache)
println("Created new fetcher for source", opts.ConfigFilePath)
// println("Creating new fetcher for source", opts.ConfigFilePath)
fetcher, err := remotefetcher.NewRemoteFetcher(opts.ConfigFilePath, opts.Cache)
// println("Created new fetcher for source", opts.ConfigFilePath)
if err != nil {
logging.ExitWithMSG(fmt.Sprintf("error initializing config fetcher: %v", err), 1, nil)
}
if opts.ConfigFilePath != "" {
loadConfigFile(fetcher, opts.ConfigFilePath, backyKoanf, opts)
} else {
loadDefaultConfigFiles(fetcher, configFiles, backyKoanf, opts)
}
opts.koanf = backyKoanf
}
func loadConfigFile(fetcher remotefetcher.ConfigFetcher, filePath string, k *koanf.Koanf, opts *ConfigOpts) {
func loadConfigFile(fetcher remotefetcher.RemoteFetcher, filePath string, k *koanf.Koanf, opts *ConfigOpts) {
data, err := fetcher.Fetch(filePath)
if err != nil {
logging.ExitWithMSG(fmt.Sprintf("Could not fetch config file %s: %v", filePath, err), 1, nil)
@ -103,25 +104,27 @@ func loadConfigFile(fetcher remotefetcher.ConfigFetcher, filePath string, k *koa
}
}
func loadDefaultConfigFiles(fetcher remotefetcher.ConfigFetcher, configFiles []string, k *koanf.Koanf, opts *ConfigOpts) {
func loadDefaultConfigFiles(fetcher remotefetcher.RemoteFetcher, configFiles []string, k *koanf.Koanf, opts *ConfigOpts) {
cFileFailures := 0
for _, c := range configFiles {
opts.ConfigFilePath = c
data, err := fetcher.Fetch(c)
if err != nil {
cFileFailures++
continue
}
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
cFileFailures++
continue
}
if data != nil {
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err == nil {
break
} else {
logging.ExitWithMSG(fmt.Sprintf("error loading config from file %s: %v", c, err), 1, &opts.Logger)
}
}
}
if cFileFailures == len(configFiles) {
logging.ExitWithMSG("Could not find any valid config file", 1, nil)
logging.ExitWithMSG("Could not find any valid local config file", 1, nil)
}
}
@ -133,7 +136,12 @@ func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
opts.loadEnv()
if backyKoanf.Bool(getNestedConfig("logging", "cmd-std-out")) {
os.Setenv("BACKY_STDOUT", "enabled")
os.Setenv("BACKY_CMDSTDOUT", "enabled")
}
// override the default value of cmd-std-out if flag is set
if opts.CmdStdOut {
os.Setenv("BACKY_CMDSTDOUT", "enabled")
}
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
@ -228,7 +236,7 @@ func setupLogger(opts *ConfigOpts) zerolog.Logger {
func unmarshalConfig(k *koanf.Koanf, key string, target interface{}, log zerolog.Logger) {
if err := k.UnmarshalWithConf(key, target, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling %s struct: %v", key, err), 1, &log)
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling key %s into struct: %v", key, err), 1, &log)
}
}
@ -272,10 +280,12 @@ func loadCommandLists(opts *ConfigOpts, backyKoanf *koanf.Koanf) {
// if config file is remote, use the directory of the remote file
if isRemoteURL(opts.ConfigFilePath) {
_, u = getRemoteDir(opts.ConfigFilePath)
// // Still use local list files if a remote config file is used, but use them last
listConfigFiles = []string{u.JoinPath("lists.yml").String(), u.JoinPath("lists.yaml").String()}
} else {
backyConfigFileDir = path.Dir(opts.ConfigFilePath)
listConfigFiles = []string{
// "./lists.yml", "./lists.yaml",
path.Join(backyConfigFileDir, "lists.yml"),
path.Join(backyConfigFileDir, "lists.yaml"),
}
@ -289,9 +299,9 @@ func loadCommandLists(opts *ConfigOpts, backyKoanf *koanf.Koanf) {
}
}
if backyKoanf.Exists("cmd-lists") {
unmarshalConfig(backyKoanf, "cmd-lists", &opts.CmdConfigLists, opts.Logger)
if backyKoanf.Exists("cmd-lists.file") {
if backyKoanf.Exists("cmdLists") {
unmarshalConfig(backyKoanf, "cmdLists", &opts.CmdConfigLists, opts.Logger)
if backyKoanf.Exists("cmdLists.file") {
loadCmdListsFile(backyKoanf, listsConfig, opts)
}
}
@ -313,10 +323,11 @@ func getRemoteDir(filePath string) (string, *url.URL) {
}
func loadListConfigFile(filePath string, k *koanf.Koanf, opts *ConfigOpts) bool {
fetcher, err := remotefetcher.NewConfigFetcher(filePath, opts.Cache, remotefetcher.IgnoreFileNotFound())
fetcher, err := remotefetcher.NewRemoteFetcher(filePath, opts.Cache, remotefetcher.IgnoreFileNotFound())
if err != nil {
// if file not found, ignore
if errors.Is(err, remotefetcher.ErrFileNotFound) {
if errors.Is(err, remotefetcher.ErrIgnoreFileNotFound) {
println("File not found", filePath)
return true
}
@ -332,17 +343,19 @@ func loadListConfigFile(filePath string, k *koanf.Koanf, opts *ConfigOpts) bool
return false
}
unmarshalConfig(k, "cmdLists", &opts.CmdConfigLists, opts.Logger)
keyNotSupported("cmd-lists", "cmdLists", k, opts, true)
opts.CmdListFile = filePath
return true
}
func loadCmdListsFile(backyKoanf *koanf.Koanf, listsConfig *koanf.Koanf, opts *ConfigOpts) {
opts.CmdListFile = strings.TrimSpace(backyKoanf.String("cmd-lists.file"))
opts.CmdListFile = strings.TrimSpace(backyKoanf.String("cmdLists.file"))
if !path.IsAbs(opts.CmdListFile) {
opts.CmdListFile = path.Join(path.Dir(opts.ConfigFilePath), opts.CmdListFile)
}
fetcher, err := remotefetcher.NewConfigFetcher(opts.CmdListFile, opts.Cache)
fetcher, err := remotefetcher.NewRemoteFetcher(opts.CmdListFile, opts.Cache)
if err != nil {
logging.ExitWithMSG(fmt.Sprintf("error initializing config fetcher: %v", err), 1, nil)
@ -357,14 +370,17 @@ func loadCmdListsFile(backyKoanf *koanf.Koanf, listsConfig *koanf.Koanf, opts *C
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
}
unmarshalConfig(listsConfig, "cmd-lists", &opts.CmdConfigLists, opts.Logger)
keyNotSupported("cmd-lists", "cmdLists", listsConfig, opts, true)
unmarshalConfig(listsConfig, "cmdLists", &opts.CmdConfigLists, opts.Logger)
opts.Logger.Info().Str("using lists config file", opts.CmdListFile).Send()
}
func validateCommandLists(opts *ConfigOpts) {
var cmdNotFoundSliceErr []error
for cmdListName, cmdList := range opts.CmdConfigLists {
// if cron is enabled and cron is not set, delete the list
if opts.cronEnabled && strings.TrimSpace(cmdList.Cron) == "" {
opts.Logger.Debug().Str("cron", "enabled").Str("list", cmdListName).Msg("cron not set, deleting list")
delete(opts.CmdConfigLists, cmdListName)
continue
}
@ -404,9 +420,9 @@ func getLoggingKeyFromConfig(key string) string {
return fmt.Sprintf("logging.%s", key)
}
func getCmdListFromConfig(list string) string {
return fmt.Sprintf("cmd-lists.%s", list)
}
// func getCmdListFromConfig(list string) string {
// return fmt.Sprintf("cmdLists.%s", list)
// }
func (opts *ConfigOpts) setupVault() error {
if !opts.koanf.Bool("vault.enabled") {
@ -549,6 +565,16 @@ func processCmds(opts *ConfigOpts) error {
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
cmd.RemoteHost = &Host{Host: *cmd.Host}
}
} else {
if cmd.Dir != nil {
cmdDir, err := resolveDir(*cmd.Dir)
if err != nil {
return err
}
cmd.Dir = &cmdDir
}
}
// Parse package commands
@ -603,23 +629,22 @@ func processCmds(opts *ConfigOpts) error {
}
}
if cmd.Type == "remoteScript" {
if !isRemoteURL(cmd.Cmd) {
return fmt.Errorf("remoteScript command %s must be a remote resource", cmdName)
}
}
}
return nil
}
// processHooks evaluates if hooks are valid Commands
//
// Takes the following arguments:
// The cmd.hookRefs[hookType] is created with any hooks found.
//
// 1. a []string of hooks
// 2. a map of Commands as arguments
// 3. a string hookType, must be the hook type
//
// The cmds.hookRef is modified in this function.
//
// Returns the following:
//
// An error, if any, if the command is not found
// Returns an error, if any, if the hook command is not found
func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType string) error {
// initialize hook type
@ -676,3 +701,14 @@ func detectOSType(cmd *Command, opts *ConfigOpts) error {
}
return nil
}
func keyNotSupported(oldKey, newKey string, koanf *koanf.Koanf, opts *ConfigOpts, deprecated bool) {
if koanf.Exists(oldKey) {
if deprecated {
opts.Logger.Warn().Str("key", oldKey).Msg("key is deprecated. Use " + newKey + " instead.")
} else {
opts.Logger.Fatal().Err(fmt.Errorf("key %s found; it has changed to %s", oldKey, newKey)).Send()
}
}
}

View File

@ -17,10 +17,7 @@ func (opts *ConfigOpts) Cron() {
s := gocron.NewScheduler(time.Local)
s.TagsUnique()
cmdLists := opts.CmdConfigLists
for listName, config := range cmdLists {
if config.Name == "" {
config.Name = listName
}
for _, config := range cmdLists {
cron := strings.TrimSpace(config.Cron)
if cron != "" {

View File

@ -23,8 +23,7 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
var cmdFound bool = false
var cmdInfo *Command
// check commands in file against cmd
for _, cmdInFile := range opts.executeCmds {
print(cmdInFile)
for cmdInFile := range opts.Cmds {
cmdFound = false
if cmd == cmdInFile {
@ -37,28 +36,34 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
// print the command's information
if cmdFound {
print("Command: ")
println("Command: ")
print(cmdInfo.Cmd)
if len(cmdInfo.Args) >= 0 {
for _, v := range cmdInfo.Args {
print(" ") // print space between command and args
print(v) // print command arg
}
}
// is is remote or local
// is it remote or local
if cmdInfo.Host != nil {
print("Host: ", cmdInfo.Host)
println()
print("Host: ", *cmdInfo.Host)
println()
} else {
println()
print("Host: Runs on Local Machine\n\n")
}
if cmdInfo.Dir != nil {
println()
print("Directory: ", *cmdInfo.Dir)
println()
}
} else {
fmt.Printf("Command %s not found. Check spelling.\n", cmd)
@ -66,3 +71,38 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
}
}
func (opts *ConfigOpts) ListCommandList(list string) {
// bool for commands not found
// gets set to false if a command is not found
// set to true if the command is found
var listFound bool = false
var listInfo *CmdList
// check commands in file against cmd
for listInFile, l := range opts.CmdConfigLists {
listFound = false
if list == listInFile {
listFound = true
listInfo = l
break
}
}
// print the command's information
if listFound {
println("List: ", list)
println()
for _, v := range listInfo.Order {
println()
opts.ListCommand(v)
}
} else {
fmt.Printf("List %s not found. Check spelling.\n", list)
}
}

View File

@ -757,3 +757,12 @@ func (h *Host) DetectOS(opts *ConfigOpts) (string, error) {
osName := string(output)
return osName, nil
}
func CheckIfHostHasHostName(host string) (bool, string) {
HostName, err := ssh_config.DefaultUserSettings.GetStrict(host, "HostName")
if err != nil {
return false, ""
}
println(HostName)
return HostName != "", HostName
}

View File

@ -1,12 +1,12 @@
Command list {{.listName }} failed.
The command run was {{.Cmd}}.
The command run was {{.CmdName}}.
The command executed was {{.Command}} {{ if .Args }} {{- range .Args}} {{.}} {{end}} {{end}}
{{ if .Err }} The error was {{ .Err }}{{ end }}
{{ if .Output }} The output was {{- range .Output}} {{.}} {{end}} {{end}}
{{ if .Output }} The output was: {{- range .Output}} {{.}} {{end}} {{end}}
{{ if .CmdsRan }}
The following commands ran:

View File

@ -67,10 +67,7 @@ type (
// hook refs are internal references of commands for each hook type
hookRefs map[string]map[string]*Command
/*
Shell specifies which shell to run the command in, if any.
Not applicable when host is defined.
*/
// Shell specifies which shell to run the command in, if any.
Shell string `yaml:"shell,omitempty"`
RemoteHost *Host `yaml:"-"`
@ -80,7 +77,6 @@ type (
/*
Dir specifies a directory in which to run the command.
Ignored if Host is set.
*/
Dir *string `yaml:"dir,omitempty"`
@ -91,11 +87,14 @@ type (
Environment []string `yaml:"environment,omitempty"`
// Output determines if output is requested.
// Only works if command is in a list.
//
// Only for when command is in a list.
GetOutput bool `yaml:"getOutput,omitempty"`
ScriptEnvFile string `yaml:"scriptEnvFile"`
// BEGIN PACKAGE COMMAND FIELDS
PackageManager string `yaml:"packageManager,omitempty"`
PackageName string `yaml:"packageName,omitempty"`
@ -109,6 +108,7 @@ type (
pkgMan pkgman.PackageManager
packageCmdSet bool
// END PACKAGE COMMAND FIELDS
// RemoteSource specifies a URL to fetch the command or configuration remotely
RemoteSource string `yaml:"remoteSource,omitempty"`
@ -116,8 +116,12 @@ type (
// FetchBeforeExecution determines if the remoteSource should be fetched before running
FetchBeforeExecution bool `yaml:"fetchBeforeExecution,omitempty"`
// BEGIN USER COMMAND FIELDS
// Username specifies the username for user creation or related operations
Username string `yaml:"username,omitempty"`
Username string `yaml:"userName,omitempty"`
UserID string `yaml:"userID,omitempty"`
// UserGroups specifies the groups to add the user to
UserGroups []string `yaml:"userGroups,omitempty"`
@ -144,12 +148,15 @@ type (
userCmdSet bool
// stdin only for userOperation = password (for now)
stdin *strings.Reader
// END USER STRUCT FIELDS
}
RemoteSource struct {
URL string `yaml:"url"`
Type string `yaml:"type"` // e.g., yaml
Type string `yaml:"type"` // e.g., s3, http
Auth struct {
AccessKey string `yaml:"accessKey"`
SecretKey string `yaml:"secretKey"`
@ -179,7 +186,7 @@ type (
// CmdConfigLists holds the lists of commands to be run in order.
// Key is the command list name.
CmdConfigLists map[string]*CmdList `yaml:"cmd-lists"`
CmdConfigLists map[string]*CmdList `yaml:"cmdLists"`
// Hosts holds the Host config.
// key is the host.
@ -190,6 +197,8 @@ type (
// Global log level
BackyLogLvl *string
CmdStdOut bool
// Holds config file
ConfigFilePath string

View File

@ -43,7 +43,7 @@ func AddCommandLists(lists []string) BackyOptionFunc {
}
}
// AddPrintLists adds lists to print out
// SetListsToSearch adds lists to search
func SetListsToSearch(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.List.Lists = append(bco.List.Lists, lists...)
@ -64,8 +64,15 @@ func SetLogFile(logFile string) BackyOptionFunc {
}
}
// cronEnabled enables the execution of command lists at specified times
func CronEnabled() BackyOptionFunc {
// SetCmdStdOut forces the command output to stdout
func SetCmdStdOut(setStdOut bool) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.CmdStdOut = setStdOut
}
}
// EnableCron enables the execution of command lists at specified times
func EnableCron() BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.cronEnabled = true
}
@ -182,10 +189,12 @@ func IsTerminalActive() bool {
}
func IsCmdStdOutEnabled() bool {
return os.Getenv("BACKY_STDOUT") == "enabled"
return os.Getenv("BACKY_CMDSTDOUT") == "enabled"
}
func resolveDir(path string) (string, error) {
path = strings.TrimSpace(path)
if path == "~" {
homeDir, err := os.UserHomeDir()
if err != nil {

View File

@ -40,7 +40,6 @@ func (a *AptManager) Install(pkg, version string, args []string) (string, []stri
if args != nil {
baseArgs = append(baseArgs, args...)
}
fmt.Printf("baseArgs: %v\n", baseArgs)
return baseCmd, baseArgs
}

View File

@ -64,7 +64,6 @@ func (c *Cache) loadFromFile() error {
}
func (c *Cache) saveToFile() error {
// println("Saving cache to file:", c.file)
c.mu.Lock()
defer c.mu.Unlock()
@ -84,10 +83,8 @@ func (c *Cache) saveToFile() error {
func (c *Cache) Get(hash string) ([]byte, CacheData, bool) {
c.mu.Lock()
defer c.mu.Unlock()
println("Getting cache data for hash:", hash)
cacheData, exists := c.store[hash]
if !exists {
println("Cache data does not exist for hash:", hash)
return nil, CacheData{}, false
}
@ -99,10 +96,9 @@ func (c *Cache) Get(hash string) ([]byte, CacheData, bool) {
return data, cacheData, true
}
func (c *Cache) AddDataToStore(hash string, cacheData CacheData) {
c.mu.Lock()
defer c.mu.Unlock()
func (c *Cache) AddDataToStore(hash string, cacheData CacheData) error {
c.store[hash] = cacheData
return c.saveToFile()
}
func (c *Cache) Set(source, hash string, data []byte, dataType string) (CacheData, error) {
@ -164,9 +160,8 @@ func (cf *CachedFetcher) Hash(data []byte) string {
return hex.EncodeToString(hash[:])
}
// Function to read and parse the hashMetadataSample.yml file
// Function to read and parse the metadata file
func LoadMetadataFromFile(filePath string) ([]*CacheData, error) {
// fmt.Println("Loading metadata from file:", filePath)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
// Create the file if it does not exist
emptyData := []byte("[]")

View File

@ -5,7 +5,7 @@ import (
"strings"
)
type ConfigFetcher interface {
type RemoteFetcher interface {
// Fetch retrieves the configuration from the specified URL or source
// Returns the raw data as bytes or an error
Fetch(source string) ([]byte, error)
@ -18,30 +18,31 @@ type ConfigFetcher interface {
Hash(data []byte) string
}
// ErrFileNotFound is returned when the file is not found and should be ignored
var ErrFileNotFound = errors.New("remotefetcher: file not found")
// ErrIgnoreFileNotFound is returned when the file is not found and should be ignored
var ErrIgnoreFileNotFound = errors.New("remotefetcher: file not found")
func NewConfigFetcher(source string, cache *Cache, options ...Option) (ConfigFetcher, error) {
var fetcher ConfigFetcher
var dataType string
func NewRemoteFetcher(source string, cache *Cache, options ...FetcherOption) (RemoteFetcher, error) {
var fetcher RemoteFetcher
config := FetcherConfig{}
for _, option := range options {
option(&config)
}
// If FileType is empty (i.e. WithFileType was not called), yaml is the default file type
if strings.TrimSpace(config.FileType) == "" {
config.FileType = "yaml"
}
if strings.HasPrefix(source, "http") || strings.HasPrefix(source, "https") {
fetcher = NewHTTPFetcher(options...)
dataType = "yaml"
} else if strings.HasPrefix(source, "s3") {
var err error
fetcher, err = NewS3Fetcher(options...)
fetcher, err = NewS3Fetcher(source, options...)
if err != nil {
return nil, err
}
dataType = "yaml"
} else {
fetcher = &LocalFetcher{}
dataType = "yaml"
return fetcher, nil
}
@ -51,7 +52,7 @@ func NewConfigFetcher(source string, cache *Cache, options ...Option) (ConfigFet
data, err := fetcher.Fetch(source)
if err != nil {
if config.IgnoreFileNotFound && isFileNotFoundError(err) {
return nil, ErrFileNotFound
return nil, ErrIgnoreFileNotFound
}
return nil, err
}
@ -61,7 +62,7 @@ func NewConfigFetcher(source string, cache *Cache, options ...Option) (ConfigFet
return &CachedFetcher{data: cachedData, path: cacheMeta.Path, dataType: cacheMeta.Type}, nil
}
cacheData, err := cache.Set(source, hash, data, dataType)
cacheData, err := cache.Set(source, hash, data, config.FileType)
if err != nil {
return nil, err
}

View File

@ -16,7 +16,7 @@ type HTTPFetcher struct {
}
// NewHTTPFetcher creates a new instance of HTTPFetcher with the provided options.
func NewHTTPFetcher(options ...Option) *HTTPFetcher {
func NewHTTPFetcher(options ...FetcherOption) *HTTPFetcher {
cfg := &FetcherConfig{}
for _, opt := range options {
opt(cfg)
@ -39,7 +39,7 @@ func (h *HTTPFetcher) Fetch(source string) ([]byte, error) {
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound && h.config.IgnoreFileNotFound {
return nil, ErrFileNotFound
return nil, ErrIgnoreFileNotFound
}
if resp.StatusCode != http.StatusOK {

View File

@ -18,7 +18,7 @@ func (l *LocalFetcher) Fetch(source string) ([]byte, error) {
// Check if the file exists
if _, err := os.Stat(source); os.IsNotExist(err) {
if l.config.IgnoreFileNotFound {
return nil, ErrFileNotFound
return nil, ErrIgnoreFileNotFound
}
return nil, nil
}

View File

@ -2,36 +2,48 @@ package remotefetcher
import (
"net/http"
"strings"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// Option is a function that configures a fetcher.
type Option func(*FetcherConfig)
type FetcherOption func(*FetcherConfig)
// FetcherConfig holds the configuration for a fetcher.
type FetcherConfig struct {
S3Client *s3.Client
HTTPClient *http.Client
FileType string
IgnoreFileNotFound bool
}
// WithS3Client sets the S3 client for the fetcher.
func WithS3Client(client *s3.Client) Option {
func WithS3Client(client *s3.Client) FetcherOption {
return func(cfg *FetcherConfig) {
cfg.S3Client = client
}
}
// WithHTTPClient sets the HTTP client for the fetcher.
func WithHTTPClient(client *http.Client) Option {
func WithHTTPClient(client *http.Client) FetcherOption {
return func(cfg *FetcherConfig) {
cfg.HTTPClient = client
}
}
func IgnoreFileNotFound() Option {
func IgnoreFileNotFound() FetcherOption {
return func(cfg *FetcherConfig) {
cfg.IgnoreFileNotFound = true
}
}
// WithFileType ensures the default FileType will be yaml
func WithFileType(fileType string) FetcherOption {
return func(cfg *FetcherConfig) {
cfg.FileType = fileType
if strings.TrimSpace(fileType) == "" {
cfg.FileType = "yaml"
}
}
}

View File

@ -1,71 +1,109 @@
package remotefetcher
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"net/http"
"net/url"
"os"
"path"
"strings"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/mitchellh/go-homedir"
"gopkg.in/yaml.v3"
)
type S3Fetcher struct {
S3Client *s3.Client
S3Client *minio.Client
config FetcherConfig
}
// NewS3Fetcher creates a new instance of S3Fetcher with the provided options.
func NewS3Fetcher(options ...Option) (*S3Fetcher, error) {
func NewS3Fetcher(endpoint string, options ...FetcherOption) (*S3Fetcher, error) {
cfg := &FetcherConfig{}
var s3Client *minio.Client
var err error
for _, opt := range options {
opt(cfg)
}
/*
options for S3 urls:
1. s3://bucket.region.endpoint.tld/path/to/object
2. alias with path and rest is looked up in file - add FetcherOptions
options for S3 credentials:
1. from file ($HOME/.aws/credentials)
2. env vars (AWS_SECRET_KEY, etc.)
*/
s3Endpoint := os.Getenv("S3_ENDPOINT")
creds, err := getS3Credentials("default", s3Endpoint, cfg.HTTPClient)
if err != nil {
println(err.Error())
return nil, err
}
// Initialize S3 client if not provided
if cfg.S3Client == nil {
awsCfg, err := config.LoadDefaultConfig(context.TODO())
s3Client, err = minio.New(s3Endpoint, &minio.Options{
Creds: creds,
Secure: true,
})
if err != nil {
return nil, err
}
cfg.S3Client = s3.NewFromConfig(awsCfg)
}
return &S3Fetcher{S3Client: cfg.S3Client, config: *cfg}, nil
return &S3Fetcher{S3Client: s3Client, config: *cfg}, nil
}
// Fetch retrieves the configuration from an S3 bucket
// Source should be in the format "bucket-name/object-key"
func (s *S3Fetcher) Fetch(source string) ([]byte, error) {
bucket, key, err := parseS3Source(source)
bucket, object, err := parseS3Source(source)
if err != nil {
return nil, err
}
resp, err := s.S3Client.GetObject(context.TODO(), &s3.GetObjectInput{
Bucket: &bucket,
Key: &key,
})
if err != nil {
var notFound *types.NoSuchKey
if errors.As(err, &notFound) && s.config.IgnoreFileNotFound {
return nil, ErrFileNotFound
}
doesObjectExist, objErr := objectExists(bucket, object, s.S3Client)
if !doesObjectExist {
if objErr != nil {
return nil, err
}
defer resp.Body.Close()
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(resp.Body)
if s.config.IgnoreFileNotFound {
return nil, ErrIgnoreFileNotFound
}
}
fileObject, err := s.S3Client.GetObject(context.TODO(), bucket, object, minio.GetObjectOptions{})
if err != nil {
println(err.Error())
return nil, err
}
defer fileObject.Close()
fileObjectStats, statErr := fileObject.Stat()
if statErr != nil {
return nil, statErr
}
buffer := make([]byte, fileObjectStats.Size)
// Read the object into the buffer
_, err = io.ReadFull(fileObject, buffer)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
return buffer, nil
}
// Parse decodes the raw data into the provided target structure
@ -79,10 +117,46 @@ func parseS3Source(source string) (bucket, key string, err error) {
if len(parts) != 2 {
return "", "", errors.New("invalid S3 source format, expected bucket-name/object-key")
}
return parts[0], parts[1], nil
u, _ := url.Parse(source)
u.Path = strings.TrimPrefix(u.Path, "/")
return u.Host, u.Path, nil
}
func (s *S3Fetcher) Hash(data []byte) string {
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:])
}
func getS3Credentials(profile, host string, httpClient *http.Client) (*credentials.Credentials, error) {
// println(s3utils.GetRegionFromURL(*u))
homeDir, hdirErr := homedir.Dir()
if hdirErr != nil {
return nil, hdirErr
}
s3Creds := credentials.NewFileAWSCredentials(path.Join(homeDir, ".aws", "credentials"), "default")
credVals, credErr := s3Creds.GetWithContext(&credentials.CredContext{Endpoint: host, Client: httpClient})
if credErr != nil {
return nil, credErr
}
creds := credentials.NewStaticV4(credVals.AccessKeyID, credVals.SecretAccessKey, "")
return creds, nil
}
var (
doesNotExist = "The specified key does not exist."
)
// objectExists checks for name in bucket using client.
// It returns false and nil if the key does not exist
func objectExists(bucket, name string, client *minio.Client) (bool, error) {
_, err := client.StatObject(context.TODO(), bucket, name, minio.StatObjectOptions{})
if err != nil {
switch err.Error() {
case doesNotExist:
return false, nil
default:
return false, errors.Join(err, errors.New("error stating object"))
}
}
return true, nil
}

View File

@ -0,0 +1,7 @@
package common
// ConfigurablePackageManager defines methods for setting configuration options.
type ConfigurableUserManager interface {
SetUseAuth(useAuth bool)
SetAuthCommand(authCommand string)
}

View File

@ -20,6 +20,7 @@ type UserManager interface {
UserExists(username string) (string, []string)
}
// NewUserManager returns a UserManager-compatible struct
func NewUserManager(system string) (UserManager, error) {
var manager UserManager

11
release
View File

@ -1,6 +1,13 @@
#!/bin/bash
# export GORELEASER_CURRENT_TAG="$(go run backy.go version -V)"
git tag "$(go run backy.go version -V)"
set -eou pipefail
export CURRENT_TAG="$(go run backy.go version -V)"
goreleaser -f .goreleaser/github.yml check
goreleaser -f .goreleaser/gitea.yml check
changie batch $CURRENT_TAG
changie merge
git add .changes/
git commit -am "$CURRENT_TAG"
git tag "$CURRENT_TAG"
git push all
git push all --tags
# goreleaser release -f .goreleaser/gitea.yml --clean --release-notes=".changes/$(go run backy.go version -V).md"