v0.11.0
Some checks failed
Some checks failed
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'feat: Package operation `versionCheck` supports regular expressions (see [regexp](https://pkg.go.dev/regexp) package for docs)'
|
||||
time: 2025-04-09T17:45:28.836497149-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'Command lists: added `cmdLists.[name].notify` object'
|
||||
time: 2025-05-01T11:07:45.96164753-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: Testing setup with Docker
|
||||
time: 2025-07-04T08:59:17.430373451-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'CLI: add global flag --hostsConfig that allows hosts to be dynamic in relation to the main config'
|
||||
time: 2025-07-04T10:21:26.864635558-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'CLI: Exec subcommand `hosts`. See documentation for more details.'
|
||||
time: 2025-07-15T20:23:03.647128713-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'CLI: added `exec hosts` subcommand `list`'
|
||||
time: 2025-07-23T22:03:40.24191927-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: 'Commands: `host` can now be `localhost` or `127.0.0.1` to run commands locally'
|
||||
time: 2025-03-21T09:08:49.871021144-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: lists loaded from external files only if no list config present in current file
|
||||
time: 2025-03-25T00:33:57.039431409-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: "`PackageManager.Parse` renamed to `ParseRemotePackageManagerVersionOutput`. This now returns arrays of PackageManagerCommon.Package and errors."
|
||||
time: 2025-04-07T22:30:20.342177323-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: 'Internal: refactoring and renaming functions'
|
||||
time: 2025-04-18T13:34:40.842541658-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: 'Commands: moved output-prefixed keys to the `commands.[name].output` object'
|
||||
time: 2025-05-01T11:05:34.90130087-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: Change internal method name for better understanding
|
||||
time: 2025-06-09T07:26:01.819927627-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: Improved error message for remote version package output
|
||||
time: 2025-07-09T23:19:19.431960446-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: 'Command Lists: hooks now run correctly when commands finish'
|
||||
time: 2025-04-18T09:57:47.39035092-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: Log file passed using `--log-file` correctly used
|
||||
time: 2025-04-24T22:57:11.592829277-05:00
|
||||
@@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: Cmd Type `script` now correctly appends arguments
|
||||
time: 2025-11-15T17:32:06.86128885-06:00
|
||||
21
.changes/v0.11.0.md
Normal file
21
.changes/v0.11.0.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## v0.11.0 - 2025-11-24
|
||||
### Added
|
||||
* feat: Package operation `versionCheck` supports regular expressions (see [regexp](https://pkg.go.dev/regexp) package for docs)
|
||||
* Command lists: added `cmdLists.[name].notify` object
|
||||
* Testing setup with Docker
|
||||
* CLI: add global flag --hostsConfig that allows hosts to be dynamic in relation to the main config
|
||||
* CLI: Exec subcommand `hosts`. See documentation for more details.
|
||||
* CLI: added `exec hosts` subcommand `list`
|
||||
* Add support for hosts in parallel
|
||||
### Changed
|
||||
* Commands: `host` can now be `localhost` or `127.0.0.1` to run commands locally
|
||||
* lists loaded from external files only if no list config present in current file
|
||||
* `PackageManager.Parse` renamed to `ParseRemotePackageManagerVersionOutput`. This now returns arrays of PackageManagerCommon.Package and errors.
|
||||
* Internal: refactoring and renaming functions
|
||||
* Commands: moved output-prefixed keys to the `commands.[name].output` object
|
||||
* Change internal method name for better understanding
|
||||
* Improved error message for remote version package output
|
||||
### Fixed
|
||||
* Command Lists: hooks now run correctly when commands finish
|
||||
* Log file passed using `--log-file` correctly used
|
||||
* Cmd Type `script` now correctly appends arguments
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -6,6 +6,28 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v0.11.0 - 2025-11-24
|
||||
### Added
|
||||
* feat: Package operation `versionCheck` supports regular expressions (see [regexp](https://pkg.go.dev/regexp) package for docs)
|
||||
* Command lists: added `cmdLists.[name].notify` object
|
||||
* Testing setup with Docker
|
||||
* CLI: add global flag --hostsConfig that allows hosts to be dynamic in relation to the main config
|
||||
* CLI: Exec subcommand `hosts`. See documentation for more details.
|
||||
* CLI: added `exec hosts` subcommand `list`
|
||||
* Add support for hosts in parallel
|
||||
### Changed
|
||||
* Commands: `host` can now be `localhost` or `127.0.0.1` to run commands locally
|
||||
* lists loaded from external files only if no list config present in current file
|
||||
* `PackageManager.Parse` renamed to `ParseRemotePackageManagerVersionOutput`. This now returns arrays of PackageManagerCommon.Package and errors.
|
||||
* Internal: refactoring and renaming functions
|
||||
* Commands: moved output-prefixed keys to the `commands.[name].output` object
|
||||
* Change internal method name for better understanding
|
||||
* Improved error message for remote version package output
|
||||
### Fixed
|
||||
* Command Lists: hooks now run correctly when commands finish
|
||||
* Log file passed using `--log-file` correctly used
|
||||
* Cmd Type `script` now correctly appends arguments
|
||||
|
||||
## v0.10.2 - 2025-03-19
|
||||
### Added
|
||||
* Notifications: http service added
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const versionStr = "0.10.2"
|
||||
const versionStr = "0.11.0"
|
||||
|
||||
var (
|
||||
versionCmd = &cobra.Command{
|
||||
|
||||
@@ -19,7 +19,8 @@ Values available for this section **(case-sensitive)**:
|
||||
| `environment` | Defines environment variables for the command | `[]string` | no | Partial |
|
||||
| `type` | See documentation further down the page. Additional fields may be required. | `string` | no | No |
|
||||
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no | No |
|
||||
| `host` | If not specified, the command will execute locally. | `string` | no | No |
|
||||
| `host` | Depricated: use `hosts`. If not specified, the command will execute locally. | `string` | no | No |
|
||||
| `hosts` | Must be specified to run commands both locallly and in parrallel. | `[]string` | no | No |
|
||||
| `scriptEnvFile` | When type is `scriptFile` or `script`, this file is prepended to the input. | `string` | no | No |
|
||||
| `shell` | Run the command in the shell | `string` | no | No |
|
||||
| `hooks` | Hooks are used at the end of the individual command. Must have at least `error`, `success`, or `final`. | `map[string][]string` | no | No |
|
||||
@@ -51,7 +52,12 @@ Get command output when a notification is sent.
|
||||
|
||||
Is not required. Can be `true` or `false`.
|
||||
|
||||
#### host
|
||||
### host
|
||||
|
||||
|
||||
{{% notice warning %}}
|
||||
Depricated: use `hosts` instead.
|
||||
{{% /notice %}}
|
||||
|
||||
{{% notice info %}}
|
||||
If any `host` is not defined or left blank, the command will run on the local machine.
|
||||
@@ -66,6 +72,36 @@ For example, say that I have a host defined in my SSH config with the `Host` def
|
||||
If I assign a value to host as `host: web-prod` and don't specify this value in the `hosts` object, web-prod will be used as the `Host` in searching the SSH config files.
|
||||
{{% /notice %}}
|
||||
|
||||
### hosts
|
||||
|
||||
{{% notice info %}}
|
||||
If any `command.[name].hosts` index is `localhost` or `127.0.0.1`, the command will run on the local machine.
|
||||
|
||||
You can also remove the field to have the command run locally.
|
||||
{{% /notice %}}
|
||||
|
||||
Host may or may not be defined in the `hosts` section.
|
||||
|
||||
{{% notice info %}}
|
||||
If any `host` from the commands section does not match any object in the `hosts` section, the `Host` is assumed to be this value. This value will be used to search in the default SSH config files.
|
||||
|
||||
For example, say that I have a host defined in my SSH config with the `Host` defined as `web-prod`.
|
||||
If I assign a value to host as `host: web-prod` and don't specify this value in the `hosts` object, web-prod will be used as the `Host` in searching the SSH config files.
|
||||
{{% /notice %}}
|
||||
|
||||
###### Example:
|
||||
|
||||
|
||||
```yaml
|
||||
command:
|
||||
start-some-process:
|
||||
cmd: start-server
|
||||
hosts:
|
||||
- prod-1
|
||||
- prod-2
|
||||
```
|
||||
|
||||
|
||||
### shell
|
||||
|
||||
If shell is defined, the command will run in the specified shell.
|
||||
|
||||
@@ -8,7 +8,7 @@ This is dedicated to `package` commands. The command `type` field must be `packa
|
||||
|
||||
| name | notes | type | required |
|
||||
| --- | --- | --- | --- |
|
||||
| `packageName` | The name of a package to be modified. | `string` | yes |
|
||||
| `packageName` | The name of a package to be modified. | `[]packagemanagercommon.Package` | yes |
|
||||
| `packageManager` | The name of the package manger to be used. | `string` | yes |
|
||||
| `packageOperation` | The type of operation to perform. | `string` | yes |
|
||||
| `packageVersion` | The version of a package. | `string` | no |
|
||||
@@ -22,7 +22,9 @@ The following is an example of a package command:
|
||||
update-docker:
|
||||
type: package
|
||||
shell: zsh
|
||||
packageName: docker-ce
|
||||
packages:
|
||||
- name: docker-ce
|
||||
version: 10
|
||||
packageManager: apt
|
||||
packageOperation: install
|
||||
host: debian-based-host
|
||||
|
||||
@@ -29,20 +29,22 @@ commands:
|
||||
update-docker:
|
||||
type: package
|
||||
shell: zsh # best to run package commands in a shell
|
||||
packageName: docker-ce
|
||||
Args:
|
||||
- docker-ce-cli
|
||||
packages:
|
||||
- name: docker-ce
|
||||
version: latest
|
||||
- name: docker-ce-cli
|
||||
version: latest
|
||||
packageManager: apt
|
||||
packageOperation: install
|
||||
update-dockerApt:
|
||||
# type: package
|
||||
shell: zsh
|
||||
cmd: apt
|
||||
Args:
|
||||
- update
|
||||
- "&&"
|
||||
- apt install -y docker-ce
|
||||
- docker-ce-cli
|
||||
packages:
|
||||
- name: docker-ce
|
||||
version: latest
|
||||
- name: docker-ce-cli
|
||||
version: latest
|
||||
packageManager: apt
|
||||
packageOperation: install
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ commands:
|
||||
- down
|
||||
# if host is not defined, command will be run locally
|
||||
# The host has to be defined in either the config file or the SSH Config files
|
||||
host: some-host
|
||||
hosts:
|
||||
- prod
|
||||
hooks:
|
||||
error:
|
||||
- some-other-command-when-failing
|
||||
|
||||
0
pkg/backy/allowedexternaldirectives_enumer.go
Executable file → Normal file
0
pkg/backy/allowedexternaldirectives_enumer.go
Executable file → Normal file
@@ -145,6 +145,73 @@ func (e *LocalCommandExecutor) Run(cmd *Command, opts *ConfigOpts, logger zerolo
|
||||
return outputArr, nil
|
||||
}
|
||||
|
||||
// ensureRemoteHost ensures localCmd.RemoteHost is set for the given host.
|
||||
// It prefers opts.Hosts lookup and falls back to a minimal Host entry so remote execution can proceed.
|
||||
func (opts *ConfigOpts) ensureRemoteHost(localCmd *Command, host string) {
|
||||
if localCmd.RemoteHost != nil {
|
||||
return
|
||||
}
|
||||
if opts != nil && opts.Hosts != nil {
|
||||
if rh, found := opts.Hosts[host]; found {
|
||||
localCmd.RemoteHost = rh
|
||||
return
|
||||
}
|
||||
}
|
||||
// fallback: create a minimal Host so RunCmdOnHost sees a non-nil RemoteHost.
|
||||
// This uses host as the address/alias; further fields (user/key) will use defaults.
|
||||
localCmd.RemoteHost = &Host{Host: host}
|
||||
}
|
||||
|
||||
// ExecCommandOnHostsParallel runs a single configured command concurrently on the command.Hosts list.
|
||||
// It reuses the standard RunCmd / RunCmdOnHost flow so the behavior is identical to normal execution.
|
||||
func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult, error) {
|
||||
cmdObj, ok := opts.Cmds[cmdName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("command %s not found", cmdName)
|
||||
}
|
||||
if len(cmdObj.Hosts) == 0 {
|
||||
return nil, fmt.Errorf("no hosts configured for command %s", cmdName)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
resultsCh := make(chan CmdResult, len(cmdObj.Hosts))
|
||||
|
||||
for _, host := range cmdObj.Hosts {
|
||||
wg.Add(1)
|
||||
go func(h string) {
|
||||
defer wg.Done()
|
||||
// shallow copy to avoid races
|
||||
local := *cmdObj
|
||||
local.Host = h
|
||||
opts.Logger.Debug().Str("host", h).Msg("executing command in parallel on host")
|
||||
|
||||
var err error
|
||||
if IsHostLocal(h) {
|
||||
_, err := local.RunCmd(local.GenerateLogger(opts), opts)
|
||||
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err}
|
||||
return
|
||||
// _, err = local.RunCmd(local.GenerateLogger(opts), opts)
|
||||
}
|
||||
|
||||
// ensure RemoteHost is populated before calling RunCmdOnHost
|
||||
opts.ensureRemoteHost(&local, h)
|
||||
|
||||
_, err = local.RunCmdOnHost(local.GenerateLogger(opts), opts)
|
||||
|
||||
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err}
|
||||
}(host)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(resultsCh)
|
||||
|
||||
var results []CmdResult
|
||||
for r := range resultsCh {
|
||||
results = append(results, r)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// RunCmd runs a Command.
|
||||
// The environment of local commands will be the machine's environment plus any extra
|
||||
// variables specified in the Env file or Environment.
|
||||
@@ -167,6 +234,14 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
||||
outputArr []string // holds the output strings returned by processes
|
||||
)
|
||||
|
||||
if command.Host != "" && command.Hosts != nil {
|
||||
cmdCtxLogger.Warn().Msg("both 'host' and 'hosts' are set; 'hosts' will be ignored")
|
||||
return nil, fmt.Errorf("both 'host' and 'hosts' are set; please set one or the other")
|
||||
} else if command.Hosts != nil {
|
||||
opts.ExecCommandOnHostsParallel(command.Name)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Getting the command type must be done before concatenating the arguments
|
||||
command = getCommandTypeAndSetCommandInfo(command)
|
||||
|
||||
|
||||
0
pkg/backy/commandtype_enumer.go
Executable file → Normal file
0
pkg/backy/commandtype_enumer.go
Executable file → Normal file
0
pkg/backy/packageoperation_enumer.go
Executable file → Normal file
0
pkg/backy/packageoperation_enumer.go
Executable file → Normal file
@@ -454,6 +454,10 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
// cmdCtxLogger.Debug().Str("cmd", command.Cmd).Strs("args", command.Args).Send()
|
||||
|
||||
// Ensure SSH client is connected
|
||||
if command.RemoteHost == nil {
|
||||
cmdCtxLogger.Err(fmt.Errorf("remote host is not defined for command %s", command.Name)).Send()
|
||||
return nil, fmt.Errorf("remote host is not defined for command %s", command.Name)
|
||||
}
|
||||
if command.RemoteHost.SshClient == nil {
|
||||
if err := command.RemoteHost.ConnectToHost(opts); err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to host: %w", err)
|
||||
|
||||
@@ -57,7 +57,8 @@ type (
|
||||
// See CommandType enum further down the page for acceptable values
|
||||
Type CommandType `yaml:"type,omitempty"`
|
||||
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Hosts []string `yaml:"hosts,omitempty"`
|
||||
|
||||
Hooks *Hooks `yaml:"hooks,omitempty"`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user