Exemple #1
0
        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));
                }
            }
        }
Exemple #2
0
        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?");
            }
        }