2018-06-02 00:30:18 +00:00
|
|
|
package instructions
|
2017-05-22 15:21:17 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/docker/api/types/strslice"
|
2020-04-22 05:56:14 +00:00
|
|
|
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
2020-04-19 05:17:47 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-05-22 15:21:17 +00:00
|
|
|
)
|
|
|
|
|
2018-02-10 11:27:14 +00:00
|
|
|
// KeyValuePair represent an arbitrary named value (useful in slice instead of map[string] string to preserve ordering)
|
2017-05-22 15:21:17 +00:00
|
|
|
type KeyValuePair struct {
|
|
|
|
Key string
|
|
|
|
Value string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kvp *KeyValuePair) String() string {
|
|
|
|
return kvp.Key + "=" + kvp.Value
|
|
|
|
}
|
|
|
|
|
2018-07-04 10:54:40 +00:00
|
|
|
// KeyValuePairOptional is the same as KeyValuePair but Value is optional
|
|
|
|
type KeyValuePairOptional struct {
|
2020-09-22 03:54:27 +00:00
|
|
|
Key string
|
|
|
|
Value *string
|
|
|
|
Comment string
|
2018-07-04 10:54:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (kvpo *KeyValuePairOptional) ValueString() string {
|
|
|
|
v := ""
|
|
|
|
if kvpo.Value != nil {
|
|
|
|
v = *kvpo.Value
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
// Command is implemented by every command present in a dockerfile
|
|
|
|
type Command interface {
|
|
|
|
Name() string
|
2020-04-22 05:56:14 +00:00
|
|
|
Location() []parser.Range
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// KeyValuePairs is a slice of KeyValuePair
|
|
|
|
type KeyValuePairs []KeyValuePair
|
|
|
|
|
|
|
|
// withNameAndCode is the base of every command in a Dockerfile (String() returns its source code)
|
|
|
|
type withNameAndCode struct {
|
2020-04-22 05:56:14 +00:00
|
|
|
code string
|
|
|
|
name string
|
|
|
|
location []parser.Range
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *withNameAndCode) String() string {
|
|
|
|
return c.code
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name of the command
|
|
|
|
func (c *withNameAndCode) Name() string {
|
|
|
|
return c.name
|
|
|
|
}
|
|
|
|
|
2020-04-22 05:56:14 +00:00
|
|
|
// Location of the command in source
|
|
|
|
func (c *withNameAndCode) Location() []parser.Range {
|
|
|
|
return c.location
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
func newWithNameAndCode(req parseRequest) withNameAndCode {
|
2020-04-22 05:56:14 +00:00
|
|
|
return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command, location: req.location}
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SingleWordExpander is a provider for variable expansion where 1 word => 1 output
|
|
|
|
type SingleWordExpander func(word string) (string, error)
|
|
|
|
|
Fix heredoc COPY/ADD expansion to preserve quotes
In the contents of COPY/ADD, we perform expansion on variables using a
lexer. However, this lexer, by default, removes quotes as well as
expanding variables - this isn't really the kind of behavior we're
after, as it feels quite unintuitive.
To fix this, we introduce a new ExpandRaw function, which commands can
implement that implement an alternative expansion that preserves quotes
(and possibly other characters/features in the future).
Additionally, we introduce new tests to more clearly define the desired
behavior. One major note is that backslashes are not passed directly,
and are processed, following normal escape rules (so that we can use `$`
symbols directly).
Signed-off-by: Justin Chadwell <me@jedevc.com>
2021-11-01 20:36:00 +00:00
|
|
|
// SupportsSingleWordExpansion interface marks a command as supporting variable
|
|
|
|
// expansion
|
2017-05-22 15:21:17 +00:00
|
|
|
type SupportsSingleWordExpansion interface {
|
|
|
|
Expand(expander SingleWordExpander) error
|
|
|
|
}
|
|
|
|
|
Fix heredoc COPY/ADD expansion to preserve quotes
In the contents of COPY/ADD, we perform expansion on variables using a
lexer. However, this lexer, by default, removes quotes as well as
expanding variables - this isn't really the kind of behavior we're
after, as it feels quite unintuitive.
To fix this, we introduce a new ExpandRaw function, which commands can
implement that implement an alternative expansion that preserves quotes
(and possibly other characters/features in the future).
Additionally, we introduce new tests to more clearly define the desired
behavior. One major note is that backslashes are not passed directly,
and are processed, following normal escape rules (so that we can use `$`
symbols directly).
Signed-off-by: Justin Chadwell <me@jedevc.com>
2021-11-01 20:36:00 +00:00
|
|
|
// SupportsSingleWordExpansionRaw interface marks a command as supporting
|
|
|
|
// variable expansion, while ensuring that quotes are preserved
|
|
|
|
type SupportsSingleWordExpansionRaw interface {
|
|
|
|
ExpandRaw(expander SingleWordExpander) error
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
// PlatformSpecific adds platform checks to a command
|
|
|
|
type PlatformSpecific interface {
|
|
|
|
CheckPlatform(platform string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandKvp(kvp KeyValuePair, expander SingleWordExpander) (KeyValuePair, error) {
|
|
|
|
key, err := expander(kvp.Key)
|
|
|
|
if err != nil {
|
|
|
|
return KeyValuePair{}, err
|
|
|
|
}
|
|
|
|
value, err := expander(kvp.Value)
|
|
|
|
if err != nil {
|
|
|
|
return KeyValuePair{}, err
|
|
|
|
}
|
|
|
|
return KeyValuePair{Key: key, Value: value}, nil
|
|
|
|
}
|
|
|
|
func expandKvpsInPlace(kvps KeyValuePairs, expander SingleWordExpander) error {
|
|
|
|
for i, kvp := range kvps {
|
|
|
|
newKvp, err := expandKvp(kvp, expander)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
kvps[i] = newKvp
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandSliceInPlace(values []string, expander SingleWordExpander) error {
|
|
|
|
for i, v := range values {
|
|
|
|
newValue, err := expander(v)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
values[i] = newValue
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnvCommand : ENV key1 value1 [keyN valueN...]
|
|
|
|
type EnvCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Env KeyValuePairs // kvp slice instead of map to preserve ordering
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *EnvCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
return expandKvpsInPlace(c.Env, expander)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaintainerCommand : MAINTAINER maintainer_name
|
|
|
|
type MaintainerCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Maintainer string
|
|
|
|
}
|
|
|
|
|
2018-05-07 05:28:55 +00:00
|
|
|
// NewLabelCommand creates a new 'LABEL' command
|
|
|
|
func NewLabelCommand(k string, v string, NoExp bool) *LabelCommand {
|
|
|
|
kvp := KeyValuePair{Key: k, Value: v}
|
|
|
|
c := "LABEL "
|
|
|
|
c += kvp.String()
|
|
|
|
nc := withNameAndCode{code: c, name: "label"}
|
|
|
|
cmd := &LabelCommand{
|
|
|
|
withNameAndCode: nc,
|
|
|
|
Labels: KeyValuePairs{
|
|
|
|
kvp,
|
|
|
|
},
|
|
|
|
noExpand: NoExp,
|
|
|
|
}
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
// LabelCommand : LABEL some json data describing the image
|
|
|
|
//
|
|
|
|
// Sets the Label variable foo to bar,
|
|
|
|
//
|
|
|
|
type LabelCommand struct {
|
|
|
|
withNameAndCode
|
2018-05-07 05:28:55 +00:00
|
|
|
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
|
|
|
noExpand bool
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *LabelCommand) Expand(expander SingleWordExpander) error {
|
2018-05-07 05:28:55 +00:00
|
|
|
if c.noExpand {
|
|
|
|
return nil
|
|
|
|
}
|
2017-05-22 15:21:17 +00:00
|
|
|
return expandKvpsInPlace(c.Labels, expander)
|
|
|
|
}
|
|
|
|
|
2021-06-09 09:50:31 +00:00
|
|
|
// SourceContent represents an anonymous file object
|
|
|
|
type SourceContent struct {
|
|
|
|
Path string
|
|
|
|
Data string
|
|
|
|
Expand bool
|
|
|
|
}
|
2017-05-22 15:21:17 +00:00
|
|
|
|
2021-06-09 09:50:31 +00:00
|
|
|
// SourcesAndDest represent a collection of sources and a destination
|
|
|
|
type SourcesAndDest struct {
|
|
|
|
DestPath string
|
|
|
|
SourcePaths []string
|
|
|
|
SourceContents []SourceContent
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
2021-06-09 09:50:31 +00:00
|
|
|
func (s *SourcesAndDest) Expand(expander SingleWordExpander) error {
|
|
|
|
err := expandSliceInPlace(s.SourcePaths, expander)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
expandedDestPath, err := expander(s.DestPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.DestPath = expandedDestPath
|
|
|
|
|
|
|
|
return nil
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
Fix heredoc COPY/ADD expansion to preserve quotes
In the contents of COPY/ADD, we perform expansion on variables using a
lexer. However, this lexer, by default, removes quotes as well as
expanding variables - this isn't really the kind of behavior we're
after, as it feels quite unintuitive.
To fix this, we introduce a new ExpandRaw function, which commands can
implement that implement an alternative expansion that preserves quotes
(and possibly other characters/features in the future).
Additionally, we introduce new tests to more clearly define the desired
behavior. One major note is that backslashes are not passed directly,
and are processed, following normal escape rules (so that we can use `$`
symbols directly).
Signed-off-by: Justin Chadwell <me@jedevc.com>
2021-11-01 20:36:00 +00:00
|
|
|
func (s *SourcesAndDest) ExpandRaw(expander SingleWordExpander) error {
|
|
|
|
for i, content := range s.SourceContents {
|
|
|
|
if !content.Expand {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
expandedData, err := expander(content.Data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.SourceContents[i].Data = expandedData
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
// AddCommand : ADD foo /path
|
|
|
|
//
|
2018-07-03 00:24:21 +00:00
|
|
|
// Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
|
2017-05-22 15:21:17 +00:00
|
|
|
// exist here. If you do not wish to have this automatic handling, use COPY.
|
|
|
|
//
|
|
|
|
type AddCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
SourcesAndDest
|
|
|
|
Chown string
|
2020-05-04 07:39:38 +00:00
|
|
|
Chmod string
|
2021-10-22 06:46:55 +00:00
|
|
|
Link bool
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *AddCommand) Expand(expander SingleWordExpander) error {
|
2020-05-01 23:44:35 +00:00
|
|
|
expandedChown, err := expander(c.Chown)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Chown = expandedChown
|
2021-06-09 09:50:31 +00:00
|
|
|
|
|
|
|
return c.SourcesAndDest.Expand(expander)
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CopyCommand : COPY foo /path
|
|
|
|
//
|
|
|
|
// Same as 'ADD' but without the tar and remote url handling.
|
|
|
|
//
|
|
|
|
type CopyCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
SourcesAndDest
|
|
|
|
From string
|
|
|
|
Chown string
|
2020-05-04 07:39:38 +00:00
|
|
|
Chmod string
|
2021-10-22 06:46:55 +00:00
|
|
|
Link bool
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *CopyCommand) Expand(expander SingleWordExpander) error {
|
2019-04-06 08:53:05 +00:00
|
|
|
expandedChown, err := expander(c.Chown)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Chown = expandedChown
|
2021-06-09 09:50:31 +00:00
|
|
|
|
|
|
|
return c.SourcesAndDest.Expand(expander)
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// OnbuildCommand : ONBUILD <some other command>
|
|
|
|
type OnbuildCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Expression string
|
|
|
|
}
|
|
|
|
|
|
|
|
// WorkdirCommand : WORKDIR /tmp
|
|
|
|
//
|
|
|
|
// Set the working directory for future RUN/CMD/etc statements.
|
|
|
|
//
|
|
|
|
type WorkdirCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Path string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *WorkdirCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
p, err := expander(c.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Path = p
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-09 09:50:31 +00:00
|
|
|
// ShellInlineFile represents an inline file created for a shell command
|
|
|
|
type ShellInlineFile struct {
|
|
|
|
Name string
|
|
|
|
Data string
|
|
|
|
Chomp bool
|
|
|
|
}
|
|
|
|
|
2018-02-10 11:27:14 +00:00
|
|
|
// ShellDependantCmdLine represents a cmdline optionally prepended with the shell
|
2017-05-22 15:21:17 +00:00
|
|
|
type ShellDependantCmdLine struct {
|
|
|
|
CmdLine strslice.StrSlice
|
2021-06-09 09:50:31 +00:00
|
|
|
Files []ShellInlineFile
|
2017-05-22 15:21:17 +00:00
|
|
|
PrependShell bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// RunCommand : RUN some command yo
|
|
|
|
//
|
|
|
|
// run a command and commit the image. Args are automatically prepended with
|
|
|
|
// the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
|
|
|
|
// Windows, in the event there is only one argument The difference in processing:
|
|
|
|
//
|
|
|
|
// RUN echo hi # sh -c echo hi (Linux)
|
|
|
|
// RUN echo hi # cmd /S /C echo hi (Windows)
|
|
|
|
// RUN [ "echo", "hi" ] # echo hi
|
|
|
|
//
|
|
|
|
type RunCommand struct {
|
|
|
|
withNameAndCode
|
2018-06-06 23:30:51 +00:00
|
|
|
withExternalData
|
2017-05-22 15:21:17 +00:00
|
|
|
ShellDependantCmdLine
|
2021-01-26 15:48:58 +00:00
|
|
|
FlagsUsed []string
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
2021-04-26 18:30:23 +00:00
|
|
|
func (c *RunCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
if err := setMountState(c, expander); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:21:17 +00:00
|
|
|
// CmdCommand : CMD foo
|
|
|
|
//
|
|
|
|
// Set the default command to run in the container (which may be empty).
|
|
|
|
// Argument handling is the same as RUN.
|
|
|
|
//
|
|
|
|
type CmdCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
ShellDependantCmdLine
|
|
|
|
}
|
|
|
|
|
|
|
|
// HealthCheckCommand : HEALTHCHECK foo
|
|
|
|
//
|
|
|
|
// Set the default healthcheck command to run in the container (which may be empty).
|
|
|
|
// Argument handling is the same as RUN.
|
|
|
|
//
|
|
|
|
type HealthCheckCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Health *container.HealthConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// EntrypointCommand : ENTRYPOINT /usr/sbin/nginx
|
|
|
|
//
|
|
|
|
// Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
|
|
|
|
// to /usr/sbin/nginx. Uses the default shell if not in JSON format.
|
|
|
|
//
|
|
|
|
// Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
|
|
|
|
// is initialized at newBuilder time instead of through argument parsing.
|
|
|
|
//
|
|
|
|
type EntrypointCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
ShellDependantCmdLine
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExposeCommand : EXPOSE 6667/tcp 7000/tcp
|
|
|
|
//
|
|
|
|
// Expose ports for links and port mappings. This all ends up in
|
|
|
|
// req.runConfig.ExposedPorts for runconfig.
|
|
|
|
//
|
|
|
|
type ExposeCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Ports []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// UserCommand : USER foo
|
|
|
|
//
|
|
|
|
// Set the user to 'foo' for future commands and when running the
|
|
|
|
// ENTRYPOINT/CMD at container run time.
|
|
|
|
//
|
|
|
|
type UserCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
User string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *UserCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
p, err := expander(c.User)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.User = p
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VolumeCommand : VOLUME /foo
|
|
|
|
//
|
|
|
|
// Expose the volume /foo for use. Will also accept the JSON array form.
|
|
|
|
//
|
|
|
|
type VolumeCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Volumes []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *VolumeCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
return expandSliceInPlace(c.Volumes, expander)
|
|
|
|
}
|
|
|
|
|
|
|
|
// StopSignalCommand : STOPSIGNAL signal
|
|
|
|
//
|
|
|
|
// Set the signal that will be used to kill the container.
|
|
|
|
type StopSignalCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Signal string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *StopSignalCommand) Expand(expander SingleWordExpander) error {
|
|
|
|
p, err := expander(c.Signal)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Signal = p
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckPlatform checks that the command is supported in the target platform
|
|
|
|
func (c *StopSignalCommand) CheckPlatform(platform string) error {
|
|
|
|
if platform == "windows" {
|
|
|
|
return errors.New("The daemon on this platform does not support the command stopsignal")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ArgCommand : ARG name[=value]
|
|
|
|
//
|
|
|
|
// Adds the variable foo to the trusted list of variables that can be passed
|
|
|
|
// to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
|
|
|
|
// Dockerfile author may optionally set a default value of this variable.
|
|
|
|
type ArgCommand struct {
|
|
|
|
withNameAndCode
|
2020-09-22 01:32:43 +00:00
|
|
|
Args []KeyValuePairOptional
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expand variables
|
|
|
|
func (c *ArgCommand) Expand(expander SingleWordExpander) error {
|
2020-09-22 01:32:43 +00:00
|
|
|
for i, v := range c.Args {
|
|
|
|
p, err := expander(v.Key)
|
2017-05-22 15:21:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-09-22 01:32:43 +00:00
|
|
|
v.Key = p
|
|
|
|
if v.Value != nil {
|
|
|
|
p, err = expander(*v.Value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
v.Value = &p
|
|
|
|
}
|
|
|
|
c.Args[i] = v
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ShellCommand : SHELL powershell -command
|
|
|
|
//
|
|
|
|
// Set the non-default shell to use.
|
|
|
|
type ShellCommand struct {
|
|
|
|
withNameAndCode
|
|
|
|
Shell strslice.StrSlice
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stage represents a single stage in a multi-stage build
|
|
|
|
type Stage struct {
|
2018-02-23 21:03:49 +00:00
|
|
|
Name string
|
|
|
|
Commands []Command
|
|
|
|
BaseName string
|
|
|
|
SourceCode string
|
2018-06-25 02:32:13 +00:00
|
|
|
Platform string
|
2020-04-22 05:56:14 +00:00
|
|
|
Location []parser.Range
|
2020-09-22 03:54:27 +00:00
|
|
|
Comment string
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddCommand to the stage
|
|
|
|
func (s *Stage) AddCommand(cmd Command) {
|
|
|
|
// todo: validate cmd type
|
|
|
|
s.Commands = append(s.Commands, cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsCurrentStage check if the stage name is the current stage
|
|
|
|
func IsCurrentStage(s []Stage, name string) bool {
|
|
|
|
if len(s) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return s[len(s)-1].Name == name
|
|
|
|
}
|
|
|
|
|
|
|
|
// CurrentStage return the last stage in a slice
|
|
|
|
func CurrentStage(s []Stage) (*Stage, error) {
|
|
|
|
if len(s) == 0 {
|
2020-04-20 03:54:38 +00:00
|
|
|
return nil, errors.New("no build stage in current context")
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
return &s[len(s)-1], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasStage looks for the presence of a given stage name
|
|
|
|
func HasStage(s []Stage, name string) (int, bool) {
|
|
|
|
for i, stage := range s {
|
2018-04-27 10:40:59 +00:00
|
|
|
// Stage name is case-insensitive by design
|
|
|
|
if strings.EqualFold(stage.Name, name) {
|
2017-05-22 15:21:17 +00:00
|
|
|
return i, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1, false
|
|
|
|
}
|
2018-06-06 23:30:51 +00:00
|
|
|
|
|
|
|
type withExternalData struct {
|
|
|
|
m map[interface{}]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *withExternalData) getExternalValue(k interface{}) interface{} {
|
|
|
|
return c.m[k]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *withExternalData) setExternalValue(k, v interface{}) {
|
|
|
|
if c.m == nil {
|
|
|
|
c.m = map[interface{}]interface{}{}
|
|
|
|
}
|
|
|
|
c.m[k] = v
|
|
|
|
}
|