Skip to content

Commit

Permalink
feat: adds global and optional secrets (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgilman authored Sep 13, 2024
1 parent cb1f8cc commit e88ac64
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 26 deletions.
8 changes: 8 additions & 0 deletions blueprint.cue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ global: {

github: registry: "ghcr.io"
}
secrets: [
{
name: "GITHUB_TOKEN"
optional: true
provider: "env"
path: "GITHUB_TOKEN"
},
]
tagging: {
aliases: {
forge: "forge/cli"
Expand Down
20 changes: 14 additions & 6 deletions blueprint/schema/_embed/schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ package schema
// +optional
registries?: [...string] @go(Registries,[]string)

// Secrets contains global secrets that will be passed to all targets.
// +optional
secrets?: [...#Secret] @go(Secrets,[]Secret)

// Tagging contains the tagging configuration for the CI system.
// +optional
tagging?: #Tagging @go(Tagging)
Expand Down Expand Up @@ -134,12 +138,6 @@ package schema

// Secret contains the secret provider and a list of mappings
#Secret: {
// Path contains the path to the secret.
path?: null | string @go(Path,*string)

// Provider contains the provider to use for the secret.
provider?: null | string @go(Provider,*string)

// Maps contains mappings for Earthly secret names to JSON keys in the secret.
// Mutually exclusive with Name.
// +optional
Expand All @@ -151,6 +149,16 @@ package schema
// Mutually exclusive with Maps.
// +optional
name?: null | string @go(Name,*string)

// Optional determines if the secret is optional.
// +optional
optional?: null | bool @go(Optional,*bool)

// Path contains the path to the secret.
path?: null | string @go(Path,*string)

// Provider contains the provider to use for the secret.
provider?: null | string @go(Provider,*string)
}
version: "1.0"
#Tagging: {
Expand Down
20 changes: 14 additions & 6 deletions blueprint/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ type GlobalCI struct {
// +optional
Registries []string `json:"registries"`

// Secrets contains global secrets that will be passed to all targets.
// +optional
Secrets []Secret `json:"secrets"`

// Tagging contains the tagging configuration for the CI system.
// +optional
Tagging Tagging `json:"tagging"`
Expand Down Expand Up @@ -141,12 +145,6 @@ type ProviderGithub struct {

// Secret contains the secret provider and a list of mappings
type Secret struct {
// Path contains the path to the secret.
Path *string `json:"path"`

// Provider contains the provider to use for the secret.
Provider *string `json:"provider"`

// Maps contains mappings for Earthly secret names to JSON keys in the secret.
// Mutually exclusive with Name.
// +optional
Expand All @@ -156,6 +154,16 @@ type Secret struct {
// Mutually exclusive with Maps.
// +optional
Name *string `json:"name"`

// Optional determines if the secret is optional.
// +optional
Optional *bool `json:"optional"`

// Path contains the path to the secret.
Path *string `json:"path"`

// Provider contains the provider to use for the secret.
Provider *string `json:"provider"`
}

type Tagging struct {
Expand Down
20 changes: 14 additions & 6 deletions blueprint/schema/schema_go_gen.cue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ package schema
// +optional
registries?: [...string] @go(Registries,[]string)

// Secrets contains global secrets that will be passed to all targets.
// +optional
secrets?: [...#Secret] @go(Secrets,[]Secret)

// Tagging contains the tagging configuration for the CI system.
// +optional
tagging?: #Tagging @go(Tagging)
Expand Down Expand Up @@ -136,12 +140,6 @@ package schema

// Secret contains the secret provider and a list of mappings
#Secret: {
// Path contains the path to the secret.
path?: null | string @go(Path,*string)

// Provider contains the provider to use for the secret.
provider?: null | string @go(Provider,*string)

// Maps contains mappings for Earthly secret names to JSON keys in the secret.
// Mutually exclusive with Name.
// +optional
Expand All @@ -151,6 +149,16 @@ package schema
// Mutually exclusive with Maps.
// +optional
name?: null | string @go(Name,*string)

// Optional determines if the secret is optional.
// +optional
optional?: null | bool @go(Optional,*bool)

// Path contains the path to the secret.
path?: null | string @go(Path,*string)

// Provider contains the provider to use for the secret.
provider?: null | string @go(Provider,*string)
}

#Tagging: {
Expand Down
4 changes: 4 additions & 0 deletions forge/cli/cmd/cmds/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func generateOpts(target string, flags *RunCmd, config *schema.Blueprint) []eart
if config.Global.CI.Providers.Earthly.Satellite != nil && !flags.Local {
opts = append(opts, earthly.WithSatellite(*config.Global.CI.Providers.Earthly.Satellite))
}

if len(config.Global.CI.Secrets) > 0 {
opts = append(opts, earthly.WithSecrets(config.Global.CI.Secrets))
}
}

if flags != nil {
Expand Down
26 changes: 19 additions & 7 deletions forge/cli/pkg/earthly/earthly.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,16 @@ func (e EarthlyExecutor) Run() (map[string]EarthlyExecutionResult, error) {
return nil, err
}

var secretString []string
for _, secret := range secrets {
e.logger.Info("Adding Earthly secret", "earthly_id", secret.Id, "value", secret.Value)
secretString = append(secretString, fmt.Sprintf("%s=%s", secret.Id, secret.Value))
}
if len(secrets) > 0 {
var secretString []string
for _, secret := range secrets {
e.logger.Info("Adding Earthly secret", "earthly_id", secret.Id, "value", secret.Value)
secretString = append(secretString, fmt.Sprintf("%s=%s", secret.Id, secret.Value))
}

if err := os.Setenv("EARTHLY_SECRETS", strings.Join(secretString, ",")); err != nil {
e.logger.Error("Failed to set secret environment varibles", "envvar", "EARTHLY_SECRETS")
if err := os.Setenv("EARTHLY_SECRETS", strings.Join(secretString, ",")); err != nil {
e.logger.Error("Failed to set secret environment varibles", "envvar", "EARTHLY_SECRETS")
}
}
}

Expand Down Expand Up @@ -149,11 +151,21 @@ func (e *EarthlyExecutor) buildSecrets() ([]EarthlySecret, error) {

s, err := secretClient.Get(*secret.Path)
if err != nil {
if secret.Optional != nil && *secret.Optional {
e.logger.Warn("Secret is optional and not found", "provider", *secret.Provider, "path", *secret.Path)
continue
}

e.logger.Error("Unable to get secret", "provider", secret.Provider, "path", secret.Path, "error", err)
return secrets, fmt.Errorf("unable to get secret %s from provider: %s", *secret.Path, *secret.Provider)
}

if len(secret.Maps) == 0 {
if secret.Name == nil {
e.logger.Error("Secret does not contain name or maps", "provider", secret.Provider, "path", secret.Path)
return nil, fmt.Errorf("secret does not contain name or maps: %s", *secret.Path)
}

secrets = append(secrets, EarthlySecret{
Id: *secret.Name,
Value: s,
Expand Down
19 changes: 19 additions & 0 deletions forge/cli/pkg/earthly/earthly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,25 @@ func TestEarthlyExecutor_buildSecrets(t *testing.T) {
expectErr: false,
expectedErr: "",
},
{
name: "optional secret",
provider: &smocks.SecretProviderMock{
GetFunc: func(path string) (string, error) {
return "", fmt.Errorf("not found")
},
},
secrets: []schema.Secret{
{
Name: utils.StringPtr("name"),
Optional: utils.BoolPtr(true),
Path: utils.StringPtr("path"),
Provider: utils.StringPtr("mock"),
Maps: map[string]string{},
},
},
expect: nil,
expectErr: false,
},
{
name: "name and maps defined",
provider: &smocks.SecretProviderMock{
Expand Down
2 changes: 1 addition & 1 deletion forge/cli/pkg/earthly/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func WithSatellite(s string) EarthlyExecutorOption {
// be passed to the Earthly target.
func WithSecrets(secrets []schema.Secret) EarthlyExecutorOption {
return func(e *EarthlyExecutor) {
e.secrets = secrets
e.secrets = append(e.secrets, secrets...)
}
}

Expand Down

0 comments on commit e88ac64

Please sign in to comment.