using System; using System.Collections.Generic; using System.IO; using System.Text; using ln.build.commands; using ln.json; using ln.logging; namespace ln.build.pipeline { public class DefaultPipeLine { public CommandEnvironment CommandEnvironment { get; } public CIService CIService { get; } List stages = new List(); public IEnumerable Stages => stages; public DefaultPipeLine(CIService ciService) { CIService = ciService; CommandEnvironment = new CommandEnvironment(); } public DefaultPipeLine(CIService ciService, CommandEnvironment commandEnvironment) { CIService = ciService; CommandEnvironment = new CommandEnvironment(commandEnvironment); } public void LoadJson(JSONObject jsonPipeLine) { if (jsonPipeLine.ContainsKey("env")) CommandEnvironment.Apply(jsonPipeLine["env"] as JSONObject); if (jsonPipeLine.ContainsKey("stages")) { JSONArray jsonStages = jsonPipeLine["stages"] as JSONArray; foreach (JSONObject jsonStage in jsonStages.Children) { Stage stage = new Stage(this); stage.LoadJson(jsonStage); stages.Add(stage); } } } public void Run() { foreach (Stage stage in stages) { CommandEnvironment.Logger.Log(LogLevel.INFO,"-------------------------------------------------------------------------------------"); CommandEnvironment.Logger.Log(LogLevel.INFO,"STAGE: {0}", stage.Name); CommandEnvironment.Logger.Log(LogLevel.INFO,"-------------------------------------------------------------------------------------"); stage.Run(); } } } public class Stage { public DefaultPipeLine PipeLine { get; } public string Name { get; set; } public CommandEnvironment CommandEnvironment { get; } List commands = new List(); public IEnumerable Commands => commands; public Stage(DefaultPipeLine pipeLine) { PipeLine = pipeLine; CommandEnvironment = new CommandEnvironment(pipeLine.CommandEnvironment); } public void LoadJson(JSONObject jsonStage) { Name = jsonStage["name"].ToNative().ToString(); if (jsonStage.ContainsKey("env")) { CommandEnvironment.Apply(jsonStage["env"] as JSONObject); } if (jsonStage.ContainsKey("commands")) { JSONArray jsonCommands = jsonStage["commands"] as JSONArray; foreach (JSONValue jsonValue in jsonCommands.Children) { commands.Add(StageCommand.Create(jsonValue.ToNative().ToString())); } } if (jsonStage.ContainsKey("secrets")) { JSONObject jsonSecrets = jsonStage["secrets"] as JSONObject; foreach (string key in jsonSecrets.Keys) { CommandEnvironment.Set(key, CommandEnvironment.SecretStorage?.GetSecret(jsonSecrets[key]?.ToNative()?.ToString())); } } } public void Run() { foreach (StageCommand command in commands) command.Run(this); } } public abstract class StageCommand { public string Name { get; } public StageCommand(string name) { Name = name; } public abstract void Run(Stage stage); static Dictionary> commandFactories = new Dictionary>(); public static StageCommand Create(string cmdline) { string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2); if (commandFactories.TryGetValue(tokens[0],out Func factory)) { return factory(tokens[1]); } throw new Exception(string.Format("can't find factory for command keyword '{0}'", tokens[0])); } static StageCommand() { commandFactories.Add("SH", (args) => new ShellCommand(args)); commandFactories.Add("DOTNET", (args) => new DotNetCommand(args)); commandFactories.Add("GITEA", (args) => new DotNetCommand(args)); } } public class ShellCommand : StageCommand { public event CommandExitedDelegate OnCommandExited; public CommandRunner CommandRunner { get; } public ShellCommand(string filename,params CommandRunner.Argument[] arguments) :base("SH") { CommandRunner = new CommandRunner("/bin/bash", "-c"){ Throw = CRThrow.NEVER, }; CommandRunner.AddArguments(arguments); } public ShellCommand(string cmdline) :base("SH") { CommandRunner = new CommandRunner("/bin/bash", "-c", string.Format("\"{0}\"",cmdline)){ Throw = CRThrow.NEVER, }; } public override void Run(Stage stage) { MemoryStream logStream = new MemoryStream(); int result = CommandRunner.Run(stage.CommandEnvironment, logStream); stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command output:\n{0}", Encoding.UTF8.GetString(logStream.ToArray())); stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command exit code: {0}", result); if (result != 0) throw new Exception(String.Format("command exited with code {0} [0x{0:x}]", result)); } } }