-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add St Bernard to help with module syncing in the workspace (#58)
* feat: add proper variant of St Bernard * chore: use modsync instead of build for init * chore: add config files to air reloads * chore: change foo to a more useful example command * chore: cleanup * chore: start up integration testing services as part of init * chore: add volume clearing and clean build conditionally * chore: add wiping mechanic to integration testing services * fix: possible infinite loop if running modsync in a directory with no go.work anywhere above it * fix: remove bh-testing service start from init
- Loading branch information
1 parent
cfc127c
commit a8bb5bd
Showing
13 changed files
with
476 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,38 @@ | ||
{ | ||
"recommendations": [ | ||
"alefragnani.bookmarks", | ||
"nickgo.cuelang", | ||
"hediet.vscode-drawio", | ||
"ms-toolsai.jupyter-keymap", | ||
"pkief.material-icon-theme", | ||
"ms-vscode-remote.vscode-remote-extensionpack", | ||
"ms-vscode.remote-explorer", | ||
"ms-python.black-formatter", | ||
"jakeboone02.cypher-query-language", | ||
"redhat.fabric8-analytics", | ||
"ms-azuretools.vscode-docker", | ||
"tombonnike.vscode-status-bar-format-toggle", | ||
"felipecaputo.git-project-manager", | ||
"eamodio.gitlens", | ||
"golang.go", | ||
"bierner.markdown-checkbox", | ||
"ryu1kn.partial-diff", | ||
"jebbs.plantuml", | ||
"ms-ossdata.vscode-postgresql", | ||
"ms-python.vscode-pylance", | ||
"ms-python.python", | ||
"buenon.scratchpads", | ||
"richie5um2.vscode-sort-json", | ||
"luisfontes19.vscode-swissknife", | ||
"tabnine.tabnine-vscode", | ||
"gruntfuggly.todo-tree", | ||
"tomsaunders.vscode-workspace-explorer", | ||
"tonybaloney.vscode-pets", | ||
"gitlab.gitlab-workflow", | ||
"yoavbls.pretty-ts-errors", | ||
"esbenp.prettier-vscode", | ||
"prisma.prisma", | ||
"skellock.just", | ||
"github.vscode-github-actions" | ||
] | ||
} | ||
"recommendations": [ | ||
"alefragnani.bookmarks", | ||
"nickgo.cuelang", | ||
"hediet.vscode-drawio", | ||
"ms-toolsai.jupyter-keymap", | ||
"pkief.material-icon-theme", | ||
"ms-vscode-remote.vscode-remote-extensionpack", | ||
"ms-vscode.remote-explorer", | ||
"ms-python.black-formatter", | ||
"jakeboone02.cypher-query-language", | ||
"redhat.fabric8-analytics", | ||
"ms-azuretools.vscode-docker", | ||
"tombonnike.vscode-status-bar-format-toggle", | ||
"felipecaputo.git-project-manager", | ||
"eamodio.gitlens", | ||
"golang.go", | ||
"bierner.markdown-checkbox", | ||
"ryu1kn.partial-diff", | ||
"jebbs.plantuml", | ||
"ms-ossdata.vscode-postgresql", | ||
"ms-python.vscode-pylance", | ||
"ms-python.python", | ||
"buenon.scratchpads", | ||
"richie5um2.vscode-sort-json", | ||
"luisfontes19.vscode-swissknife", | ||
"tabnine.tabnine-vscode", | ||
"gruntfuggly.todo-tree", | ||
"tomsaunders.vscode-workspace-explorer", | ||
"tonybaloney.vscode-pets", | ||
"gitlab.gitlab-workflow", | ||
"yoavbls.pretty-ts-errors", | ||
"esbenp.prettier-vscode", | ||
"prisma.prisma", | ||
"kokakiwi.vscode-just", | ||
"github.vscode-github-actions" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,4 +34,5 @@ use ( | |
./packages/go/params | ||
./packages/go/schemagen | ||
./packages/go/slices | ||
./packages/go/stbernard | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package command | ||
|
||
import ( | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/specterops/bloodhound/packages/go/stbernard/command/envdump" | ||
"github.com/specterops/bloodhound/packages/go/stbernard/command/modsync" | ||
) | ||
|
||
// Commander is an interface for commands, allowing commands to implement the minimum | ||
// set of requirements to observe and run the command from above. It is used as a return | ||
// type to allow passing a usable command to the caller after parsing and creating | ||
// the command implementation | ||
type Commander interface { | ||
Name() string | ||
Usage() string | ||
Run() error | ||
} | ||
|
||
var NoCmdErr = errors.New("no command specified") | ||
var InvalidCmdErr = errors.New("invalid command specified") | ||
var FailedCreateCmdErr = errors.New("failed to create command") | ||
|
||
// ParseCLI parses for a subcommand as the first argument to the calling binary, | ||
// and initializes the command (if it exists). It also provides the default usage | ||
// statement. | ||
// | ||
// It does not support flags of its own, each subcommand is responsible for parsing | ||
// their flags. | ||
func ParseCLI() (Commander, error) { | ||
// Generate a nice usage message | ||
flag.Usage = usage | ||
|
||
// Default usage if no arguments provided | ||
if len(os.Args) < 2 { | ||
flag.Usage() | ||
return nil, NoCmdErr | ||
} | ||
|
||
switch os.Args[1] { | ||
case ModSync.String(): | ||
config := modsync.Config{Environment: environment()} | ||
if cmd, err := modsync.Create(config); err != nil { | ||
return nil, fmt.Errorf("%w: %w", FailedCreateCmdErr, err) | ||
} else { | ||
return cmd, nil | ||
} | ||
|
||
case EnvDump.String(): | ||
config := envdump.Config{Environment: environment()} | ||
if cmd, err := envdump.Create(config); err != nil { | ||
return nil, fmt.Errorf("%w: %w", FailedCreateCmdErr, err) | ||
} else { | ||
return cmd, nil | ||
} | ||
|
||
default: | ||
flag.Parse() | ||
flag.Usage() | ||
return nil, InvalidCmdErr | ||
} | ||
} | ||
|
||
// usage creates a pretty usage message for our main command | ||
func usage() { | ||
var longestCmdLen int | ||
|
||
w := flag.CommandLine.Output() | ||
fmt.Fprint(w, "A BloodHound Swiss Army Knife\n\nUsage: stbernard COMMAND\n\nCommands:\n") | ||
|
||
for _, cmd := range Commands() { | ||
if len(cmd.String()) > longestCmdLen { | ||
longestCmdLen = len(cmd.String()) | ||
} | ||
} | ||
|
||
for cmd, usage := range CommandsUsage() { | ||
cmdStr := Command(cmd).String() | ||
padding := strings.Repeat(" ", longestCmdLen-len(cmdStr)) | ||
fmt.Fprintf(w, " %s%s %s\n", cmdStr, padding, usage) | ||
} | ||
} | ||
|
||
// environment is used to add default env vars as needed to the existing environment variables | ||
func environment() []string { | ||
var envMap = make(map[string]string) | ||
|
||
for _, env := range os.Environ() { | ||
envTuple := strings.SplitN(env, "=", 2) | ||
envMap[envTuple[0]] = envTuple[1] | ||
} | ||
|
||
// Make any changes here | ||
envMap["FOO"] = "foo" // For illustrative purposes only | ||
|
||
var envSlice = make([]string, 0, len(envMap)) | ||
for key, val := range envMap { | ||
envSlice = append(envSlice, strings.Join([]string{key, val}, "=")) | ||
} | ||
|
||
return envSlice | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package envdump | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
const ( | ||
Name = "envdump" | ||
Usage = "Dump your environment variables" | ||
) | ||
|
||
type Config struct { | ||
Environment []string | ||
} | ||
|
||
type command struct { | ||
config Config | ||
} | ||
|
||
func (s command) Name() string { | ||
return Name | ||
} | ||
|
||
func (s command) Usage() string { | ||
return Usage | ||
} | ||
|
||
func (s command) Run() error { | ||
log.Printf("Environment:\n\n") | ||
for _, env := range s.config.Environment { | ||
envTuple := strings.SplitN(env, "=", 2) | ||
log.Printf("%s: %s\n", envTuple[0], envTuple[1]) | ||
} | ||
log.Printf("\n") | ||
|
||
return nil | ||
} | ||
|
||
func Create(config Config) (command, error) { | ||
envdumpCmd := flag.NewFlagSet(Name, flag.ExitOnError) | ||
|
||
envdumpCmd.Usage = func() { | ||
w := flag.CommandLine.Output() | ||
fmt.Fprintf(w, "%s\n\nUsage: %s %s [OPTIONS]\n", Usage, filepath.Base(os.Args[0]), Name) | ||
} | ||
|
||
if err := envdumpCmd.Parse(os.Args[2:]); err != nil { | ||
envdumpCmd.Usage() | ||
return command{}, fmt.Errorf("failed to parse %s command: %w", Name, err) | ||
} else { | ||
return command{config: config}, nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package modsync | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/specterops/bloodhound/packages/go/stbernard/workspace" | ||
) | ||
|
||
const ( | ||
Name = "modsync" | ||
Usage = "Sync all modules in current workspace" | ||
) | ||
|
||
type flags struct { | ||
verbose bool | ||
} | ||
|
||
type Config struct { | ||
flags flags | ||
Environment []string | ||
} | ||
|
||
type command struct { | ||
config Config | ||
} | ||
|
||
func (s command) Usage() string { | ||
return Usage | ||
} | ||
|
||
func (s command) Name() string { | ||
return Name | ||
} | ||
|
||
func (s command) Run() error { | ||
if cwd, err := workspace.FindRoot(); err != nil { | ||
return fmt.Errorf("could not find workspace root: %w", err) | ||
} else if modPaths, err := workspace.ParseModulesAbsPaths(cwd); err != nil { | ||
return fmt.Errorf("could not parse module absolute paths: %w", err) | ||
} else if err := workspace.DownloadModules(modPaths, s.config.Environment); err != nil { | ||
return fmt.Errorf("could not download modules: %w", err) | ||
} else if err := workspace.SyncWorkspace(cwd, s.config.Environment); err != nil { | ||
return fmt.Errorf("could not sync workspace: %w", err) | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
func Create(config Config) (command, error) { | ||
modsyncCmd := flag.NewFlagSet(Name, flag.ExitOnError) | ||
modsyncCmd.BoolVar(&config.flags.verbose, "v", false, "Print verbose logs") | ||
|
||
modsyncCmd.Usage = func() { | ||
w := flag.CommandLine.Output() | ||
fmt.Fprintf(w, "%s\n\nUsage: %s %s [OPTIONS]\n\nOptions:\n", Usage, filepath.Base(os.Args[0]), Name) | ||
modsyncCmd.PrintDefaults() | ||
} | ||
|
||
if err := modsyncCmd.Parse(os.Args[2:]); err != nil { | ||
modsyncCmd.Usage() | ||
return command{}, fmt.Errorf("failed to parse modsync command: %w", err) | ||
} else { | ||
return command{config: config}, nil | ||
} | ||
} |
Oops, something went wrong.