UserFromDockerfile inspects the contents of a provided Dockerfile and returns the user that will be used to run the container.
(dockerfileContent string, buildArgs map[string]string)
| 320 | // UserFromDockerfile inspects the contents of a provided Dockerfile |
| 321 | // and returns the user that will be used to run the container. |
| 322 | func UserFromDockerfile(dockerfileContent string, buildArgs map[string]string) (user string, err error) { |
| 323 | res, err := parser.Parse(strings.NewReader(dockerfileContent)) |
| 324 | if err != nil { |
| 325 | return "", fmt.Errorf("parse dockerfile: %w", err) |
| 326 | } |
| 327 | |
| 328 | // Collect ARG values (defaults + overrides from buildArgs) for |
| 329 | // substitution into FROM image refs. |
| 330 | lexer := shell.NewLex('\\') |
| 331 | var argEnvs []string |
| 332 | for _, child := range res.AST.Children { |
| 333 | if !strings.EqualFold(child.Value, "arg") || child.Next == nil { |
| 334 | continue |
| 335 | } |
| 336 | if key, val, ok := strings.Cut(child.Next.Value, "="); ok { |
| 337 | if override, has := buildArgs[key]; has { |
| 338 | val = override |
| 339 | } |
| 340 | argEnvs = append(argEnvs, key+"="+val) |
| 341 | } else { |
| 342 | arg := child.Next.Value |
| 343 | if val, has := buildArgs[arg]; has { |
| 344 | argEnvs = append(argEnvs, arg+"="+val) |
| 345 | } |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | // Parse stages and user commands to determine the relevant user |
| 350 | // from the final stage. |
| 351 | var ( |
| 352 | stages []*instructions.Stage |
| 353 | stageNames = make(map[string]*instructions.Stage) |
| 354 | stageUser = make(map[*instructions.Stage]*instructions.UserCommand) |
| 355 | currentStage *instructions.Stage |
| 356 | ) |
| 357 | for _, child := range res.AST.Children { |
| 358 | inst, err := instructions.ParseInstruction(child) |
| 359 | if err != nil { |
| 360 | return "", fmt.Errorf("parse instruction: %w", err) |
| 361 | } |
| 362 | |
| 363 | switch i := inst.(type) { |
| 364 | case *instructions.Stage: |
| 365 | // Substitute ARG values in the base image name. |
| 366 | baseName, _, err := lexer.ProcessWord(i.BaseName, shell.EnvsFromSlice(argEnvs)) |
| 367 | if err != nil { |
| 368 | return "", fmt.Errorf("processing ARG substitution in FROM %q: %w", i.BaseName, err) |
| 369 | } |
| 370 | i.BaseName = baseName |
| 371 | stages = append(stages, i) |
| 372 | if i.Name != "" { |
| 373 | stageNames[i.Name] = i |
| 374 | } |
| 375 | currentStage = i |
| 376 | case *instructions.UserCommand: |
| 377 | if currentStage == nil { |
| 378 | continue |
| 379 | } |