Compare commits
20 Commits
Author | SHA1 | Date |
---|---|---|
Harald Wolff | 1e49ba6247 | |
Harald Wolff | e2a6124707 | |
Harald Wolff | 833890438f | |
Harald Wolff | 353397e523 | |
Harald Wolff | 65d64c2484 | |
Harald Wolff | 440c6af85d | |
Harald Wolff | 1b922b8096 | |
Harald Wolff | 0e17216cc9 | |
Harald Wolff | 3845bbd4c4 | |
Harald Wolff | b770183fef | |
Harald Wolff | 73994ac366 | |
Harald Wolff | 5f265a41d9 | |
Harald Wolff | b6cde40c20 | |
Harald Wolff | 654ae4156e | |
Harald Wolff | 1b9ed4e7ad | |
Harald Wolff | 18e7a3a229 | |
Harald Wolff | ff4068c718 | |
Harald Wolff | 2e48a0ac99 | |
Harald Wolff | dbc1ed61f8 | |
Harald Wolff | c87831ca00 |
43
build.ln
43
build.ln
|
@ -1,50 +1,17 @@
|
||||||
{
|
{
|
||||||
|
"templates": [
|
||||||
|
"dotnet"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"NUGET_SOURCE": "https://nexus.niclas-thobaben.de/repository/l--n.de",
|
"NUGET_SOURCE": "https://nexus.niclas-thobaben.de/repository/l--n.de/",
|
||||||
"CONFIGURATION": "Release"
|
"CONFIGURATION": "Release"
|
||||||
},
|
},
|
||||||
"stages": [
|
"stages": [
|
||||||
{
|
|
||||||
"name": "setup",
|
|
||||||
"env": {
|
|
||||||
"SOME_ENV_VAR": "Some text",
|
|
||||||
},
|
|
||||||
"commands": [
|
|
||||||
"SH echo Setting up build environment",
|
|
||||||
"SH set",
|
|
||||||
"SH rm -Rf .build"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "prepare",
|
"name": "prepare",
|
||||||
"commands": [
|
"commands": [
|
||||||
"SH dotnet restore",
|
"dotnet prepare */*.csproj"
|
||||||
"SH dotnet clean"
|
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "build",
|
|
||||||
"commands": [
|
|
||||||
"SH dotnet build -c $CONFIGURATION"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pack_and_publish",
|
|
||||||
"commands": [
|
|
||||||
"SH dotnet pack ln.build -o .build -c $CONFIGURATION",
|
|
||||||
"SH dotnet pack ln.build.server -o .build -c $CONFIGURATION",
|
|
||||||
"SH dotnet publish ln.build.server -p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRun=true --self-contained -r linux-x64 -c $CONFIGURATION -o .build/linux-x64"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "push",
|
|
||||||
"commands": [
|
|
||||||
"SH for NUPKG in .build/ln.build.*.nupkg; do dotnet nuget push $NUPKG -s $NUGET_SOURCE -k $NUGET_APIKEY; done",
|
|
||||||
"RELEASE .build/linux-x64/ln.build.server=ln.build.server-linux-amd64"
|
|
||||||
],
|
|
||||||
"secrets": {
|
|
||||||
"NUGET_APIKEY": "https://nexus.niclas-thobaben.de"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -12,18 +12,41 @@ using Microsoft.VisualBasic;
|
||||||
using ln.application;
|
using ln.application;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.build.repositories.gitea;
|
using ln.build.repositories.gitea;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using ln.build.semver;
|
||||||
|
using ln.build.pipeline;
|
||||||
|
|
||||||
namespace ln.build.server
|
namespace ln.build.server
|
||||||
{
|
{
|
||||||
|
public enum RunMode {
|
||||||
|
serv,
|
||||||
|
versioning,
|
||||||
|
build
|
||||||
|
}
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static CIService CIService;
|
static CIService CIService;
|
||||||
|
|
||||||
|
[StaticArgument( Option = 'm')]
|
||||||
|
static RunMode RunMode { get; set; } = RunMode.serv;
|
||||||
|
|
||||||
[StaticArgument( LongOption = "build")]
|
[StaticArgument( LongOption = "build")]
|
||||||
public static string BuildPath { get; set; }
|
static string BuildPath { get; set; }
|
||||||
|
|
||||||
[StaticArgument( LongOption = "build-secret")]
|
[StaticArgument( LongOption = "build-secret")]
|
||||||
public static string BuildSecret { get; set; }
|
static string BuildSecret { get; set; }
|
||||||
|
|
||||||
|
[StaticArgument( LongOption = "versioning-provider")]
|
||||||
|
static string VersioningProviderName { get; set; }
|
||||||
|
|
||||||
|
[StaticArgument( LongOption = "versioning-level")]
|
||||||
|
static SemVerLevels VersioningLevel { get; set;} = SemVerLevels.PATCH;
|
||||||
|
|
||||||
|
[StaticArgument( LongOption = "versioning-source")]
|
||||||
|
static string VersioningSource { get; set;}
|
||||||
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
@ -35,23 +58,42 @@ namespace ln.build.server
|
||||||
|
|
||||||
CIService.Initialize();
|
CIService.Initialize();
|
||||||
|
|
||||||
if (BuildPath != null)
|
switch (RunMode)
|
||||||
{
|
{
|
||||||
CIJob job = new CIJob(CIService,null, (BuildSecret != null) ? CIService.GetSecretStorage(BuildSecret) : null);
|
case RunMode.serv:
|
||||||
job.WorkingDirectory = BuildPath;
|
CIService.AddWebHookHandler("gitea", GiteaRepository.WebHookHandler);
|
||||||
job.RunJob();
|
CIService.Start();
|
||||||
} else {
|
break;
|
||||||
CIService.Initialize();
|
case RunMode.build:
|
||||||
CIService.AddWebHookHandler("gitea", GiteaRepository.WebHookHandler);
|
CIJob job = new CIJob(CIService, null, (BuildSecret != null) ? CIService.GetSecretStorage(BuildSecret) : null);
|
||||||
|
|
||||||
// (new GiteaRepository("https://git.l--n.de"){ AuthorizationToken = "1d03e9577c404b5b4f46b340147b1d500ff95b2e", });
|
job.WorkingDirectory = BuildPath;
|
||||||
|
job.Environment.WorkingDirectory = BuildPath;
|
||||||
|
|
||||||
CIService.Start();
|
job.RunJob();
|
||||||
|
break;
|
||||||
|
case RunMode.versioning:
|
||||||
|
Versioning versioning = new Versioning(VersioningProviderName);
|
||||||
|
versioning.Sources = new string[]{ VersioningSource };
|
||||||
|
|
||||||
|
SemVersion version = versioning.GetCurrentVersion(null);
|
||||||
|
Logging.Log("INFO: found version {0}", version);
|
||||||
|
version.Increment(VersioningLevel);
|
||||||
|
Logging.Log("INFO: write back version {0}", version);
|
||||||
|
versioning.SetVersion(null, version);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.UseSystemd()
|
||||||
|
.ConfigureServices((hostContext, services) =>
|
||||||
|
{
|
||||||
|
services.AddHostedService<ServiceWorker>();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace ln.build
|
||||||
|
{
|
||||||
|
public class ServiceWorker : BackgroundService
|
||||||
|
{
|
||||||
|
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,32 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
<PackageVersion>0.4.4</PackageVersion>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<Version>0.4.0</Version>
|
<PropertyGroup>
|
||||||
<Authors>Harald Wolff-Thobaben</Authors>
|
<Version>0.4.4-ci</Version>
|
||||||
<Company>l--n.de</Company>
|
<Authors>Harald Wolff-Thobaben</Authors>
|
||||||
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
<Company>l--n.de</Company>
|
||||||
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
||||||
<PackageTags>build build-server</PackageTags>
|
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
||||||
</PropertyGroup>
|
<PackageTags>build build-server</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="ln.application" Version="0.1.1" />
|
<ItemGroup>
|
||||||
<PackageReference Include="ln.http" Version="0.1.2" />
|
<PackageReference Include="ln.application" Version="0.1.*" />
|
||||||
<PackageReference Include="ln.json" Version="1.0.0" />
|
<PackageReference Include="ln.http" Version="0.1.*" />
|
||||||
<PackageReference Include="ln.logging" Version="1.0.1" />
|
<PackageReference Include="ln.json" Version="1.0.*" />
|
||||||
<PackageReference Include="ln.threading" Version="0.1.0" />
|
<PackageReference Include="ln.logging" Version="1.0.*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="ln.threading" Version="0.1.*" />
|
||||||
|
<PackageReference Include="ln.type" Version="0.1.*" />
|
||||||
<ItemGroup>
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="5.0.0" />
|
||||||
<ProjectReference Include="../ln.build/ln.build.csproj" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
|
||||||
|
<ItemGroup>
|
||||||
</Project>
|
<ProjectReference Include="../ln.build/ln.build.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
|
||||||
using ln.build.commands;
|
using ln.build.commands;
|
||||||
using ln.build.pipeline;
|
using ln.build.pipeline;
|
||||||
using ln.build.repositories;
|
using ln.build.repositories;
|
||||||
using ln.build.secrets;
|
using ln.build.secrets;
|
||||||
|
using ln.build.semver;
|
||||||
using ln.json;
|
using ln.json;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
using ln.threading;
|
using ln.threading;
|
||||||
|
|
||||||
namespace ln.build
|
namespace ln.build
|
||||||
{
|
{
|
||||||
|
public delegate void CIJobCompleted(CIJob cIJob);
|
||||||
public class CIJob : PoolJob
|
public class CIJob : PoolJob
|
||||||
{
|
{
|
||||||
|
public event CIJobCompleted OnJobCompleted;
|
||||||
|
|
||||||
public string JobID { get; } = Guid.NewGuid().ToString("N");
|
public string JobID { get; } = Guid.NewGuid().ToString("N");
|
||||||
public CIService CIService { get; }
|
public CIService CIService { get; }
|
||||||
public Repository Repository { get; set; }
|
public Repository Repository { get; set; }
|
||||||
|
@ -53,7 +55,7 @@ namespace ln.build
|
||||||
Logger = new Logger(new FileLogger(Path.Combine(ciService.ReportsDirectory, JobID, "build.log")));
|
Logger = new Logger(new FileLogger(Path.Combine(ciService.ReportsDirectory, JobID, "build.log")));
|
||||||
Logger.Backends.Add(Logger.ConsoleLogger);
|
Logger.Backends.Add(Logger.ConsoleLogger);
|
||||||
|
|
||||||
Environment = new CommandEnvironment(){ CIJob = this };
|
Environment = new CommandEnvironment(){ CIJob = this, WorkingDirectory = WorkingDirectory };
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream GetLogStream(string name)
|
public Stream GetLogStream(string name)
|
||||||
|
@ -115,6 +117,8 @@ namespace ln.build
|
||||||
Logger.Log(LogLevel.ERROR, "CIJob failed at CloneRepository()");
|
Logger.Log(LogLevel.ERROR, "CIJob failed at CloneRepository()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnJobCompleted?.Invoke(this);
|
||||||
|
|
||||||
Notify();
|
Notify();
|
||||||
Cleanup();
|
Cleanup();
|
||||||
}
|
}
|
||||||
|
@ -158,5 +162,30 @@ namespace ln.build
|
||||||
Directory.Delete(WorkingDirectory, true);
|
Directory.Delete(WorkingDirectory, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void PublishRelease(SemVerLevels releaseLevel)
|
||||||
|
{
|
||||||
|
if (BuildState == BuildState.SUCCESS)
|
||||||
|
{
|
||||||
|
if (PipeLine.Versioning != null)
|
||||||
|
{
|
||||||
|
Versioning versioning = PipeLine.Versioning;
|
||||||
|
SemVersion version = versioning.GetCurrentVersion(this);
|
||||||
|
|
||||||
|
if (version.IsPreRelease)
|
||||||
|
{
|
||||||
|
SemVersion releaseVersion = new SemVersion(version);
|
||||||
|
releaseVersion.PreRelease = null;
|
||||||
|
|
||||||
|
versioning.SetVersion(this, releaseVersion);
|
||||||
|
} else if (version.IsRelease)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,11 @@ using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using ln.application;
|
using ln.application;
|
||||||
|
using ln.build.commands;
|
||||||
|
using ln.build.pipeline;
|
||||||
using ln.build.repositories;
|
using ln.build.repositories;
|
||||||
using ln.build.secrets;
|
using ln.build.secrets;
|
||||||
|
using ln.build.semver;
|
||||||
using ln.http;
|
using ln.http;
|
||||||
using ln.http.router;
|
using ln.http.router;
|
||||||
using ln.json;
|
using ln.json;
|
||||||
|
@ -43,9 +46,7 @@ namespace ln.build
|
||||||
TemplateRouter templateRouter;
|
TemplateRouter templateRouter;
|
||||||
HTTPServer httpServer;
|
HTTPServer httpServer;
|
||||||
|
|
||||||
HashSet<PipeLine> pipelines = new HashSet<PipeLine>();
|
public StageCommandContainer StageCommands { get; } = new StageCommandContainer(null,null);
|
||||||
public IEnumerable<PipeLine> PipeLines => pipelines;
|
|
||||||
|
|
||||||
|
|
||||||
public CIService()
|
public CIService()
|
||||||
{
|
{
|
||||||
|
@ -72,6 +73,8 @@ namespace ln.build
|
||||||
Directory.CreateDirectory(ReportsDirectory);
|
Directory.CreateDirectory(ReportsDirectory);
|
||||||
Directory.CreateDirectory(Path.Combine(BaseDirectory, "secrets"));
|
Directory.CreateDirectory(Path.Combine(BaseDirectory, "secrets"));
|
||||||
|
|
||||||
|
InitializeStageCommands();
|
||||||
|
|
||||||
InitializeHttpServer();
|
InitializeHttpServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +117,21 @@ namespace ln.build
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitializeStageCommands()
|
||||||
|
{
|
||||||
|
StageCommands.AddCommand(CoreCommands.ShellCommand, "sh");
|
||||||
|
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Prepare, "dotnet", "prepare" );
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Build, "dotnet", "build" );
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Test, "dotnet", "test" );
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Pack, "dotnet", "pack" );
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Push, "dotnet", "push" );
|
||||||
|
StageCommands.AddCommand(DotNetCommand.Publish, "dotnet", "publish" );
|
||||||
|
|
||||||
|
StageCommands.AddCommand(DeployCommand.Deploy, "deploy" );
|
||||||
|
StageCommands.AddCommand(DeployCommand.Release, "release" );
|
||||||
|
}
|
||||||
|
|
||||||
public string GetJobURL(CIJob job) => string.Format("{0}/builds/{1}", BaseURL, job.JobID);
|
public string GetJobURL(CIJob job) => string.Format("{0}/builds/{1}", BaseURL, job.JobID);
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
|
@ -122,11 +140,6 @@ namespace ln.build
|
||||||
httpServer.Start();
|
httpServer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPipeline(PipeLine pipeLine)
|
|
||||||
{
|
|
||||||
pipelines.Add(pipeLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWebHookHandler(string name, Func<CIService,HttpRequest,HttpResponse> webHookHandler)
|
public void AddWebHookHandler(string name, Func<CIService,HttpRequest,HttpResponse> webHookHandler)
|
||||||
{
|
{
|
||||||
hookRouter.AddSimpleRoute(String.Format("/{0}", name), (HttpRoutingContext context,HttpRequest request) => webHookHandler(this, request));
|
hookRouter.AddSimpleRoute(String.Format("/{0}", name), (HttpRoutingContext context,HttpRequest request) => webHookHandler(this, request));
|
||||||
|
|
|
@ -62,6 +62,7 @@ namespace ln.build
|
||||||
return httpResponse.StatusCode;
|
return httpResponse.StatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpStatusCode Delete(params string[] path) => HttpClient.DeleteAsync(string.Format("{0}/{1}",BaseURL, string.Join('/', path))).Result.StatusCode;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using ln.http.exceptions;
|
||||||
|
|
||||||
|
namespace ln.build
|
||||||
|
{
|
||||||
|
public static class PathHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IEnumerable<string> ResolvePattern(string pattern) => ResolvePattern(pattern, Environment.CurrentDirectory);
|
||||||
|
public static IEnumerable<string> ResolvePattern(string pattern, string start)
|
||||||
|
{
|
||||||
|
string[] patternTokens = pattern.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
List<string> matches = new List<string>();
|
||||||
|
collect(patternTokens, 0, start, matches);
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collect(string[] tokens,int depth,string currentPath, List<string> matches)
|
||||||
|
{
|
||||||
|
if (depth < tokens.Length-1)
|
||||||
|
{
|
||||||
|
foreach (string dirname in Directory.GetDirectories(currentPath, tokens[depth]))
|
||||||
|
{
|
||||||
|
collect(tokens, depth + 1, dirname, matches);
|
||||||
|
}
|
||||||
|
} else if (depth == (tokens.Length - 1))
|
||||||
|
{
|
||||||
|
foreach (string filename in Directory.GetFiles(currentPath, tokens[depth]))
|
||||||
|
{
|
||||||
|
matches.Add(filename);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw new Exception("serious bug");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Jint.Parser.Ast;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
|
|
||||||
namespace ln.build.commands
|
namespace ln.build.commands
|
||||||
|
@ -33,9 +34,9 @@ namespace ln.build.commands
|
||||||
TestExitCode = testExitCode;
|
TestExitCode = testExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddArgument(Argument argument) => arguments.Add(argument);
|
public CommandRunner AddArgument(Argument argument){ arguments.Add(argument); return this; }
|
||||||
public void AddArguments(params Argument[] args) => arguments.AddRange(args);
|
public CommandRunner AddArguments(params Argument[] args) { arguments.AddRange(args); return this; }
|
||||||
|
public CommandRunner AddArguments(params string[] args) { arguments.AddRange(args.Select((string arg)=>(Argument)arg)); return this; }
|
||||||
public string FindFileInPath(CommandEnvironment environment, string filename)
|
public string FindFileInPath(CommandEnvironment environment, string filename)
|
||||||
{
|
{
|
||||||
Logger.Log(LogLevel.DEBUG, "Looking up {0} in paths {1}", filename, environment.Get("PATH",""));
|
Logger.Log(LogLevel.DEBUG, "Looking up {0} in paths {1}", filename, environment.Get("PATH",""));
|
||||||
|
@ -116,7 +117,7 @@ namespace ln.build.commands
|
||||||
|
|
||||||
public class Argument
|
public class Argument
|
||||||
{
|
{
|
||||||
string value;
|
protected string value;
|
||||||
public bool MaskValue { get; set; }
|
public bool MaskValue { get; set; }
|
||||||
|
|
||||||
protected Argument(){ }
|
protected Argument(){ }
|
||||||
|
@ -142,8 +143,10 @@ namespace ln.build.commands
|
||||||
public Option(string optionArgument, string optionValue) : this(optionArgument, optionValue, false){ }
|
public Option(string optionArgument, string optionValue) : this(optionArgument, optionValue, false){ }
|
||||||
public Option(string optionArgument, string optionValue, bool maskValue)
|
public Option(string optionArgument, string optionValue, bool maskValue)
|
||||||
{
|
{
|
||||||
|
value = optionValue;
|
||||||
|
|
||||||
OptionArgument = optionArgument;
|
OptionArgument = optionArgument;
|
||||||
GetValue = (e) => optionValue;
|
GetValue = (e) => value;
|
||||||
MaskValue = maskValue;
|
MaskValue = maskValue;
|
||||||
}
|
}
|
||||||
public Option(string optionArgument, Func<CommandEnvironment,string> getOptionValue) : this(optionArgument, getOptionValue, false) { }
|
public Option(string optionArgument, Func<CommandEnvironment,string> getOptionValue) : this(optionArgument, getOptionValue, false) { }
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
<PackageVersion>0.4.4</PackageVersion>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<Version>0.4.0</Version>
|
<PropertyGroup>
|
||||||
<Authors>Harald Wolff-Thobaben</Authors>
|
<Version>0.4.4-ci</Version>
|
||||||
<Company>l--n.de</Company>
|
<Authors>Harald Wolff-Thobaben</Authors>
|
||||||
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
<Company>l--n.de</Company>
|
||||||
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
||||||
<PackageTags>build build-server</PackageTags>
|
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
||||||
</PropertyGroup>
|
<PackageTags>build build-server</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Update="html/**" CopyToOutputDirectory="PreserveNewest" />
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<None Update="html/**" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
<None Update="scripts/**" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="ln.logging" Version="1.0.1" />
|
|
||||||
<PackageReference Include="ln.threading" Version="0.1.0" />
|
<ItemGroup>
|
||||||
<PackageReference Include="ln.json" Version="1.0.0" />
|
<PackageReference Include="ln.logging" Version="1.0.*" />
|
||||||
<PackageReference Include="ln.http" Version="0.1.2" />
|
<PackageReference Include="ln.threading" Version="0.1.*" />
|
||||||
<PackageReference Include="ln.templates" Version="0.1.1" />
|
<PackageReference Include="ln.json" Version="1.0.*" />
|
||||||
<PackageReference Include="ln.templates.http" Version="0.0.1-test" />
|
<PackageReference Include="ln.http" Version="0.1.*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="ln.templates" Version="0.1.*" />
|
||||||
|
<PackageReference Include="ln.templates.http" Version="0.0.*" />
|
||||||
</Project>
|
<PackageReference Include="ln.type" Version="0.1.*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using ln.build.commands;
|
||||||
|
using ln.logging;
|
||||||
|
|
||||||
|
namespace ln.build.pipeline
|
||||||
|
{
|
||||||
|
public static class CoreCommands
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void ShellCommand(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
CommandRunner commandRunner = new CommandRunner("/bin/bash", "-c", string.Format("\"{0}\"", string.Join(' ', arguments))) { Throw = CRThrow.NEVER, };
|
||||||
|
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));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using ln.build.commands;
|
using ln.build.commands;
|
||||||
|
using ln.build.semver;
|
||||||
using ln.json;
|
using ln.json;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
|
|
||||||
|
@ -14,6 +19,11 @@ namespace ln.build.pipeline
|
||||||
List<Stage> stages = new List<Stage>();
|
List<Stage> stages = new List<Stage>();
|
||||||
public IEnumerable<Stage> Stages => stages;
|
public IEnumerable<Stage> Stages => stages;
|
||||||
|
|
||||||
|
public Versioning Versioning { get; set; }
|
||||||
|
|
||||||
|
List<string> loadedTemplates = new List<string>();
|
||||||
|
public IEnumerable<string> LoadedTemplates => loadedTemplates;
|
||||||
|
|
||||||
public DefaultPipeLine(CIService ciService)
|
public DefaultPipeLine(CIService ciService)
|
||||||
{
|
{
|
||||||
CIService = ciService;
|
CIService = ciService;
|
||||||
|
@ -27,6 +37,29 @@ namespace ln.build.pipeline
|
||||||
|
|
||||||
public void LoadJson(JSONObject jsonPipeLine)
|
public void LoadJson(JSONObject jsonPipeLine)
|
||||||
{
|
{
|
||||||
|
if (jsonPipeLine.ContainsKey("templates"))
|
||||||
|
{
|
||||||
|
foreach (JSONString jsonTemplateName in jsonPipeLine["templates"].Children)
|
||||||
|
{
|
||||||
|
if (!loadedTemplates.Contains(jsonTemplateName.Value))
|
||||||
|
{
|
||||||
|
loadedTemplates.Add(jsonTemplateName.Value);
|
||||||
|
|
||||||
|
string repoTemplate = Path.Combine(CommandEnvironment.WorkingDirectory, String.Format("{0}.ln", jsonTemplateName.Value));
|
||||||
|
string systemTemplate = Path.Combine(CIService.ContextDirectory, "scripts", "pipeline", String.Format("{0}.ln", jsonTemplateName.Value));
|
||||||
|
|
||||||
|
if (File.Exists(repoTemplate))
|
||||||
|
{
|
||||||
|
LoadJson(JSONParser.ParseFile(repoTemplate) as JSONObject);
|
||||||
|
} else if (File.Exists(systemTemplate))
|
||||||
|
{
|
||||||
|
LoadJson(JSONParser.ParseFile(systemTemplate) as JSONObject);
|
||||||
|
} else
|
||||||
|
throw new FileNotFoundException(String.Format("{0}.ln", jsonTemplateName.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonPipeLine.ContainsKey("env"))
|
if (jsonPipeLine.ContainsKey("env"))
|
||||||
CommandEnvironment.Apply(jsonPipeLine["env"] as JSONObject);
|
CommandEnvironment.Apply(jsonPipeLine["env"] as JSONObject);
|
||||||
|
|
||||||
|
@ -35,16 +68,19 @@ namespace ln.build.pipeline
|
||||||
JSONArray jsonStages = jsonPipeLine["stages"] as JSONArray;
|
JSONArray jsonStages = jsonPipeLine["stages"] as JSONArray;
|
||||||
foreach (JSONObject jsonStage in jsonStages.Children)
|
foreach (JSONObject jsonStage in jsonStages.Children)
|
||||||
{
|
{
|
||||||
Stage stage = new Stage(this);
|
Stage stage = GetStage(jsonStage["name"].ToNative().ToString());
|
||||||
stage.LoadJson(jsonStage);
|
stage.LoadJson(jsonStage);
|
||||||
|
|
||||||
stages.Add(stage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (jsonPipeLine.ContainsKey("versioning"))
|
||||||
|
Versioning = new Versioning(jsonPipeLine["versioning"] as JSONObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
|
stages.Sort((a,b)=>a.Priority-b.Priority);
|
||||||
|
|
||||||
foreach (Stage stage in stages)
|
foreach (Stage stage in stages)
|
||||||
{
|
{
|
||||||
CommandEnvironment.Logger.Log(LogLevel.INFO,"-------------------------------------------------------------------------------------");
|
CommandEnvironment.Logger.Log(LogLevel.INFO,"-------------------------------------------------------------------------------------");
|
||||||
|
@ -54,6 +90,19 @@ namespace ln.build.pipeline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage GetStage(string stageName)
|
||||||
|
{
|
||||||
|
foreach (Stage stage in stages)
|
||||||
|
{
|
||||||
|
if (stageName.Equals(stage.Name))
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stage _stage = new Stage(this);
|
||||||
|
_stage.Name = stageName;
|
||||||
|
stages.Add(_stage);
|
||||||
|
return _stage;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +110,11 @@ namespace ln.build.pipeline
|
||||||
{
|
{
|
||||||
public DefaultPipeLine PipeLine { get; }
|
public DefaultPipeLine PipeLine { get; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public int Priority { get; set; }
|
||||||
|
|
||||||
public CommandEnvironment CommandEnvironment { get; }
|
public CommandEnvironment CommandEnvironment { get; }
|
||||||
|
|
||||||
List<StageCommand> commands = new List<StageCommand>();
|
public List<string> commands = new List<string>();
|
||||||
public IEnumerable<StageCommand> Commands => commands;
|
|
||||||
|
|
||||||
public Stage(DefaultPipeLine pipeLine)
|
public Stage(DefaultPipeLine pipeLine)
|
||||||
{
|
{
|
||||||
|
@ -76,6 +125,8 @@ namespace ln.build.pipeline
|
||||||
public void LoadJson(JSONObject jsonStage)
|
public void LoadJson(JSONObject jsonStage)
|
||||||
{
|
{
|
||||||
Name = jsonStage["name"].ToNative().ToString();
|
Name = jsonStage["name"].ToNative().ToString();
|
||||||
|
if (jsonStage.ContainsKey("priority"))
|
||||||
|
Priority = (int)(long)jsonStage["priority"].ToNative();
|
||||||
|
|
||||||
if (jsonStage.ContainsKey("env"))
|
if (jsonStage.ContainsKey("env"))
|
||||||
{
|
{
|
||||||
|
@ -86,7 +137,8 @@ namespace ln.build.pipeline
|
||||||
JSONArray jsonCommands = jsonStage["commands"] as JSONArray;
|
JSONArray jsonCommands = jsonStage["commands"] as JSONArray;
|
||||||
foreach (JSONValue jsonValue in jsonCommands.Children)
|
foreach (JSONValue jsonValue in jsonCommands.Children)
|
||||||
{
|
{
|
||||||
commands.Add(StageCommand.Create(jsonValue.ToNative().ToString()));
|
Logging.Log(LogLevel.DEBUG, "stage {0} command: {1}", Name, jsonValue.ToNative().ToString());
|
||||||
|
commands.Add(jsonValue.ToNative().ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (jsonStage.ContainsKey("secrets"))
|
if (jsonStage.ContainsKey("secrets"))
|
||||||
|
@ -96,15 +148,14 @@ namespace ln.build.pipeline
|
||||||
{
|
{
|
||||||
CommandEnvironment.Set(key, CommandEnvironment.SecretStorage?.GetSecret(jsonSecrets[key]?.ToNative()?.ToString()));
|
CommandEnvironment.Set(key, CommandEnvironment.SecretStorage?.GetSecret(jsonSecrets[key]?.ToNative()?.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
foreach (StageCommand command in commands)
|
foreach (string command in commands)
|
||||||
command.Run(this);
|
PipeLine.CIService.StageCommands.Run(this, command.Split());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using ln.build.repositories;
|
||||||
|
using ln.build.semver;
|
||||||
|
using ln.logging;
|
||||||
|
|
||||||
|
namespace ln.build.pipeline
|
||||||
|
{
|
||||||
|
public static class DeployCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void Deploy(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "stage command: deploy not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Release(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
if (stage.CommandEnvironment.CIJob.Repository is Repository repository)
|
||||||
|
{
|
||||||
|
SemVersion releaseVersion = (SemVersion)stage.CommandEnvironment.Get("RELEASE_VERSION");
|
||||||
|
if (releaseVersion?.IsRelease ?? false)
|
||||||
|
{
|
||||||
|
Release release = repository.GetRelease(releaseVersion.ToString());
|
||||||
|
if (release != null)
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "source repository already has release {0}", releaseVersion);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
string releaseBody = String.Format("automatic release by ln.build.server");
|
||||||
|
string release_ref = stage.CommandEnvironment.Get("REPO_REF");
|
||||||
|
if (release_ref == null)
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING,"release: no source repository reference found. can't create release!");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
release = repository.CreateRelease(releaseVersion, String.Format("CI Release {0}", releaseVersion.ToString()), releaseBody, release_ref);
|
||||||
|
foreach (string artefact in stage.CommandEnvironment.Get("RELEASE_ARTEFACTS","").Split(':'))
|
||||||
|
{
|
||||||
|
release.CreateOrReplaceAttachment(artefact,Path.GetFileName(artefact));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "release: no repository interface attached to CIJob");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (stage.CommandEnvironment.Get("REPO_EVENT","").Equals("release"))
|
||||||
|
{
|
||||||
|
Repository repository = stage.CommandEnvironment.CIJob?.Repository;
|
||||||
|
if (repository != null)
|
||||||
|
{
|
||||||
|
Release release = repository.GetRelease(int.Parse(stage.CommandEnvironment.Get("RELEASE_ID")));
|
||||||
|
|
||||||
|
foreach (string arg in Arguments)
|
||||||
|
{
|
||||||
|
string localPath, remoteFileName;
|
||||||
|
int ie = arg.IndexOf('=');
|
||||||
|
if (ie == -1)
|
||||||
|
{
|
||||||
|
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg);
|
||||||
|
remoteFileName = Path.GetFileName(arg);
|
||||||
|
} else {
|
||||||
|
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg.Substring(0, ie));
|
||||||
|
remoteFileName = arg.Substring(ie + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "Adding {0} to release as {1}", localPath, remoteFileName);
|
||||||
|
Attachment attachment = release.CreateOrReplaceAttachment( localPath, remoteFileName );
|
||||||
|
|
||||||
|
// Cleanup attachments
|
||||||
|
foreach (Attachment b in release.GetAttachments())
|
||||||
|
{
|
||||||
|
if (!b.Equals(attachment) && b.Name.Equals(attachment.Name))
|
||||||
|
b.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.ERROR, "RELEASE: no repository interface found!");
|
||||||
|
throw new Exception("Respository needed!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "RELEASE: build is no release build. Ignoring.");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
|
|
||||||
using ln.build.pipeline;
|
|
||||||
|
|
||||||
namespace ln.build.commands
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DotNetCommand : StageCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public DotNetCommand(string arguments) :base("DOTNET"){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Analyze(Stage stage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public override void Run(Stage stage)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using ln.build.pipeline;
|
||||||
|
using ln.build.semver;
|
||||||
|
using ln.build.support.dotnet;
|
||||||
|
using ln.logging;
|
||||||
|
using ln.type;
|
||||||
|
|
||||||
|
namespace ln.build.commands
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class DotNetCommand
|
||||||
|
{
|
||||||
|
public static void Prepare(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build")))
|
||||||
|
Directory.Delete(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build"), true);
|
||||||
|
|
||||||
|
List<string> projectFiles = new List<string>();
|
||||||
|
List<string> slnFiles = new List<string>();
|
||||||
|
|
||||||
|
foreach (string argument in arguments)
|
||||||
|
{
|
||||||
|
foreach (string filename in PathHelper.ResolvePattern(argument, stage.CommandEnvironment.WorkingDirectory))
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "dotnet prepare: found {0}", filename);
|
||||||
|
if (filename.EndsWith(".csproj",StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
projectFiles.Add(filename);
|
||||||
|
else
|
||||||
|
slnFiles.Add(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: Parse .sln files for referenced projects
|
||||||
|
|
||||||
|
SemVersion highestVersion = new SemVersion(0,0,0);
|
||||||
|
|
||||||
|
foreach (string projectFileName in projectFiles)
|
||||||
|
{
|
||||||
|
CSProjHelper csp = new CSProjHelper(projectFileName);
|
||||||
|
|
||||||
|
string projectName = csp.GetName();
|
||||||
|
SemVersion projectVersion= csp.GetVersion();
|
||||||
|
string ot = csp.GetOutputType();
|
||||||
|
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "dotnet prepare: project {0} version={1} type={2}", projectName, projectVersion, ot);
|
||||||
|
|
||||||
|
if (projectVersion == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (projectVersion > highestVersion)
|
||||||
|
highestVersion = projectVersion;
|
||||||
|
|
||||||
|
stage.PipeLine.CommandEnvironment.Extend("DOTNET_PROJECTS", projectFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.PipeLine.CommandEnvironment.Set("RELEASE_VERSION", highestVersion.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Build(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
foreach (string projectFileName in stage.CommandEnvironment.Get("DOTNET_PROJECTS","").Split(':'))
|
||||||
|
{
|
||||||
|
new CommandRunner("dotnet","build", projectFileName, new CommandRunner.Option("-c", stage.CommandEnvironment.Get("DOTNET_CONFIGURATION")))
|
||||||
|
.Run(stage.CommandEnvironment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Pack(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
foreach (string projectFileName in stage.CommandEnvironment.Get("DOTNET_PROJECTS","").Split(':'))
|
||||||
|
{
|
||||||
|
CSProjHelper csProject = new CSProjHelper(projectFileName);
|
||||||
|
|
||||||
|
if (!csProject.GetOutputType().Equals("Library"))
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "dotnet pack: not packing {0} [{1}]",projectFileName, csProject.GetOutputType());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "dotnet pack: packing now {0} [{1}]",projectFileName, csProject.GetOutputType());
|
||||||
|
|
||||||
|
new CommandRunner("dotnet","pack", projectFileName)
|
||||||
|
.AddArgument(new CommandRunner.Option("-c", stage.CommandEnvironment.Get("DOTNET_CONFIGURATION")))
|
||||||
|
.AddArguments("-o", Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build/"))
|
||||||
|
.Run(stage.CommandEnvironment);
|
||||||
|
|
||||||
|
string artefact = String.Format(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build","{0}.{1}.nupkg"),csProject.GetName(), csProject.GetVersion());
|
||||||
|
if (!File.Exists(artefact))
|
||||||
|
throw new FileNotFoundException(artefact);
|
||||||
|
|
||||||
|
stage.PipeLine.CommandEnvironment.Extend("DOTNET_ARTEFACTS", artefact);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void Publish(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
List<string> projectFileNames = new List<string>();
|
||||||
|
List<string> artefacts = new List<string>();
|
||||||
|
|
||||||
|
if (arguments.Length == 0)
|
||||||
|
projectFileNames.AddRange(stage.CommandEnvironment.Get("DOTNET_PROJECTS","").Split(':'));
|
||||||
|
else
|
||||||
|
projectFileNames.AddRange(arguments);
|
||||||
|
|
||||||
|
foreach (string projectFileName in projectFileNames)
|
||||||
|
{
|
||||||
|
CSProjHelper csProject = new CSProjHelper(projectFileName);
|
||||||
|
|
||||||
|
if (!csProject.GetOutputType().Equals("Exe"))
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "dotnet publish: not publishing {0} [{1}]",projectFileName, csProject.GetOutputType());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING, "dotnet publish: publishing now {0} [{1}]",projectFileName, csProject.GetOutputType());
|
||||||
|
|
||||||
|
CommandRunner cr = new CommandRunner("dotnet","publish", projectFileName)
|
||||||
|
.AddArgument(new CommandRunner.Option("-c", stage.CommandEnvironment.Get("DOTNET_CONFIGURATION")))
|
||||||
|
.AddArguments("-p:PublishTrimmed=true","-p:PublishSingleFile=true","-p:PublishReadyToRun=false");
|
||||||
|
|
||||||
|
CommandRunner.Option outputOption = new CommandRunner.Option("-o", Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build"));
|
||||||
|
cr.AddArgument(outputOption);
|
||||||
|
|
||||||
|
string[] rids = stage.CommandEnvironment.Get("DOTNET_RIDS","").Split(':');
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO,"dotnet publish: using rids: {0}", string.Join(' ', rids));
|
||||||
|
|
||||||
|
if (rids.Length == 0)
|
||||||
|
{
|
||||||
|
cr.Run(stage.CommandEnvironment);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
CommandRunner.Option ridOption = new CommandRunner.Option("-r", "");
|
||||||
|
cr.AddArgument(ridOption);
|
||||||
|
cr.AddArgument("--self-contained");
|
||||||
|
foreach (string rid in rids)
|
||||||
|
{
|
||||||
|
ridOption.SetValue(rid);
|
||||||
|
outputOption.SetValue(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build",rid));
|
||||||
|
|
||||||
|
cr.Run(stage.CommandEnvironment);
|
||||||
|
|
||||||
|
string artefact = Directory.GetFiles(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build",rid)).Where((fn)=>!fn.EndsWith(".pdb")).FirstOrDefault();
|
||||||
|
if (artefact != null)
|
||||||
|
{
|
||||||
|
string ext = Path.GetExtension(artefact);
|
||||||
|
if (!ext.Equals(".exe"))
|
||||||
|
ext = "";
|
||||||
|
|
||||||
|
string finalName = Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build", string.Format("{0}-{3}-{1}{2}",csProject.GetName(), rid, ext, csProject.GetVersion()));
|
||||||
|
File.Move(artefact, finalName);
|
||||||
|
artefacts.Add(finalName);
|
||||||
|
|
||||||
|
stage.PipeLine.CommandEnvironment.Extend("DOTNET_ARTEFACTS", finalName);
|
||||||
|
|
||||||
|
Directory.Delete(Path.Combine(stage.CommandEnvironment.WorkingDirectory, ".build",rid), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO,"dotnet publish: created the following artefacts: {0}", string.Join(' ', artefacts));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Test(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
foreach (string projectFileName in stage.CommandEnvironment.Get("DOTNET_PROJECTS","").Split(':'))
|
||||||
|
{
|
||||||
|
new CommandRunner("dotnet","test", projectFileName, new CommandRunner.Option("-c", stage.CommandEnvironment.Get("DOTNET_CONFIGURATION")))
|
||||||
|
.Run(stage.CommandEnvironment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Push(Stage stage,params string[] arguments)
|
||||||
|
{
|
||||||
|
if (stage.CommandEnvironment.SecretStorage == null)
|
||||||
|
{
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.WARNING,"dotnet push: no SecretStorage available, push operations are going to fail without authorization!");
|
||||||
|
} else {
|
||||||
|
stage.CommandEnvironment.Logger.Log(LogLevel.INFO,"dotnet push: using secrets from {0}", stage.CommandEnvironment.SecretStorage.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] nupkgList = stage.CommandEnvironment.Get("DOTNET_ARTEFACTS","").Split(':').Where((fn)=>fn.EndsWith(".nupkg")).ToArray();
|
||||||
|
string[] binaryList = stage.CommandEnvironment.Get("DOTNET_ARTEFACTS","").Split(':').Where((fn)=>!fn.EndsWith(".nupkg")).ToArray();
|
||||||
|
|
||||||
|
foreach (string binaryArtefact in binaryList)
|
||||||
|
{
|
||||||
|
stage.PipeLine.CommandEnvironment.Extend("RELEASE_ARTEFACTS", binaryArtefact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nupkgList.Length > 0)
|
||||||
|
{
|
||||||
|
string nugetSource = stage.CommandEnvironment.Get("NUGET_SOURCE");
|
||||||
|
string nugetApiKey = null;
|
||||||
|
|
||||||
|
if (!(stage.CommandEnvironment.SecretStorage?.TryGetSecret(nugetSource, out nugetApiKey) ?? false))
|
||||||
|
{
|
||||||
|
URI uriNuget = new URI(nugetSource);
|
||||||
|
stage.CommandEnvironment.SecretStorage?.TryGetSecret(new URI(uriNuget.Scheme, uriNuget.Authority, "").ToString(), out nugetApiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandRunner cr = new CommandRunner("dotnet", "nuget", "push");
|
||||||
|
|
||||||
|
CommandRunner.Argument argNupkg = new CommandRunner.Argument("");
|
||||||
|
cr.AddArgument(argNupkg);
|
||||||
|
|
||||||
|
CommandRunner.Option optSource = new CommandRunner.Option("-s",nugetSource);
|
||||||
|
cr.AddArgument(optSource);
|
||||||
|
|
||||||
|
CommandRunner.Option optApiKey = new CommandRunner.Option("-k", nugetApiKey);
|
||||||
|
cr.AddArgument(optApiKey);
|
||||||
|
|
||||||
|
foreach (string nupkg in nupkgList)
|
||||||
|
{
|
||||||
|
argNupkg.SetValue(nupkg);
|
||||||
|
cr.Run(stage.CommandEnvironment);
|
||||||
|
|
||||||
|
stage.PipeLine.CommandEnvironment.Extend("RELEASE_ARTEFACTS", nupkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,53 +0,0 @@
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Jint.Native.Function;
|
|
||||||
using ln.build.repositories;
|
|
||||||
using ln.logging;
|
|
||||||
|
|
||||||
namespace ln.build.pipeline
|
|
||||||
{
|
|
||||||
public class ReleaseCommand : StageCommand
|
|
||||||
{
|
|
||||||
public string[] Arguments { get; }
|
|
||||||
public ReleaseCommand(string args) :base("RELEASE")
|
|
||||||
{
|
|
||||||
Arguments = args.Split();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(Stage stage)
|
|
||||||
{
|
|
||||||
if (stage.CommandEnvironment.Get("REPO_EVENT","").Equals("release"))
|
|
||||||
{
|
|
||||||
Repository repository = stage.CommandEnvironment.CIJob?.Repository;
|
|
||||||
if (repository != null)
|
|
||||||
{
|
|
||||||
Release release = repository.GetRelease(int.Parse(stage.CommandEnvironment.Get("RELEASE_ID")));
|
|
||||||
|
|
||||||
foreach (string arg in Arguments)
|
|
||||||
{
|
|
||||||
string localPath, remoteFileName;
|
|
||||||
int ie = arg.IndexOf('=');
|
|
||||||
if (ie == -1)
|
|
||||||
{
|
|
||||||
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg);
|
|
||||||
remoteFileName = Path.GetFileName(arg);
|
|
||||||
} else {
|
|
||||||
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg.Substring(0, ie));
|
|
||||||
remoteFileName = arg.Substring(ie + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "Adding {0} to release as {1}", localPath, remoteFileName);
|
|
||||||
release.AddAttachement(localPath, remoteFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
stage.CommandEnvironment.Logger.Log(LogLevel.ERROR, "RELEASE: no repository interface found!");
|
|
||||||
throw new Exception("Respository needed!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "RELEASE: build is no release build. Ignoring.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using ln.build.commands;
|
|
||||||
using ln.logging;
|
|
||||||
|
|
||||||
namespace ln.build.pipeline
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using ln.build.commands;
|
|
||||||
|
|
||||||
namespace ln.build.pipeline
|
|
||||||
{
|
|
||||||
public abstract class StageCommand
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public StageCommand(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Run(Stage stage);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static Dictionary<string,Func<string,StageCommand>> commandFactories = new Dictionary<string, Func<string, StageCommand>>();
|
|
||||||
|
|
||||||
public static StageCommand Create(string cmdline)
|
|
||||||
{
|
|
||||||
string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2);
|
|
||||||
|
|
||||||
if (commandFactories.TryGetValue(tokens[0],out Func<string,StageCommand> 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));
|
|
||||||
commandFactories.Add("RELEASE", (args) => new ReleaseCommand(args));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using ln.build.commands;
|
||||||
|
using ln.type;
|
||||||
|
|
||||||
|
namespace ln.build.pipeline
|
||||||
|
{
|
||||||
|
|
||||||
|
public class StageCommandContainer
|
||||||
|
{
|
||||||
|
public StageCommandContainer Parent { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public string FullName => Parent?.FullName != null ? string.Format("{0} {1}", Parent.FullName, Name) : Name;
|
||||||
|
|
||||||
|
Dictionary<string,Action<Stage,string[]>> commands = new Dictionary<string, Action<Stage,string[]>>();
|
||||||
|
Dictionary<string,StageCommandContainer> children = new Dictionary<string, StageCommandContainer>();
|
||||||
|
|
||||||
|
public StageCommandContainer(StageCommandContainer parent, string name)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCommand(Action<Stage,string[]> commandAction, string commandPath) => AddCommand(commandAction, commandPath.Split());
|
||||||
|
public void AddCommand(Action<Stage,string[]> commandAction, params string[] commandPath)
|
||||||
|
{
|
||||||
|
if (commandPath.Length == 0)
|
||||||
|
throw new ArgumentException(nameof(commandPath));
|
||||||
|
if (commandPath.Length > 1)
|
||||||
|
{
|
||||||
|
if (!children.TryGetValue(commandPath[0], out StageCommandContainer childContainer))
|
||||||
|
{
|
||||||
|
childContainer = new StageCommandContainer(this, commandPath[0]);
|
||||||
|
children.Add(commandPath[0], childContainer);
|
||||||
|
}
|
||||||
|
childContainer.AddCommand(commandAction, commandPath.Slice(1));
|
||||||
|
} else {
|
||||||
|
commands.Add(commandPath[0], commandAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(Stage stage, params string[] arguments)
|
||||||
|
{
|
||||||
|
if (commands.TryGetValue(arguments[0], out Action<Stage,string[]> commandAction))
|
||||||
|
commandAction(stage, arguments.Slice(1));
|
||||||
|
else if (children.TryGetValue(arguments[0], out StageCommandContainer childContainer))
|
||||||
|
childContainer.Run(stage, arguments.Slice(1));
|
||||||
|
else
|
||||||
|
throw new ArgumentException(String.Format("command not found: {0}", arguments[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using ln.build.semver;
|
||||||
|
using ln.build.semver.provider;
|
||||||
|
using ln.json;
|
||||||
|
|
||||||
|
namespace ln.build.pipeline
|
||||||
|
{
|
||||||
|
public class Versioning
|
||||||
|
{
|
||||||
|
public string ProviderName { get; set; }
|
||||||
|
public string[] Sources { get; set; }
|
||||||
|
|
||||||
|
public Provider Provider { get; }
|
||||||
|
|
||||||
|
public Versioning()
|
||||||
|
{}
|
||||||
|
public Versioning(JSONObject jsonVersioning)
|
||||||
|
{
|
||||||
|
ProviderName = jsonVersioning["provider"].ToNative().ToString();
|
||||||
|
if (jsonVersioning.ContainsKey("sources"))
|
||||||
|
Sources = jsonVersioning["sources"].Children.Select((s) => s.ToNative().ToString()).ToArray();
|
||||||
|
|
||||||
|
Provider = Provider.CreateProvider(ProviderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Versioning(string providerName)
|
||||||
|
{
|
||||||
|
ProviderName = providerName;
|
||||||
|
Provider = Provider.CreateProvider(ProviderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SemVersion GetCurrentVersion(CIJob job) => GetVersion(job, Sources[0]);
|
||||||
|
public SemVersion GetVersion(CIJob job, string source)
|
||||||
|
{
|
||||||
|
string sourceFileName = Path.Combine(job?.WorkingDirectory ?? "", source);
|
||||||
|
return Provider.GetVersion(sourceFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVersion(CIJob job, SemVersion version)
|
||||||
|
{
|
||||||
|
foreach (string source in Sources)
|
||||||
|
SetVersion(job, source, version);
|
||||||
|
}
|
||||||
|
public void SetVersion(CIJob job, string source, SemVersion version)
|
||||||
|
{
|
||||||
|
string sourceFileName = Path.Combine(job?.WorkingDirectory ?? "", source);
|
||||||
|
Provider.SetVersion(sourceFileName, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime;
|
||||||
|
|
||||||
namespace ln.build.repositories
|
namespace ln.build.repositories
|
||||||
{
|
{
|
||||||
|
@ -12,31 +13,45 @@ namespace ln.build.repositories
|
||||||
public Boolean IsDraft {get; set; }
|
public Boolean IsDraft {get; set; }
|
||||||
public Boolean IsPreRelease {get; set; }
|
public Boolean IsPreRelease {get; set; }
|
||||||
|
|
||||||
|
public string TargetCommit { get; set; }
|
||||||
|
|
||||||
public string Body { get; set; }
|
public string Body { get; set; }
|
||||||
|
|
||||||
public abstract Repository Repository { get; }
|
public abstract Repository Repository { get; }
|
||||||
|
|
||||||
public abstract Attachement[] GetAttachements();
|
public abstract Attachment[] GetAttachments();
|
||||||
|
|
||||||
public abstract void AddAttachement(string localPath,string remoteFilename);
|
public abstract Attachment CreateOrReplaceAttachment(string localPath, string remoteFileName);
|
||||||
|
|
||||||
|
public virtual Attachment FindAttachmentByName(string name)
|
||||||
|
|
||||||
public class Attachement
|
|
||||||
{
|
{
|
||||||
public Release Release { get; set; }
|
foreach (Attachment attachment in GetAttachments())
|
||||||
public int Id { get; set; } = -1;
|
if (attachment.Name.Equals(name))
|
||||||
public string Name { get; set; }
|
return attachment;
|
||||||
public Guid UUID { get; set; }
|
return null;
|
||||||
|
|
||||||
public string DownloadURL { get; set; }
|
|
||||||
|
|
||||||
public Attachement(Release release)
|
|
||||||
{
|
|
||||||
Release = release;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class Attachment
|
||||||
|
{
|
||||||
|
public abstract Release Release { get; }
|
||||||
|
public abstract int Id { get; set; }
|
||||||
|
public abstract string Name { get; set; }
|
||||||
|
|
||||||
|
public abstract string DownloadURL { get; set; }
|
||||||
|
|
||||||
|
public Attachment()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Create(string localPath);
|
||||||
|
public abstract void Delete();
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => (obj is Attachment attachment) && Id.Equals(attachment.Id);
|
||||||
|
public override int GetHashCode() => Id.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using ln.build.semver;
|
||||||
using ln.http;
|
using ln.http;
|
||||||
|
|
||||||
namespace ln.build.repositories
|
namespace ln.build.repositories
|
||||||
|
@ -13,6 +14,12 @@ namespace ln.build.repositories
|
||||||
public abstract Release[] GetReleases();
|
public abstract Release[] GetReleases();
|
||||||
public abstract Release GetRelease(string tagName);
|
public abstract Release GetRelease(string tagName);
|
||||||
public abstract Release GetRelease(int id);
|
public abstract Release GetRelease(int id);
|
||||||
|
|
||||||
|
public abstract Release CreateRelease(SemVersion releaseVersion, string name, string body, string target_reference);
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void CommitAndPush(string message, string[] addedPaths, string[] modifiedPaths, string[] removedPaths);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection.Emit;
|
||||||
using ln.json;
|
using ln.json;
|
||||||
|
using ln.json.attributes;
|
||||||
using ln.type;
|
using ln.type;
|
||||||
|
|
||||||
namespace ln.build.repositories.gitea
|
namespace ln.build.repositories.gitea
|
||||||
|
@ -12,11 +15,17 @@ namespace ln.build.repositories.gitea
|
||||||
{
|
{
|
||||||
public override Repository Repository => GiteaRepository;
|
public override Repository Repository => GiteaRepository;
|
||||||
public GiteaRepository GiteaRepository { get; }
|
public GiteaRepository GiteaRepository { get; }
|
||||||
|
|
||||||
public GiteaRelease(GiteaRepository repository)
|
public GiteaRelease(GiteaRepository repository)
|
||||||
{
|
{
|
||||||
GiteaRepository = repository;
|
GiteaRepository = repository;
|
||||||
}
|
}
|
||||||
public GiteaRelease(GiteaRepository repository,JSONObject jsonRelease) : this(repository)
|
public GiteaRelease(GiteaRepository repository,JSONObject jsonRelease) : this(repository)
|
||||||
|
{
|
||||||
|
UpdateFromJson(jsonRelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromJson(JSONObject jsonRelease)
|
||||||
{
|
{
|
||||||
Id = (int)(long)jsonRelease["id"].ToNative();
|
Id = (int)(long)jsonRelease["id"].ToNative();
|
||||||
Name = jsonRelease["name"].ToNative().ToString();
|
Name = jsonRelease["name"].ToNative().ToString();
|
||||||
|
@ -24,25 +33,102 @@ namespace ln.build.repositories.gitea
|
||||||
Body = jsonRelease["body"].ToNative().ToString();
|
Body = jsonRelease["body"].ToNative().ToString();
|
||||||
IsDraft = (bool)jsonRelease["draft"].ToNative();
|
IsDraft = (bool)jsonRelease["draft"].ToNative();
|
||||||
IsPreRelease = (bool)jsonRelease["prerelease"].ToNative();
|
IsPreRelease = (bool)jsonRelease["prerelease"].ToNative();
|
||||||
|
TargetCommit = jsonRelease["target_commitish"].ToNative().ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddAttachement(string localPath, string remoteFileName)
|
public JSONObject ToJson()
|
||||||
|
{
|
||||||
|
JSONObject jsonRelease = new JSONObject();
|
||||||
|
jsonRelease.Add("name", Name);
|
||||||
|
jsonRelease.Add("tag_name", TagName);
|
||||||
|
jsonRelease.Add("target_comittish", TargetCommit);
|
||||||
|
jsonRelease.Add("prerelease",IsPreRelease);
|
||||||
|
jsonRelease.Add("draft",IsDraft);
|
||||||
|
jsonRelease.Add("body",Body);
|
||||||
|
|
||||||
|
return jsonRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Attachment CreateOrReplaceAttachment(string localPath, string remoteFileName)
|
||||||
|
{
|
||||||
|
Attachment attachment = FindAttachmentByName(remoteFileName) ?? new GiteaAttachment(this){ Name = remoteFileName, };
|
||||||
|
attachment.Create(localPath);
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Attachment[] GetAttachments()
|
||||||
|
{
|
||||||
|
if (HttpStatusCode.OK == GiteaRepository.Client.GetJson(out JSONValue jsonAssets, "repos", GiteaRepository.Owner, GiteaRepository.Name, "releases", Id.ToString(), "assets" ))
|
||||||
|
{
|
||||||
|
List<Attachment> attachments = new List<Attachment>();
|
||||||
|
|
||||||
|
foreach (JSONObject jsonAsset in (jsonAssets as JSONArray).Children)
|
||||||
|
attachments.Add(new GiteaAttachment(this, jsonAsset));
|
||||||
|
|
||||||
|
return attachments.ToArray();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GiteaAttachment : Attachment
|
||||||
|
{
|
||||||
|
public override Release Release => GiteaRelease;
|
||||||
|
public GiteaRelease GiteaRelease { get; }
|
||||||
|
|
||||||
|
int id = -1;
|
||||||
|
string downloadURL;
|
||||||
|
|
||||||
|
public override int Id { get => id; set => throw new NotImplementedException(); }
|
||||||
|
public override string Name { get; set; }
|
||||||
|
public override string DownloadURL { get => downloadURL; set => throw new NotImplementedException(); }
|
||||||
|
|
||||||
|
|
||||||
|
public GiteaAttachment(GiteaRelease giteaRelease)
|
||||||
|
{
|
||||||
|
GiteaRelease = giteaRelease;
|
||||||
|
}
|
||||||
|
public GiteaAttachment(GiteaRelease giteaRelease,JSONObject jsonAttachment) :this(giteaRelease)
|
||||||
|
{
|
||||||
|
LoadJson(jsonAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadJson(JSONObject jsonAttachment)
|
||||||
|
{
|
||||||
|
id = (int)(long)jsonAttachment["id"].ToNative();
|
||||||
|
Name = jsonAttachment["name"].ToNative().ToString();
|
||||||
|
downloadURL = jsonAttachment["browser_download_url"].ToNative().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Delete()
|
||||||
|
{
|
||||||
|
if (id != -1)
|
||||||
|
{
|
||||||
|
if (HttpStatusCode.NoContent != GiteaRelease.GiteaRepository.Client.Delete("repos", GiteaRelease.GiteaRepository.Owner, GiteaRelease.GiteaRepository.Name, "releases", GiteaRelease.Id.ToString(), "assets", id.ToString()))
|
||||||
|
throw new Exception(String.Format("could not delete attachment {0}/{1}", GiteaRelease.Id, id));
|
||||||
|
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Create(string localPath)
|
||||||
{
|
{
|
||||||
using (FileStream fs = new FileStream(localPath,FileMode.Open))
|
using (FileStream fs = new FileStream(localPath,FileMode.Open))
|
||||||
{
|
{
|
||||||
StreamContent attachmentBytes = new StreamContent(fs);
|
StreamContent attachmentBytes = new StreamContent(fs);
|
||||||
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
|
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
|
||||||
formDataContent.Add(attachmentBytes, "attachment", remoteFileName);
|
formDataContent.Add(attachmentBytes, "attachment", Name);
|
||||||
if (HttpStatusCode.Created != GiteaRepository.Client.PostContent(formDataContent,out JSONValue response, "repos", GiteaRepository.Owner, GiteaRepository.Name, "releases", Id.ToString(), String.Format("assets?name={0}", remoteFileName)))
|
if (HttpStatusCode.Created != GiteaRelease.GiteaRepository.Client.PostContent(formDataContent,out JSONValue response, "repos", GiteaRelease.GiteaRepository.Owner, GiteaRelease.GiteaRepository.Name, "releases", GiteaRelease.Id.ToString(), string.Format("assets?name={0}", Name)))
|
||||||
{
|
{
|
||||||
throw new Exception(String.Format("could not create attachment to release: {0}", localPath));
|
throw new Exception(String.Format("could not create attachment to release: {0}", localPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id != -1)
|
||||||
|
Delete();
|
||||||
|
|
||||||
|
LoadJson(response as JSONObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Attachement[] GetAttachements()
|
|
||||||
{
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,17 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using ln.build.commands;
|
||||||
|
using ln.build.pipeline;
|
||||||
using ln.build.secrets;
|
using ln.build.secrets;
|
||||||
|
using ln.build.semver;
|
||||||
using ln.http;
|
using ln.http;
|
||||||
using ln.json;
|
using ln.json;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
|
using ln.threading;
|
||||||
|
using ln.type;
|
||||||
|
|
||||||
namespace ln.build.repositories.gitea
|
namespace ln.build.repositories.gitea
|
||||||
{
|
{
|
||||||
|
@ -20,6 +26,8 @@ namespace ln.build.repositories.gitea
|
||||||
|
|
||||||
public JsonApiClient Client { get; }
|
public JsonApiClient Client { get; }
|
||||||
|
|
||||||
|
string AccessToken { get; set; }
|
||||||
|
|
||||||
public GiteaRepository(string baseURL, string owner, string name)
|
public GiteaRepository(string baseURL, string owner, string name)
|
||||||
:base(string.Format("{0}/{1}/{2}.git",baseURL, owner, name))
|
:base(string.Format("{0}/{1}/{2}.git",baseURL, owner, name))
|
||||||
{
|
{
|
||||||
|
@ -34,6 +42,7 @@ namespace ln.build.repositories.gitea
|
||||||
public GiteaRepository(string baseURL, string owner, string name, string accessToken)
|
public GiteaRepository(string baseURL, string owner, string name, string accessToken)
|
||||||
:this(baseURL, owner, name)
|
:this(baseURL, owner, name)
|
||||||
{
|
{
|
||||||
|
AccessToken = accessToken;
|
||||||
if (accessToken != null)
|
if (accessToken != null)
|
||||||
Client.HttpClient.DefaultRequestHeaders.Add("Authorization",String.Format("token {0}", accessToken));
|
Client.HttpClient.DefaultRequestHeaders.Add("Authorization",String.Format("token {0}", accessToken));
|
||||||
}
|
}
|
||||||
|
@ -79,7 +88,11 @@ namespace ln.build.repositories.gitea
|
||||||
|
|
||||||
public override Release GetRelease(string tagName)
|
public override Release GetRelease(string tagName)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (HttpStatusCode.OK == Client.GetJson(out JSONValue jsonRelease, "repos", Owner, Name, "releases", "tags", tagName))
|
||||||
|
{
|
||||||
|
return new GiteaRelease(this, jsonRelease as JSONObject);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
public override Release GetRelease(int releaseId)
|
public override Release GetRelease(int releaseId)
|
||||||
{
|
{
|
||||||
|
@ -90,9 +103,40 @@ namespace ln.build.repositories.gitea
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Release CreateRelease(SemVersion releaseVersion, string name, string body, string target_reference)
|
||||||
|
{
|
||||||
|
GiteaRelease giteaRelease = new GiteaRelease(this){ TagName = releaseVersion.ToString(), Name = name, Body = body, TargetCommit = target_reference };
|
||||||
|
|
||||||
|
if (HttpStatusCode.Created != Client.PostJson(giteaRelease.ToJson(), out JSONValue jsonRelease, "repos", Owner, Name, "releases"))
|
||||||
|
throw new Exception(string.Format("release could not be created => {0}", giteaRelease.ToJson()));
|
||||||
|
|
||||||
|
giteaRelease.UpdateFromJson(jsonRelease as JSONObject);
|
||||||
|
return giteaRelease;
|
||||||
|
}
|
||||||
|
|
||||||
public bool ContainsFile(string filename,string _ref) => (Client.GetJson(out JSONValue response, "repos", Owner, Name, "contents", string.Format("{0}?ref={1}",filename, _ref)) == HttpStatusCode.OK);
|
public bool ContainsFile(string filename,string _ref) => (Client.GetJson(out JSONValue response, "repos", Owner, Name, "contents", string.Format("{0}?ref={1}",filename, _ref)) == HttpStatusCode.OK);
|
||||||
|
|
||||||
|
public override void CommitAndPush(string message, string[] addedPaths, string[] modifiedPaths, string[] removedPaths)
|
||||||
|
{
|
||||||
|
CommandRunner cr = new CommandRunner("git","add");
|
||||||
|
cr.AddArguments(addedPaths);
|
||||||
|
cr.AddArguments(modifiedPaths);
|
||||||
|
cr.Run();
|
||||||
|
|
||||||
|
cr = new CommandRunner("git","rm");
|
||||||
|
cr.AddArguments(removedPaths);
|
||||||
|
cr.Run();
|
||||||
|
|
||||||
|
cr = new CommandRunner("git","commit", "-m", message);
|
||||||
|
cr.Run();
|
||||||
|
|
||||||
|
URI baseUri = new URI(BaseURL);
|
||||||
|
if (!AccessToken?.Equals(String.Empty) ?? false)
|
||||||
|
baseUri = baseUri.WithUserInfo(AccessToken);
|
||||||
|
|
||||||
|
cr = new CommandRunner("git","push", baseUri.ToString(true));
|
||||||
|
cr.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static HttpResponse WebHookHandler(CIService ciService, HttpRequest request)
|
public static HttpResponse WebHookHandler(CIService ciService, HttpRequest request)
|
||||||
|
@ -135,15 +179,27 @@ namespace ln.build.repositories.gitea
|
||||||
|
|
||||||
string eventType = request.GetRequestHeader("X-GITEA-EVENT");
|
string eventType = request.GetRequestHeader("X-GITEA-EVENT");
|
||||||
List<string> buildRefs = new List<string>();
|
List<string> buildRefs = new List<string>();
|
||||||
|
JSONObject jsonCommit = null;
|
||||||
|
|
||||||
switch (eventType)
|
switch (eventType)
|
||||||
{
|
{
|
||||||
case "release":
|
case "release":
|
||||||
|
if (!message["action"].ToNative().ToString().Equals("published"))
|
||||||
|
return response;
|
||||||
|
|
||||||
buildRefs.Add(message["release"]["tag_name"].ToNative().ToString());
|
buildRefs.Add(message["release"]["tag_name"].ToNative().ToString());
|
||||||
break;
|
break;
|
||||||
case "push":
|
case "push":
|
||||||
foreach (JSONObject jsonCommit in (message["commits"] as JSONArray).Children)
|
string currentCommitId = message["after"].ToNative().ToString();
|
||||||
buildRefs.Add(jsonCommit["id"].ToNative().ToString());
|
foreach (JSONObject _jsonCommit in (message["commits"] as JSONArray).Children)
|
||||||
|
{
|
||||||
|
if (currentCommitId.Equals(_jsonCommit["id"].ToNative().ToString()))
|
||||||
|
{
|
||||||
|
buildRefs.Add(_jsonCommit["id"].ToNative().ToString());
|
||||||
|
jsonCommit = _jsonCommit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logging.Log(LogLevel.WARNING, "received webhook with unsupported event type [{0}]", eventType);
|
Logging.Log(LogLevel.WARNING, "received webhook with unsupported event type [{0}]", eventType);
|
||||||
|
@ -159,6 +215,7 @@ namespace ln.build.repositories.gitea
|
||||||
.SetVariable("REPO_OWNER", owner)
|
.SetVariable("REPO_OWNER", owner)
|
||||||
.SetVariable("REPO_NAME", repoName)
|
.SetVariable("REPO_NAME", repoName)
|
||||||
.SetVariable("REPO_REF", _ref)
|
.SetVariable("REPO_REF", _ref)
|
||||||
|
.SetVariable("REPO_BASE", giteaRepository.BaseURL)
|
||||||
// .SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
|
// .SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
|
||||||
;
|
;
|
||||||
ciJob.Environment.SecretStorage = secretStorage;
|
ciJob.Environment.SecretStorage = secretStorage;
|
||||||
|
@ -170,6 +227,16 @@ namespace ln.build.repositories.gitea
|
||||||
ciJob.SetVariable("RELEASE_ID", message["release"]["id"].ToNative().ToString());
|
ciJob.SetVariable("RELEASE_ID", message["release"]["id"].ToNative().ToString());
|
||||||
break;
|
break;
|
||||||
case "push":
|
case "push":
|
||||||
|
if (jsonCommit != null)
|
||||||
|
{
|
||||||
|
string commitMessage = jsonCommit["message"].ToNative().ToString();
|
||||||
|
if (commitMessage.Contains("#ReleaseMajor"))
|
||||||
|
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.MAJOR);
|
||||||
|
else if (commitMessage.Contains("#ReleaseMinor"))
|
||||||
|
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.MINOR);
|
||||||
|
else if (commitMessage.Contains("#ReleasePatch"))
|
||||||
|
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.PATCH);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"CONFIGURATION": "Release",
|
||||||
|
"DOTNET_RIDS": "linux-x64:win-x64:osx-x64"
|
||||||
|
},
|
||||||
|
"stages": [
|
||||||
|
{
|
||||||
|
"name": "prepare",
|
||||||
|
"priority": 100,
|
||||||
|
"commands": [
|
||||||
|
"dotnet prepare *.sln *.csproj"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "build",
|
||||||
|
"priority": 300,
|
||||||
|
"commands": [
|
||||||
|
"dotnet build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"priority": 500,
|
||||||
|
"commands": [
|
||||||
|
"dotnet test"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pack",
|
||||||
|
"priority": 700,
|
||||||
|
"commands": [
|
||||||
|
"dotnet pack",
|
||||||
|
"dotnet publish"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "push",
|
||||||
|
"priority": 1000,
|
||||||
|
"commands": [
|
||||||
|
"dotnet push",
|
||||||
|
"release",
|
||||||
|
"deploy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ namespace ln.build.secrets
|
||||||
JSONObject secretsObject = JSONParser.ParseFile(FileName) as JSONObject;
|
JSONObject secretsObject = JSONParser.ParseFile(FileName) as JSONObject;
|
||||||
foreach (string key in secretsObject.Keys)
|
foreach (string key in secretsObject.Keys)
|
||||||
{
|
{
|
||||||
|
//Logging.Log(LogLevel.INFO, "loading secret {0}", key);
|
||||||
secrets.Add(key, secretsObject[key].ToNative().ToString());
|
secrets.Add(key, secretsObject[key].ToNative().ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,9 +37,15 @@ namespace ln.build.secrets
|
||||||
|
|
||||||
public string GetSecret(string key)
|
public string GetSecret(string key)
|
||||||
{
|
{
|
||||||
|
//Logging.Log(LogLevel.INFO, "trying to fetch secret for: [{0}]", key);
|
||||||
TryGetSecret(key, out string secret);
|
TryGetSecret(key, out string secret);
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
public bool TryGetSecret(string key, out string secret) => secrets.TryGetValue(key, out secret);
|
public bool TryGetSecret(string key, out string secret){
|
||||||
|
//Logging.Log(LogLevel.INFO, "trying to fetch secret for: [{0}]", key);
|
||||||
|
bool success = secrets.TryGetValue(key, out secret);
|
||||||
|
//Logging.Log(LogLevel.INFO, "{1} to fetch secret for: [{0}]", key, success ? "succeded" : "failed");
|
||||||
|
return success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
|
||||||
|
namespace ln.build.semver
|
||||||
|
{
|
||||||
|
public enum SemVerLevels {
|
||||||
|
MAJOR,
|
||||||
|
MINOR,
|
||||||
|
PATCH,
|
||||||
|
TAG
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Jint.Parser.Ast;
|
||||||
|
using ln.build.semver;
|
||||||
|
using Microsoft.VisualBasic;
|
||||||
|
|
||||||
|
namespace ln.build.semver
|
||||||
|
{
|
||||||
|
|
||||||
|
public class SemVersion
|
||||||
|
{
|
||||||
|
public int Major { get; set; }
|
||||||
|
public int Minor { get; set; }
|
||||||
|
public int Patch { get; set; }
|
||||||
|
public string PreRelease { get; set; } = String.Empty;
|
||||||
|
|
||||||
|
public bool IsPreRelease => !PreRelease?.Equals(string.Empty) ?? false;
|
||||||
|
public bool IsRelease => PreRelease?.Equals(string.Empty) ?? true;
|
||||||
|
|
||||||
|
public SemVersion(int major,int minor,int patch) : this(major, minor, patch, ""){ }
|
||||||
|
public SemVersion(int major,int minor,int patch,string prerelease)
|
||||||
|
{
|
||||||
|
Major = major;
|
||||||
|
Minor = minor;
|
||||||
|
Patch = patch;
|
||||||
|
PreRelease = prerelease;
|
||||||
|
}
|
||||||
|
private SemVersion(){}
|
||||||
|
public SemVersion(SemVersion source)
|
||||||
|
:this(source.Major, source.Minor, source.Patch, source.PreRelease)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
public void Increment(SemVerLevels level){
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case SemVerLevels.MAJOR:
|
||||||
|
Major++;
|
||||||
|
Minor = 0;
|
||||||
|
Patch = 0;
|
||||||
|
break;
|
||||||
|
case SemVerLevels.MINOR:
|
||||||
|
Minor++;
|
||||||
|
Patch = 0;
|
||||||
|
break;
|
||||||
|
case SemVerLevels.PATCH:
|
||||||
|
Patch++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.AppendFormat("{0}.{1}.{2}", Major, Minor, Patch);
|
||||||
|
if (!PreRelease?.Equals(string.Empty) ?? false)
|
||||||
|
stringBuilder.AppendFormat("-{0}", PreRelease);
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Regex rexVersion = new Regex("^(?<major>\\d+).(?<minor>\\d+).(?<patch>\\d+)(-(?<prerelease>.+))?$");
|
||||||
|
public static SemVersion Parse(string versionString)
|
||||||
|
{
|
||||||
|
Match match = rexVersion.Match(versionString);
|
||||||
|
if (!match.Success)
|
||||||
|
throw new FormatException(String.Format("{0} is no valid SemVer", versionString));
|
||||||
|
|
||||||
|
SemVersion version = new SemVersion();
|
||||||
|
version.Major = int.Parse(match.Groups["major"].Value);
|
||||||
|
version.Minor = int.Parse(match.Groups["minor"].Value);
|
||||||
|
version.Patch = int.Parse(match.Groups["patch"].Value);
|
||||||
|
if (match.Groups["prerelease"].Success)
|
||||||
|
version.PreRelease = match.Groups["prerelease"].Value;
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <(SemVersion a,SemVersion b)
|
||||||
|
{
|
||||||
|
if (a is null || b is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Major < b.Major)
|
||||||
|
return true;
|
||||||
|
if (a.Major > b.Major)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Minor < b.Minor)
|
||||||
|
return true;
|
||||||
|
if (a.Minor > b.Minor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Patch < b.Patch)
|
||||||
|
return true;
|
||||||
|
if (a.Patch > b.Patch)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.IsPreRelease && b.IsRelease)
|
||||||
|
return true;
|
||||||
|
if (a.IsRelease && b.IsPreRelease)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.IsRelease && b.IsRelease)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return a.PreRelease.CompareTo(b.PreRelease) > 0;
|
||||||
|
}
|
||||||
|
public static bool operator >(SemVersion b,SemVersion a)
|
||||||
|
{
|
||||||
|
if (a is null || b is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Major < b.Major)
|
||||||
|
return true;
|
||||||
|
if (a.Major > b.Major)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Minor < b.Minor)
|
||||||
|
return true;
|
||||||
|
if (a.Minor > b.Minor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.Patch < b.Patch)
|
||||||
|
return true;
|
||||||
|
if (a.Patch > b.Patch)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.IsPreRelease && b.IsRelease)
|
||||||
|
return true;
|
||||||
|
if (a.IsRelease && b.IsPreRelease)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a.IsRelease && b.IsRelease)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return a.PreRelease.CompareTo(b.PreRelease) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator SemVersion(string versionString) => versionString == null ? null : SemVersion.Parse(versionString);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.Xml;
|
||||||
|
using ln.build.semver;
|
||||||
|
|
||||||
|
namespace ln.build.semver.provider
|
||||||
|
{
|
||||||
|
|
||||||
|
public class DotNetProvider : Provider
|
||||||
|
{
|
||||||
|
public DotNetProvider(): base("dotnet")
|
||||||
|
{}
|
||||||
|
|
||||||
|
public override SemVersion GetVersion(string source)
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(source);
|
||||||
|
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/Version");
|
||||||
|
return SemVersion.Parse(nodeVersion.InnerText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetVersion(string source, SemVersion version)
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(source);
|
||||||
|
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/Version");
|
||||||
|
nodeVersion.InnerText = version.ToString();
|
||||||
|
|
||||||
|
projectFile.Save(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace ln.build.semver.provider
|
||||||
|
{
|
||||||
|
|
||||||
|
public abstract class Provider
|
||||||
|
{
|
||||||
|
|
||||||
|
public String Name { get; }
|
||||||
|
|
||||||
|
public Provider(string providerName)
|
||||||
|
{
|
||||||
|
Name = providerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SemVersion GetVersion(string source);
|
||||||
|
public abstract void SetVersion(string source, SemVersion version);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static Provider CreateProvider(string providerName)
|
||||||
|
{
|
||||||
|
switch (providerName)
|
||||||
|
{
|
||||||
|
case "dotnet":
|
||||||
|
return new DotNetProvider();
|
||||||
|
default:
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using ln.build.semver;
|
||||||
|
|
||||||
|
namespace ln.build.support.dotnet
|
||||||
|
{
|
||||||
|
|
||||||
|
public class CSProjHelper
|
||||||
|
{
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
public CSProjHelper(string filename)
|
||||||
|
{
|
||||||
|
FileName = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetName()
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(FileName);
|
||||||
|
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/AssemblyName");
|
||||||
|
return nodeVersion?.InnerText ?? Path.GetFileNameWithoutExtension(FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SemVersion GetVersion()
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(FileName);
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/PackageVersion") ?? projectFile.SelectSingleNode("Project/PropertyGroup/Version");
|
||||||
|
return (nodeVersion == null) ? null : SemVersion.Parse(nodeVersion.InnerText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVersion(SemVersion version)
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(FileName);
|
||||||
|
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/Version");
|
||||||
|
nodeVersion.InnerText = version.ToString();
|
||||||
|
|
||||||
|
projectFile.Save(FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPackable()
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(FileName);
|
||||||
|
XmlNode nodePackable = projectFile.SelectSingleNode("Project/PropertyGroup/IsPackable");
|
||||||
|
return bool.Parse(nodePackable?.InnerText ?? "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetOutputType()
|
||||||
|
{
|
||||||
|
XmlDocument projectFile = new XmlDocument();
|
||||||
|
projectFile.Load(FileName);
|
||||||
|
|
||||||
|
XmlNode nodeVersion = projectFile.SelectSingleNode("Project/PropertyGroup/OutputType");
|
||||||
|
return nodeVersion?.InnerText ?? "Library";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue