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 }