private void ConvertInputs( TemplateContext context, TemplateToken inputsToken, ActionDefinitionData actionDefinition) { actionDefinition.Inputs = new MappingToken(null, null, null); var inputsMapping = inputsToken.AssertMapping("inputs"); foreach (var input in inputsMapping) { bool hasDefault = false; var inputName = input.Key.AssertString("input name"); var inputMetadata = input.Value.AssertMapping("input metadata"); foreach (var metadata in inputMetadata) { var metadataName = metadata.Key.AssertString("input metadata").Value; if (string.Equals(metadataName, "default", StringComparison.OrdinalIgnoreCase)) { hasDefault = true; actionDefinition.Inputs.Add(inputName, metadata.Value); } else if (string.Equals(metadataName, "deprecationMessage", StringComparison.OrdinalIgnoreCase)) { if (actionDefinition.Deprecated == null) { actionDefinition.Deprecated = new Dictionary <String, String>(); } var message = metadata.Value.AssertString("input deprecationMessage"); actionDefinition.Deprecated.Add(inputName.Value, message.Value); } } if (!hasDefault) { actionDefinition.Inputs.Add(inputName, new StringToken(null, null, null, string.Empty)); } } }
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile) { var templateContext = CreateContext(executionContext); ActionDefinitionData actionDefinition = new ActionDefinitionData(); // Clean up file name real quick // Instead of using Regex which can be computationally expensive, // we can just remove the # of characters from the fileName according to the length of the basePath string basePath = HostContext.GetDirectory(WellKnownDirectory.Actions); string fileRelativePath = manifestFile; if (manifestFile.Contains(basePath)) { fileRelativePath = manifestFile.Remove(0, basePath.Length + 1); } try { var token = default(TemplateToken); // Get the file ID var fileId = templateContext.GetFileId(fileRelativePath); // Add this file to the FileTable in executionContext if it hasn't been added already // we use > since fileID is 1 indexed if (fileId > executionContext.FileTable.Count) { executionContext.FileTable.Add(fileRelativePath); } // Read the file var fileContent = File.ReadAllText(manifestFile); using (var stringReader = new StringReader(fileContent)) { var yamlObjectReader = new YamlObjectReader(fileId, stringReader); token = TemplateReader.Read(templateContext, "action-root", yamlObjectReader, fileId, out _); } var actionMapping = token.AssertMapping("action manifest root"); var actionOutputs = default(MappingToken); var actionRunValueToken = default(TemplateToken); foreach (var actionPair in actionMapping) { var propertyName = actionPair.Key.AssertString($"action.yml property key"); switch (propertyName.Value) { case "name": actionDefinition.Name = actionPair.Value.AssertString("name").Value; break; case "outputs": if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA"))) { actionOutputs = actionPair.Value.AssertMapping("outputs"); break; } Trace.Info($"Ignore action property outputs. Outputs for a whole action is not supported yet."); break; case "description": actionDefinition.Description = actionPair.Value.AssertString("description").Value; break; case "inputs": ConvertInputs(templateContext, actionPair.Value, actionDefinition); break; case "runs": // Defer runs token evaluation to after for loop to ensure that order of outputs doesn't matter. actionRunValueToken = actionPair.Value; break; default: Trace.Info($"Ignore action property {propertyName}."); break; } } // Evaluate Runs Last if (actionRunValueToken != null) { actionDefinition.Execution = ConvertRuns(executionContext, templateContext, actionRunValueToken, actionOutputs); } } catch (Exception ex) { Trace.Error(ex); templateContext.Errors.Add(ex); } if (templateContext.Errors.Count > 0) { foreach (var error in templateContext.Errors) { Trace.Error($"Action.yml load error: {error.Message}"); executionContext.Error(error.Message); } throw new ArgumentException($"Fail to load {fileRelativePath}"); } if (actionDefinition.Execution == null) { executionContext.Debug($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}"); throw new ArgumentException($"Top level 'runs:' section is required for {fileRelativePath}"); } else { Trace.Info($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}"); } return(actionDefinition); }
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile) { var context = CreateContext(executionContext, null); ActionDefinitionData actionDefinition = new ActionDefinitionData(); try { var token = default(TemplateToken); // Get the file ID var fileId = context.GetFileId(manifestFile); var fileContent = File.ReadAllText(manifestFile); using (var stringReader = new StringReader(fileContent)) { var yamlObjectReader = new YamlObjectReader(null, stringReader); token = TemplateReader.Read(context, "action-root", yamlObjectReader, fileId, out _); } var actionMapping = token.AssertMapping("action manifest root"); foreach (var actionPair in actionMapping) { var propertyName = actionPair.Key.AssertString($"action.yml property key"); switch (propertyName.Value) { case "name": actionDefinition.Name = actionPair.Value.AssertString("name").Value; break; case "description": actionDefinition.Description = actionPair.Value.AssertString("description").Value; break; case "inputs": ConvertInputs(context, actionPair.Value, actionDefinition); break; case "runs": actionDefinition.Execution = ConvertRuns(context, actionPair.Value); break; default: Trace.Info($"Ignore action property {propertyName}."); break; } } } catch (Exception ex) { Trace.Error(ex); context.Errors.Add(ex); } if (context.Errors.Count > 0) { foreach (var error in context.Errors) { Trace.Error($"Action.yml load error: {error.Message}"); executionContext.Error(error.Message); } throw new ArgumentException($"Fail to load {manifestFile}"); } if (actionDefinition.Execution == null) { executionContext.Debug($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}"); throw new ArgumentException($"Top level 'run:' section is required for {manifestFile}"); } else { Trace.Info($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}"); } return(actionDefinition); }
private ActionContainer PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction) { var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference; if (string.Equals(repositoryReference.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)) { Trace.Info($"Repository action is in 'self' repository."); return(null); } var setupInfo = new ActionContainer(); string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref); string actionEntryDirectory = destDirectory; string dockerFileRelativePath = repositoryReference.Name; ArgUtil.NotNull(repositoryReference, nameof(repositoryReference)); if (!string.IsNullOrEmpty(repositoryReference.Path)) { actionEntryDirectory = Path.Combine(destDirectory, repositoryReference.Path); dockerFileRelativePath = $"{dockerFileRelativePath}/{repositoryReference.Path}"; setupInfo.ActionRepository = $"{repositoryReference.Name}/{repositoryReference.Path}@{repositoryReference.Ref}"; } else { setupInfo.ActionRepository = $"{repositoryReference.Name}@{repositoryReference.Ref}"; } // find the docker file or action.yml file var dockerFile = Path.Combine(actionEntryDirectory, "Dockerfile"); var dockerFileLowerCase = Path.Combine(actionEntryDirectory, "dockerfile"); var actionManifest = Path.Combine(actionEntryDirectory, Constants.Path.ActionManifestYmlFile); var actionManifestYaml = Path.Combine(actionEntryDirectory, Constants.Path.ActionManifestYamlFile); if (File.Exists(actionManifest) || File.Exists(actionManifestYaml)) { executionContext.Debug($"action.yml for action: '{actionManifest}'."); var manifestManager = HostContext.GetService <IActionManifestManager>(); ActionDefinitionData actionDefinitionData = null; if (File.Exists(actionManifest)) { actionDefinitionData = manifestManager.Load(executionContext, actionManifest); } else { actionDefinitionData = manifestManager.Load(executionContext, actionManifestYaml); } if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Container) { var containerAction = actionDefinitionData.Execution as ContainerActionExecutionData; if (containerAction.Image.EndsWith("Dockerfile") || containerAction.Image.EndsWith("dockerfile")) { var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image); executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'."); setupInfo.Dockerfile = dockerFileFullPath; setupInfo.WorkingDirectory = destDirectory; return(setupInfo); } else if (containerAction.Image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase)) { var actionImage = containerAction.Image.Substring("docker://".Length); executionContext.Debug($"Container image for action: '{actionImage}'."); setupInfo.Image = actionImage; return(setupInfo); } else { throw new NotSupportedException($"'{containerAction.Image}' should be either '[path]/Dockerfile' or 'docker://image[:tag]'."); } } else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.NodeJS) { Trace.Info($"Action node.js file: {(actionDefinitionData.Execution as NodeJSActionExecutionData).Script}, no more preparation."); return(null); } else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Plugin) { Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation."); return(null); } else { throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString()); } } else if (File.Exists(dockerFile)) { executionContext.Debug($"Dockerfile for action: '{dockerFile}'."); setupInfo.Dockerfile = dockerFile; setupInfo.WorkingDirectory = destDirectory; return(setupInfo); } else if (File.Exists(dockerFileLowerCase)) { executionContext.Debug($"Dockerfile for action: '{dockerFileLowerCase}'."); setupInfo.Dockerfile = dockerFileLowerCase; setupInfo.WorkingDirectory = destDirectory; return(setupInfo); } else { var fullPath = IOUtil.ResolvePath(actionEntryDirectory, "."); // resolve full path without access filesystem. throw new InvalidOperationException($"Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '{fullPath}'. Did you forget to run actions/checkout before running your local action?"); } }