github.1485827954.workers.dev/nektos/act@v0.2.63/pkg/runner/command.go (about)

     1  package runner
     2  
     3  import (
     4  	"context"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/nektos/act/pkg/common"
     9  )
    10  
    11  var commandPatternGA *regexp.Regexp
    12  var commandPatternADO *regexp.Regexp
    13  
    14  func init() {
    15  	commandPatternGA = regexp.MustCompile("^::([^ ]+)( (.+))?::([^\r\n]*)[\r\n]+$")
    16  	commandPatternADO = regexp.MustCompile("^##\\[([^ ]+)( (.+))?]([^\r\n]*)[\r\n]+$")
    17  }
    18  
    19  func tryParseRawActionCommand(line string) (command string, kvPairs map[string]string, arg string, ok bool) {
    20  	if m := commandPatternGA.FindStringSubmatch(line); m != nil {
    21  		command = m[1]
    22  		kvPairs = parseKeyValuePairs(m[3], ",")
    23  		arg = m[4]
    24  		ok = true
    25  	} else if m := commandPatternADO.FindStringSubmatch(line); m != nil {
    26  		command = m[1]
    27  		kvPairs = parseKeyValuePairs(m[3], ";")
    28  		arg = m[4]
    29  		ok = true
    30  	}
    31  	return
    32  }
    33  
    34  func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler {
    35  	logger := common.Logger(ctx)
    36  	resumeCommand := ""
    37  	return func(line string) bool {
    38  		command, kvPairs, arg, ok := tryParseRawActionCommand(line)
    39  		if !ok {
    40  			return true
    41  		}
    42  
    43  		if resumeCommand != "" && command != resumeCommand {
    44  			logger.Infof("  \U00002699  %s", line)
    45  			return false
    46  		}
    47  		arg = unescapeCommandData(arg)
    48  		kvPairs = unescapeKvPairs(kvPairs)
    49  		switch command {
    50  		case "set-env":
    51  			rc.setEnv(ctx, kvPairs, arg)
    52  		case "set-output":
    53  			rc.setOutput(ctx, kvPairs, arg)
    54  		case "add-path":
    55  			rc.addPath(ctx, arg)
    56  		case "debug":
    57  			logger.Infof("  \U0001F4AC  %s", line)
    58  		case "warning":
    59  			logger.Infof("  \U0001F6A7  %s", line)
    60  		case "error":
    61  			logger.Infof("  \U00002757  %s", line)
    62  		case "add-mask":
    63  			rc.AddMask(arg)
    64  			logger.Infof("  \U00002699  %s", "***")
    65  		case "stop-commands":
    66  			resumeCommand = arg
    67  			logger.Infof("  \U00002699  %s", line)
    68  		case resumeCommand:
    69  			resumeCommand = ""
    70  			logger.Infof("  \U00002699  %s", line)
    71  		case "save-state":
    72  			logger.Infof("  \U0001f4be  %s", line)
    73  			rc.saveState(ctx, kvPairs, arg)
    74  		case "add-matcher":
    75  			logger.Infof("  \U00002753 add-matcher %s", arg)
    76  		default:
    77  			logger.Infof("  \U00002753  %s", line)
    78  		}
    79  
    80  		return false
    81  	}
    82  }
    83  
    84  func (rc *RunContext) setEnv(ctx context.Context, kvPairs map[string]string, arg string) {
    85  	name := kvPairs["name"]
    86  	common.Logger(ctx).Infof("  \U00002699  ::set-env:: %s=%s", name, arg)
    87  	if rc.Env == nil {
    88  		rc.Env = make(map[string]string)
    89  	}
    90  	if rc.GlobalEnv == nil {
    91  		rc.GlobalEnv = map[string]string{}
    92  	}
    93  	newenv := map[string]string{
    94  		name: arg,
    95  	}
    96  	mergeIntoMap := mergeIntoMapCaseSensitive
    97  	if rc.JobContainer != nil && rc.JobContainer.IsEnvironmentCaseInsensitive() {
    98  		mergeIntoMap = mergeIntoMapCaseInsensitive
    99  	}
   100  	mergeIntoMap(rc.Env, newenv)
   101  	mergeIntoMap(rc.GlobalEnv, newenv)
   102  }
   103  func (rc *RunContext) setOutput(ctx context.Context, kvPairs map[string]string, arg string) {
   104  	logger := common.Logger(ctx)
   105  	stepID := rc.CurrentStep
   106  	outputName := kvPairs["name"]
   107  	if outputMapping, ok := rc.OutputMappings[MappableOutput{StepID: stepID, OutputName: outputName}]; ok {
   108  		stepID = outputMapping.StepID
   109  		outputName = outputMapping.OutputName
   110  	}
   111  
   112  	result, ok := rc.StepResults[stepID]
   113  	if !ok {
   114  		logger.Infof("  \U00002757  no outputs used step '%s'", stepID)
   115  		return
   116  	}
   117  
   118  	logger.Infof("  \U00002699  ::set-output:: %s=%s", outputName, arg)
   119  	result.Outputs[outputName] = arg
   120  }
   121  func (rc *RunContext) addPath(ctx context.Context, arg string) {
   122  	common.Logger(ctx).Infof("  \U00002699  ::add-path:: %s", arg)
   123  	extraPath := []string{arg}
   124  	for _, v := range rc.ExtraPath {
   125  		if v != arg {
   126  			extraPath = append(extraPath, v)
   127  		}
   128  	}
   129  	rc.ExtraPath = extraPath
   130  }
   131  
   132  func parseKeyValuePairs(kvPairs string, separator string) map[string]string {
   133  	rtn := make(map[string]string)
   134  	kvPairList := strings.Split(kvPairs, separator)
   135  	for _, kvPair := range kvPairList {
   136  		kv := strings.Split(kvPair, "=")
   137  		if len(kv) == 2 {
   138  			rtn[kv[0]] = kv[1]
   139  		}
   140  	}
   141  	return rtn
   142  }
   143  func unescapeCommandData(arg string) string {
   144  	escapeMap := map[string]string{
   145  		"%25": "%",
   146  		"%0D": "\r",
   147  		"%0A": "\n",
   148  	}
   149  	for k, v := range escapeMap {
   150  		arg = strings.ReplaceAll(arg, k, v)
   151  	}
   152  	return arg
   153  }
   154  func unescapeCommandProperty(arg string) string {
   155  	escapeMap := map[string]string{
   156  		"%25": "%",
   157  		"%0D": "\r",
   158  		"%0A": "\n",
   159  		"%3A": ":",
   160  		"%2C": ",",
   161  	}
   162  	for k, v := range escapeMap {
   163  		arg = strings.ReplaceAll(arg, k, v)
   164  	}
   165  	return arg
   166  }
   167  func unescapeKvPairs(kvPairs map[string]string) map[string]string {
   168  	for k, v := range kvPairs {
   169  		kvPairs[k] = unescapeCommandProperty(v)
   170  	}
   171  	return kvPairs
   172  }
   173  
   174  func (rc *RunContext) saveState(_ context.Context, kvPairs map[string]string, arg string) {
   175  	stepID := rc.CurrentStep
   176  	if stepID != "" {
   177  		if rc.IntraActionState == nil {
   178  			rc.IntraActionState = map[string]map[string]string{}
   179  		}
   180  		state, ok := rc.IntraActionState[stepID]
   181  		if !ok {
   182  			state = map[string]string{}
   183  			rc.IntraActionState[stepID] = state
   184  		}
   185  		state[kvPairs["name"]] = arg
   186  	}
   187  }