@@ -130,6 +130,9 @@ func (c *Config) Exec(client compiler.Engine) error {
130130 }
131131 }
132132
133+ // find all secrets that were not provided
134+ missingSecrets := collectMissingSecrets (_pipeline )
135+
133136 // create current directory path for local mount
134137 mount := fmt .Sprintf ("%s:%s:rw" , base , constants .WorkspaceDefault )
135138
@@ -199,6 +202,9 @@ func (c *Config) Exec(client compiler.Engine) error {
199202 }()
200203
201204 defer func () {
205+ // print any secrets not set to the user
206+ reportMissingSecrets (missingSecrets )
207+
202208 // destroy the build with the executor
203209 err = _executor .DestroyBuild (context .Background ())
204210 if err != nil {
@@ -244,6 +250,96 @@ func (c *Config) Exec(client compiler.Engine) error {
244250 return nil
245251}
246252
253+ // reportMissingSecrets informs the user of any secrets not set.
254+ func reportMissingSecrets (s map [string ]string ) {
255+ if len (s ) > 0 {
256+ logrus .Warn ("the following secrets were not set, use --help to learn how to set them:" )
257+
258+ for step , secret := range s {
259+ logrus .Warnf ("secret %#q not set for step %#q" , secret , step )
260+ }
261+ }
262+ }
263+
264+ // collectMissingSecrets searches a given pipeline for used secrets
265+ // and returns a map of secrets not set in the current environment.
266+ // The map key is is the step, stage+step, or secret name formatted
267+ // to match the local exec log output.
268+ func collectMissingSecrets (p * pipeline.Build ) map [string ]string {
269+ if p == nil {
270+ return make (map [string ]string )
271+ }
272+
273+ secrets := map [string ]string {}
274+
275+ for _ , stage := range p .Stages {
276+ for _ , step := range stage .Steps {
277+ for _ , secret := range step .Secrets {
278+ stepName := formatStepIdentifier (stage .Name , step .Name , false )
279+ secrets [stepName ] = secret .Target
280+ }
281+ }
282+ }
283+
284+ for _ , step := range p .Steps {
285+ for _ , secret := range step .Secrets {
286+ stepName := formatStepIdentifier ("" , step .Name , false )
287+ secrets [stepName ] = secret .Target
288+ }
289+ }
290+
291+ for _ , s := range p .Secrets {
292+ if ! s .Origin .Empty () {
293+ for _ , secret := range s .Origin .Secrets {
294+ stepName := formatStepIdentifier ("" , s .Origin .Name , true )
295+ secrets [stepName ] = secret .Target
296+ }
297+ }
298+ }
299+
300+ for step , secret := range secrets {
301+ // if the secret was supplied, remove it from the map
302+ // we only care about unset secrets
303+ val , exists := os .LookupEnv (secret )
304+ if exists {
305+ delete (secrets , step )
306+
307+ if val == "" {
308+ logrus .Debugf ("secret %#q for step %#q is provided but empty" , secret , step )
309+ }
310+ }
311+ }
312+
313+ return secrets
314+ }
315+
316+ // formatStepIdentifier formats a step name to be consistent with what
317+ // the worker logs to make it easier to associate a missing secret
318+ // with a step.
319+ func formatStepIdentifier (stageName , stepName string , isSecret bool ) string {
320+ const (
321+ secretPrefix = "[secret: %s]" //nolint:gosec // false positive
322+ stagePrefix = "[stage: %s]"
323+ stepPrefix = "[step: %s]"
324+ )
325+
326+ output := strings.Builder {}
327+
328+ if stageName != "" {
329+ output .WriteString (fmt .Sprintf (stagePrefix , stageName ))
330+ }
331+
332+ if stepName != "" {
333+ if isSecret {
334+ output .WriteString (fmt .Sprintf (secretPrefix , stepName ))
335+ } else {
336+ output .WriteString (fmt .Sprintf (stepPrefix , stepName ))
337+ }
338+ }
339+
340+ return output .String ()
341+ }
342+
247343// skipSteps filters out steps to be removed from the pipeline.
248344func skipSteps (_pipeline * pipeline.Build , stepsToRemove []string ) error {
249345 // filter out steps to be removed
0 commit comments