Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,32 @@ Conjure ships as a single binary with no runtime dependencies.

See the [full documentation](https://conjure.wizardops.dev) on the Conjure website.

## Features and Status

| # | Feature | Status |
| :-: | ------------------------------------------------- | :----: |
| 1 | Template generation with Go template syntax | Done |
| 2 | Bundle generation (multiple templates at once) | Done |
| 3 | Interactive mode with guided variable prompts | Done |
| 4 | Values files with variable precedence | Done |
| 5 | Local template and bundle repositories | Done |
| 6 | Remote repositories with SHA256 verification | Done |
| 7 | Repository index generation (`conjure repo index`)| Done |
| 8 | Variable types: string, int, bool | Done |
## Features

- Template configuration files once, generate many times interactivley or in automated workflows.
- Bundle generation (multiple templates at once). Package multiple templates into bundles.
- Interactive mode with guided variable prompts.
- Values files and `--vars` flags with variable precedence.
- Local template and bundle repositories supported.
- Remote repositories support with SHA256 verification over https/https.
- Repository index generation (`conjure repo index`) made easy.
- Support for versioned templates and bundles.

### Interactive Mode
Run in interactive mode for a more guided configuration creation:

![generate a redis.conf](./assets/theme-dragon-hoard.gif)

### Non-interacitve Mode
For power users and automation `--vars` and `--values` / `f` files are supported and can be used in combination with precedence:

![generate a k8s manifests](./assets/non-interactive.gif)

## The Conjure Workflow

1. Someone templates a configruation or set of configurations.
2. That configuration is published to a local or remote registry.
3. Team members consume those templates interactivley or in automated workflows.

## Contributing

Expand Down
Binary file added assets/non-interactive.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/non-interactive.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Output non-interactive.gif

Set FontSize 14
Set Width 800
Set Height 400

Type "conjure bundle k8s-web-app -o ./manifests -f k8s-web-app-bundle-example.yaml --var image=docker.io/web"
Enter
Sleep 4s
Binary file added assets/theme-dragon-hoard.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions assets/theme-dragon-hoard.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Output theme-dragon-hoard.gif

Set FontSize 14
Set Width 800
Set Height 400

Type "conjure template redis-config -o redis.conf"
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Type "coolpassword"
Sleep 1s
Enter
Sleep 4s
Binary file added assets/theme-enchanted-aurora.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions assets/theme-enchanted-aurora.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Output theme-enchanted-aurora.gif

Set FontSize 14
Set Width 800
Set Height 400

Type "conjure template redis-config -o redis.conf"
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Type "coolpassword"
Sleep 1s
Enter
Sleep 4s

Binary file added assets/theme-runestone-grove.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions assets/theme-runestone-grove.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

Output theme-runestone-grove.gif

Set FontSize 14
Set Width 800
Set Height 400

Type "conjure template redis-config -o redis.conf"
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Enter
Sleep 1s
Type "coolpassword"
Sleep 1s
Enter
Sleep 4s

2 changes: 1 addition & 1 deletion cmd/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func generateBundle(bundleName, bundleVersion, outputPath string, varsList []str

if interactive {
var interactiveOverrides map[string]map[string]interface{}
userVariables, interactiveOverrides, err = prompt.CollectBundleVariables(bundleMeta, userVariables)
userVariables, interactiveOverrides, err = prompt.CollectBundleVariables(bundleMeta, userVariables, cfg.ColorTheme)
if err != nil {
return fmt.Errorf("failed to collect variables: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func initConfig() {
_ = viper.BindEnv("CACHE_DIR")
_ = viper.BindEnv("TEMPLATES_PRIORITY")
_ = viper.BindEnv("BUNDLES_PRIORITY")
_ = viper.BindEnv("COLOR_THEME")

if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
Expand Down
2 changes: 1 addition & 1 deletion cmd/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func generateTemplate(templateName, templateVersion, outputPath string, varsList
fmt.Printf("Using metadata: %s\n\n", meta.TemplateDescription)

if interactive {
finalVariables, err = prompt.CollectVariables(meta, userVariables)
finalVariables, err = prompt.CollectVariables(meta, userVariables, cfg.ColorTheme)
if err != nil {
return fmt.Errorf("failed to collect variables: %w", err)
}
Expand Down
6 changes: 6 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/spf13/viper"
"github.com/wizardopstech/conjure/internal/security"
"github.com/wizardopstech/conjure/internal/themes"
)

const (
Expand Down Expand Up @@ -39,6 +40,7 @@ type Config struct {
CacheDir string `mapstructure:"cache_dir"`
TemplatesPriority string `mapstructure:"templates_priority"`
BundlesPriority string `mapstructure:"bundles_priority"`
ColorTheme string `mapstructure:"color_theme"`
}

func LoadConfig() (*Config, error) {
Expand Down Expand Up @@ -156,6 +158,10 @@ func (c *Config) Validate() error {
return err
}

if !themes.IsValid(c.ColorTheme) {
return fmt.Errorf("invalid color_theme: %q (see https://conjure.wizardops.dev/docs/themes)", c.ColorTheme)
}

return nil
}

Expand Down
48 changes: 48 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,54 @@ func TestConfigValidate(t *testing.T) {
}
}

func TestConfigValidate_ColorTheme(t *testing.T) {
base := Config{
TemplatesSource: "local",
BundlesSource: "local",
TemplatesLocalDir: "templates",
BundlesLocalDir: "bundles",
CacheDir: ".cache",
TemplatesPriority: "local-first",
BundlesPriority: "local-first",
}

tests := []struct {
name string
colorTheme string
wantErr bool
}{
{"empty theme is valid (uses default)", "", false},
{"arcane-ember is valid", "arcane-ember", false},
{"moonlit-mana is valid", "moonlit-mana", false},
{"runestone-grove is valid", "runestone-grove", false},
{"spellforge is valid", "spellforge", false},
{"celestial-grimoire is valid", "celestial-grimoire", false},
{"mystic-marsh is valid", "mystic-marsh", false},
{"dragon-hoard is valid", "dragon-hoard", false},
{"enchanted-aurora is valid", "enchanted-aurora", false},
{"hexfire is valid", "hexfire", false},
{"potionmaker is valid", "potionmaker", false},
{"feywild-bloom is valid", "feywild-bloom", false},
{"storm-sorcerer is valid", "storm-sorcerer", false},
{"necromancers-ledger is valid", "necromancers-ledger", false},
{"sunspell-sanctum is valid", "sunspell-sanctum", false},
{"crystal-familiar is valid", "crystal-familiar", false},
{"unknown theme is invalid", "rainbow-wizard", true},
{"uppercase theme is invalid", "Arcane-Ember", true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := base
cfg.ColorTheme = tt.colorTheme
err := cfg.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Config.Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func TestConfigGetters(t *testing.T) {
cfg := Config{
TemplatesSource: "both",
Expand Down
Loading
Loading