├── gitlab-ci-runner
├── runner
│ ├── State.cs
│ ├── Runner.cs
│ └── Build.cs
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── Program.cs
├── helper
│ ├── IniFile.cs
│ ├── SSHKey.cs
│ ├── TextFile.cs
│ └── Network.cs
├── setup
│ └── Setup.cs
├── api
│ └── API.cs
├── app.manifest
├── gitlab-ci-runner.csproj
└── conf
│ └── Config.cs
├── runner.Build.success.test
├── Properties
│ └── AssemblyInfo.cs
├── BuildTest.cs
└── runner.Build.success.test.csproj
├── gitlab-ci-runner.sln
├── .gitignore
└── README.md
/gitlab-ci-runner/runner/State.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace gitlab_ci_runner.runner
7 | {
8 | enum State
9 | {
10 | RUNNING,
11 | FAILED,
12 | SUCCESS,
13 | WAITING
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/runner.Build.success.test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("runner.Build.success.test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("runner.Build.success.test")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("74a91e53-3ebc-4154-b4cd-19357166d87a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/gitlab-ci-runner.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gitlab-ci-runner", "gitlab-ci-runner\gitlab-ci-runner.csproj", "{CA24BB4A-79A3-424E-9EDC-8B1871E2D601}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "runner.Build.success.test", "runner.Build.success.test\runner.Build.success.test.csproj", "{AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A1F260B-3C0C-4A98-8DA7-476C7AA7E303}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // Allgemeine Informationen über eine Assembly werden über die folgenden
6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
7 | // die mit einer Assembly verknüpft sind.
8 | [assembly: AssemblyTitle("gitlab-ci-runner")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("gitlab-ci-runner")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
19 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
20 | [assembly: ComVisible(false)]
21 |
22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
23 | [assembly: Guid("e7e13988-cb89-4ffd-8dda-d1aa99c3b5a7")]
24 |
25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
26 | //
27 | // Hauptversion
28 | // Nebenversion
29 | // Buildnummer
30 | // Revision
31 | //
32 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
33 | // übernehmen, indem Sie "*" eingeben:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
38 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("runner.Build.success.test")]
--------------------------------------------------------------------------------
/gitlab-ci-runner/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Text;
6 | using System.IO;
7 | using System.Reflection;
8 | using gitlab_ci_runner.conf;
9 | using gitlab_ci_runner.runner;
10 | using gitlab_ci_runner.setup;
11 |
12 | namespace gitlab_ci_runner
13 | {
14 | class Program
15 | {
16 | static void Main(string[] args)
17 | {
18 | Console.InputEncoding = Encoding.Default;
19 | Console.OutputEncoding = Encoding.Default;
20 | ServicePointManager.DefaultConnectionLimit = 999;
21 |
22 | if (args.Contains ("-sslbypass"))
23 | {
24 | Program.RegisterSecureSocketsLayerBypass ();
25 | }
26 |
27 | if (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).Substring(0, 1) == @"\") {
28 | Console.WriteLine("Can't run on UNC Path");
29 | } else {
30 | Console.WriteLine("Starting Gitlab CI Runner for Windows");
31 | Config.loadConfig();
32 | if (Config.isConfigured()) {
33 | // Load the runner
34 | Runner.run();
35 | } else {
36 | // Load the setup
37 | Setup.run();
38 | }
39 | }
40 | Console.WriteLine();
41 | Console.WriteLine("Runner quit. Press any key to exit!");
42 | Console.ReadKey();
43 | }
44 |
45 | static void RegisterSecureSocketsLayerBypass()
46 | {
47 | System.Net.ServicePointManager.ServerCertificateValidationCallback +=
48 | delegate (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
49 | System.Security.Cryptography.X509Certificates.X509Chain chain,
50 | System.Net.Security.SslPolicyErrors sslPolicyErrors)
51 | {
52 | return true; // **** Always accept
53 | };
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/helper/IniFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 |
7 | namespace gitlab_ci_runner.helper
8 | {
9 | ///
10 | /// .ini File Parser
11 | /// (c) http://www.codeproject.com/Articles/1966/An-INI-file-handling-class-using-C
12 | ///
13 | class IniFile
14 | {
15 | public string path;
16 |
17 | [DllImport("kernel32")]
18 | private static extern long WritePrivateProfileString(string section,
19 | string key, string val, string filePath);
20 | [DllImport("kernel32")]
21 | private static extern int GetPrivateProfileString(string section,
22 | string key, string def, StringBuilder retVal,
23 | int size, string filePath);
24 |
25 | ///
26 | /// INIFile Constructor.
27 | ///
28 | /// Path to the .ini File
29 | public IniFile(string INIPath)
30 | {
31 | path = INIPath;
32 | }
33 | ///
34 | /// Write Data to the INI File
35 | ///
36 | /// Section name
37 | /// Key Name
38 | /// Value Name
39 | public void IniWriteValue(string Section, string Key, string Value)
40 | {
41 | WritePrivateProfileString(Section, Key, Value, this.path);
42 | }
43 |
44 | ///
45 | /// Read Data Value From the Ini File
46 | ///
47 | /// Section name
48 | /// Key Name
49 | /// Value
50 | public string IniReadValue(string Section, string Key)
51 | {
52 | StringBuilder temp = new StringBuilder(255);
53 | int i = GetPrivateProfileString(Section, Key, "", temp,
54 | 255, this.path);
55 | return temp.ToString();
56 |
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/setup/Setup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using gitlab_ci_runner.conf;
6 | using gitlab_ci_runner.helper;
7 |
8 | namespace gitlab_ci_runner.setup
9 | {
10 | class Setup
11 | {
12 | ///
13 | /// Start the Setup
14 | ///
15 | public static void run()
16 | {
17 | Console.WriteLine("This seems to be the first run,");
18 | Console.WriteLine("please provide the following info to proceed:");
19 | Console.WriteLine();
20 |
21 | // Read coordinator URL
22 | String sCoordUrl = "";
23 | while (sCoordUrl == "")
24 | {
25 | Console.WriteLine("Please enter the gitlab-ci coordinator URL (e.g. http(s)://gitlab-ci.org:3000/ )");
26 | sCoordUrl = Console.ReadLine();
27 | }
28 | Config.url = sCoordUrl;
29 | Console.WriteLine();
30 |
31 | // Generate SSH Keys
32 | SSHKey.generateKeypair();
33 |
34 | // Register Runner
35 | registerRunner();
36 | }
37 |
38 | ///
39 | /// Register the runner with the coordinator
40 | ///
41 | private static void registerRunner()
42 | {
43 | // Read Token
44 | string sToken = "";
45 | while (sToken == "")
46 | {
47 | Console.WriteLine("Please enter the gitlab-ci token for this runner:");
48 | sToken = Console.ReadLine();
49 | }
50 |
51 | // Register Runner
52 | string sTok = Network.registerRunner(SSHKey.getPublicKey(), sToken);
53 | if (sTok != null)
54 | {
55 | // Save Config
56 | Config.token = sTok;
57 | Config.saveConfig();
58 |
59 | Console.WriteLine();
60 | Console.WriteLine("Runner registered successfully. Feel free to start it!");
61 | }
62 | else
63 | {
64 | Console.WriteLine();
65 | Console.WriteLine("Failed to register this runner. Perhaps your SSH key is invalid or you are having network problems");
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/api/API.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using ServiceStack;
6 |
7 |
8 | ///
9 | /// V1 API
10 | ///
11 | namespace gitlab_ci_runner.api
12 | {
13 | [Route ("/runners/register.json","POST")]
14 | public class RegisterRunner : IReturn
15 | {
16 | public string token
17 | {
18 | get;
19 | set;
20 | }
21 |
22 | public string public_key
23 | {
24 | get;
25 | set;
26 | }
27 | }
28 |
29 | [Route ("/builds/register.json", "POST")]
30 | public class CheckForBuild : IReturn
31 | {
32 | public string token
33 | {
34 | get;
35 | set;
36 | }
37 | }
38 |
39 | [Route ("/builds/{id}", "PUT")]
40 | public class PushBuild : IReturn
41 | {
42 | public string id
43 | {
44 | get;
45 | set;
46 | }
47 |
48 | public string token
49 | {
50 | get;
51 | set;
52 | }
53 |
54 | public string state
55 | {
56 | get;
57 | set;
58 | }
59 |
60 | public string trace
61 | {
62 | get;
63 | set;
64 | }
65 | }
66 |
67 | public class RunnerInfo
68 | {
69 | public int id
70 | {
71 | get;
72 | set;
73 | }
74 | public string token
75 | {
76 | get;
77 | set;
78 | }
79 | }
80 |
81 | public class BuildInfo
82 | {
83 | public int id
84 | {
85 | get;
86 | set;
87 | }
88 |
89 | public int project_id
90 | {
91 | get;
92 | set;
93 | }
94 |
95 | public string project_name
96 | {
97 | get;
98 | set;
99 | }
100 |
101 | public string commands
102 | {
103 | get;
104 | set;
105 | }
106 |
107 | public string repo_url
108 | {
109 | get;
110 | set;
111 | }
112 |
113 | public string sha
114 | {
115 | get;
116 | set;
117 | }
118 |
119 | public string before_sha
120 | {
121 | get;
122 | set;
123 | }
124 |
125 | public string @ref
126 | {
127 | get;
128 | set;
129 | }
130 | public int timeout
131 | {
132 | get;
133 | set;
134 | }
135 |
136 | public bool allow_git_fetch
137 | {
138 | get;
139 | set;
140 | }
141 |
142 | public string[] GetCommands()
143 | {
144 | return System.Text.RegularExpressions.Regex.Replace (this.commands, "(\r|\n)+", "\n").Split ('\n');
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/helper/SSHKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 |
9 | namespace gitlab_ci_runner.helper
10 | {
11 | class SSHKey
12 | {
13 | ///
14 | /// Generate a keypair
15 | ///
16 | public static void generateKeypair()
17 | {
18 | // We need both, the Public and Private Key
19 | // Public Key to send to Gitlab
20 | // Private Key to connect to Gitlab later
21 | if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa.pub") &&
22 | File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa"))
23 | {
24 | return;
25 | }
26 |
27 | try {
28 | Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh");
29 | } catch (Exception) {}
30 | Process p = new Process();
31 | p.StartInfo.FileName = "ssh-keygen";
32 | p.StartInfo.Arguments = "-t rsa -f \"" + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\.ssh\\id_rsa\" -N \"\"";
33 | p.Start();
34 | Console.WriteLine();
35 | Console.WriteLine("Waiting for SSH Key to be generated ...");
36 | p.WaitForExit();
37 | while (!File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa.pub") &&
38 | !File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa"))
39 | {
40 | Thread.Sleep(1000);
41 | }
42 | Console.WriteLine("SSH Key generated successfully!");
43 | }
44 |
45 | ///
46 | /// Get the public key
47 | ///
48 | ///
49 | public static string getPublicKey()
50 | {
51 | if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa.pub"))
52 | {
53 | return TextFile.ReadFile(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.ssh\id_rsa.pub");
54 | }
55 | else
56 | {
57 | return null;
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/runner.Build.success.test/BuildTest.cs:
--------------------------------------------------------------------------------
1 | using gitlab_ci_runner.runner;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 | using gitlab_ci_runner.api;
5 |
6 | namespace runner.Build.success.test
7 | {
8 |
9 |
10 | ///
11 | ///This is a test class for BuildTest and is intended
12 | ///to contain all BuildTest Unit Tests
13 | ///
14 | [TestClass()]
15 | public class BuildTest
16 | {
17 |
18 |
19 | private TestContext testContextInstance;
20 |
21 | ///
22 | ///Gets or sets the test context which provides
23 | ///information about and functionality for the current test run.
24 | ///
25 | public TestContext TestContext
26 | {
27 | get
28 | {
29 | return testContextInstance;
30 | }
31 | set
32 | {
33 | testContextInstance = value;
34 | }
35 | }
36 |
37 | #region Additional test attributes
38 | //
39 | //You can use the following additional attributes as you write your tests:
40 | //
41 | //Use ClassInitialize to run code before running the first test in the class
42 | //[ClassInitialize()]
43 | //public static void MyClassInitialize(TestContext testContext)
44 | //{
45 | //}
46 | //
47 | //Use ClassCleanup to run code after all tests in a class have run
48 | //[ClassCleanup()]
49 | //public static void MyClassCleanup()
50 | //{
51 | //}
52 | //
53 | //Use TestInitialize to run code before running each test
54 | //[TestInitialize()]
55 | //public void MyTestInitialize()
56 | //{
57 | //}
58 | //
59 | //Use TestCleanup to run code after each test has run
60 | //[TestCleanup()]
61 | //public void MyTestCleanup()
62 | //{
63 | //}
64 | //
65 | #endregion
66 |
67 |
68 | ///
69 | ///A test for run
70 | ///
71 | [TestMethod()]
72 | public void runTest()
73 | {
74 | // copied from official gitlab ci runner spec
75 | BuildInfo buildInfo = new BuildInfo();
76 | buildInfo.commands = "dir";
77 | buildInfo.allow_git_fetch = false;
78 | buildInfo.project_id = 0;
79 | buildInfo.id = 9312;
80 | buildInfo.repo_url = "https://github.com/randx/six.git";
81 | buildInfo.sha = "2e008a711430a16092cd6a20c225807cb3f51db7";
82 | buildInfo.timeout = 1800;
83 | buildInfo.@ref = "master";
84 |
85 | gitlab_ci_runner.runner.Build target = new gitlab_ci_runner.runner.Build(buildInfo);
86 | target.run();
87 | Console.WriteLine(target.output);
88 | Assert.AreEqual(target.state, State.SUCCESS);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # VS files
10 | *.vsmdi
11 | *.testsettings
12 |
13 | # Build results
14 |
15 | [Dd]ebug/
16 | [Rr]elease/
17 | x64/
18 | build/
19 | [Bb]in/
20 | [Oo]bj/
21 |
22 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
23 | !packages/*/build/
24 |
25 | # MSTest test Results
26 | [Tt]est[Rr]esult*/
27 | [Bb]uild[Ll]og.*
28 |
29 | *_i.c
30 | *_p.c
31 | *.ilk
32 | *.meta
33 | *.obj
34 | *.pch
35 | *.pdb
36 | *.pgc
37 | *.pgd
38 | *.rsp
39 | *.sbr
40 | *.tlb
41 | *.tli
42 | *.tlh
43 | *.tmp
44 | *.tmp_proj
45 | *.log
46 | *.vspscc
47 | *.vssscc
48 | .builds
49 | *.pidb
50 | *.log
51 | *.scc
52 |
53 | # Visual C++ cache files
54 | ipch/
55 | *.aps
56 | *.ncb
57 | *.opensdf
58 | *.sdf
59 | *.cachefile
60 |
61 | # Visual Studio profiler
62 | *.psess
63 | *.vsp
64 | *.vspx
65 |
66 | # Guidance Automation Toolkit
67 | *.gpState
68 |
69 | # ReSharper is a .NET coding add-in
70 | _ReSharper*/
71 | *.[Rr]e[Ss]harper
72 |
73 | # TeamCity is a build add-in
74 | _TeamCity*
75 |
76 | # DotCover is a Code Coverage Tool
77 | *.dotCover
78 |
79 | # NCrunch
80 | *.ncrunch*
81 | .*crunch*.local.xml
82 |
83 | # Installshield output folder
84 | [Ee]xpress/
85 |
86 | # DocProject is a documentation generator add-in
87 | DocProject/buildhelp/
88 | DocProject/Help/*.HxT
89 | DocProject/Help/*.HxC
90 | DocProject/Help/*.hhc
91 | DocProject/Help/*.hhk
92 | DocProject/Help/*.hhp
93 | DocProject/Help/Html2
94 | DocProject/Help/html
95 |
96 | # Click-Once directory
97 | publish/
98 |
99 | # Publish Web Output
100 | *.Publish.xml
101 | *.pubxml
102 |
103 | # NuGet Packages Directory
104 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
105 | packages/
106 |
107 | # Windows Azure Build Output
108 | csx
109 | *.build.csdef
110 |
111 | # Windows Store app package directory
112 | AppPackages/
113 |
114 | # Others
115 | sql/
116 | *.Cache
117 | ClientBin/
118 | [Ss]tyle[Cc]op.*
119 | ~$*
120 | *~
121 | *.dbmdl
122 | *.[Pp]ublish.xml
123 | *.pfx
124 | *.publishsettings
125 |
126 | # RIA/Silverlight projects
127 | Generated_Code/
128 |
129 | # Backup & report files from converting an old project file to a newer
130 | # Visual Studio version. Backup files are not needed, because we have git ;-)
131 | _UpgradeReport_Files/
132 | Backup*/
133 | UpgradeLog*.XML
134 | UpgradeLog*.htm
135 |
136 | # SQL Server files
137 | App_Data/*.mdf
138 | App_Data/*.ldf
139 |
140 | # =========================
141 | # Windows detritus
142 | # =========================
143 |
144 | # Windows image file caches
145 | Thumbs.db
146 | ehthumbs.db
147 |
148 | # Folder config file
149 | Desktop.ini
150 |
151 | # Recycle Bin used on file shares
152 | $RECYCLE.BIN/
153 |
154 | # Mac crap
155 | .DS_Store
156 |
--------------------------------------------------------------------------------
/runner.Build.success.test/runner.Build.success.test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 |
7 |
8 | 2.0
9 | {AD9B6A82-AEBD-445B-B37E-C2C425BDDC76}
10 | Library
11 | Properties
12 | runner.Build.success.test
13 | runner.Build.success.test
14 | v4.0
15 | 512
16 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 | ..\packages\ServiceStack.Text.3.9.63\lib\net35\ServiceStack.Text.dll
40 |
41 |
42 |
43 | 3.5
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | False
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}
62 | gitlab-ci-runner
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/runner/Runner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using gitlab_ci_runner.api;
7 | using gitlab_ci_runner.helper;
8 |
9 | namespace gitlab_ci_runner.runner
10 | {
11 | class Runner
12 | {
13 | ///
14 | /// Build process
15 | ///
16 | private static Build build = null;
17 |
18 | ///
19 | /// Start the configured runner
20 | ///
21 | public static void run()
22 | {
23 | Console.WriteLine("* Gitlab CI Runner started");
24 | Console.WriteLine("* Waiting for builds");
25 | waitForBuild();
26 | }
27 |
28 | ///
29 | /// Build completed?
30 | ///
31 | public static bool completed
32 | {
33 | get
34 | {
35 | return running && build.completed;
36 | }
37 | }
38 |
39 | ///
40 | /// Build running?
41 | ///
42 | public static bool running
43 | {
44 | get
45 | {
46 | return build != null;
47 | }
48 | }
49 |
50 | ///
51 | /// Wait for an incoming build or update current Build
52 | ///
53 | private static void waitForBuild()
54 | {
55 | while (true)
56 | {
57 | if (completed || running)
58 | {
59 | // Build is running or completed
60 | // Update build
61 | updateBuild();
62 | }
63 | else
64 | {
65 | // Get new build
66 | getBuild();
67 | }
68 | Thread.Sleep(5000);
69 | }
70 | }
71 |
72 | ///
73 | /// Update the current running build progress
74 | ///
75 | private static void updateBuild()
76 | {
77 | if (build.completed)
78 | {
79 | // Build finished
80 | if (pushBuild())
81 | {
82 | Console.WriteLine("[" + DateTime.Now.ToString() + "] Completed build " + build.buildInfo.id);
83 | build = null;
84 | }
85 | }
86 | else
87 | {
88 | // Build is currently running
89 | pushBuild();
90 | }
91 | }
92 |
93 | ///
94 | /// PUSH Build Status to Gitlab CI
95 | ///
96 | /// true on success, false on fail
97 | private static bool pushBuild()
98 | {
99 | return Network.pushBuild(build.buildInfo.id, build.state, build.output);
100 | }
101 |
102 | ///
103 | /// Get a new build job
104 | ///
105 | private static void getBuild()
106 | {
107 | BuildInfo binfo = Network.getBuild();
108 | if (binfo != null)
109 | {
110 | // Create Build Job
111 | build = new Build(binfo);
112 | Console.WriteLine("[" + DateTime.Now.ToString() + "] Build " + binfo.id + " started...");
113 | Thread t = new Thread(build.run);
114 | t.Start();
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/gitlab-ci-runner.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {CA24BB4A-79A3-424E-9EDC-8B1871E2D601}
8 | Exe
9 | Properties
10 | gitlab_ci_runner
11 | gitlab-ci-runner
12 | v4.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | none
28 | true
29 | bin\Release\
30 |
31 |
32 | prompt
33 | 4
34 | false
35 | Off
36 |
37 |
38 | app.manifest
39 |
40 |
41 |
42 | ..\packages\ini-parser.2.1.1\lib\INIFileParser.dll
43 |
44 |
45 | ..\packages\Microsoft.Experimental.IO.1.0.0.0\lib\NETFramework40\Microsoft.Experimental.IO.dll
46 |
47 |
48 | ..\packages\ServiceStack.Client.4.0.20\lib\net40\ServiceStack.Client.dll
49 |
50 |
51 | ..\packages\ServiceStack.Interfaces.4.0.20\lib\net40\ServiceStack.Interfaces.dll
52 |
53 |
54 | False
55 | ..\packages\ServiceStack.Text.4.0.20\lib\net40\ServiceStack.Text.dll
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/helper/TextFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace gitlab_ci_runner.helper
8 | {
9 | class TextFile
10 | {
11 | ///
12 | /// Get the content of the file
13 | ///
14 | ///File path and name
15 | public static string ReadFile(String sFilename)
16 | {
17 | string sContent = "";
18 | if (File.Exists(sFilename))
19 | {
20 | StreamReader myFile = new StreamReader(sFilename, System.Text.Encoding.Default);
21 | sContent = myFile.ReadToEnd();
22 | myFile.Close();
23 | }
24 | return sContent;
25 | }
26 |
27 | ///
28 | /// Writes a file
29 | ///
30 | ///File path and name
31 | ///Content
32 | public static void WriteFile(String sFilename, String sLines)
33 | {
34 | StreamWriter myFile = new StreamWriter(sFilename);
35 | myFile.Write(sLines);
36 | myFile.Close();
37 | }
38 |
39 | ///
40 | /// Appends the file
41 | ///
42 | ///File path and name
43 | ///Content
44 | public static void Append(string sFilename, string sLines)
45 | {
46 | StreamWriter myFile = new StreamWriter(sFilename, true);
47 | myFile.Write(sLines);
48 | myFile.Close();
49 | }
50 |
51 | ///
52 | /// Get the content of a defined line
53 | ///
54 | ///File path and name
55 | ///Line number
56 | public static string ReadLine(String sFilename, int iLine)
57 | {
58 | string sContent = "";
59 | float fRow = 0;
60 | if (File.Exists(sFilename))
61 | {
62 | StreamReader myFile = new StreamReader(sFilename, System.Text.Encoding.Default);
63 | while (!myFile.EndOfStream && fRow < iLine)
64 | {
65 | fRow++;
66 | sContent = myFile.ReadLine();
67 | }
68 | myFile.Close();
69 | if (fRow < iLine)
70 | sContent = "";
71 | }
72 | return sContent;
73 | }
74 |
75 | ///
76 | /// Writes into a defined line
77 | ///
78 | ///File path and name
79 | ///Line number
80 | ///Content of the line
81 | ///Replace the lines content (true) or append (false)
82 | public static void WriteLine(String sFilename, int iLine, string sLines, bool bReplace)
83 | {
84 | string sContent = "";
85 | string[] delimiterstring = { "\r\n" };
86 | if (File.Exists(sFilename))
87 | {
88 | StreamReader myFile = new StreamReader(sFilename, System.Text.Encoding.Default);
89 | sContent = myFile.ReadToEnd();
90 | myFile.Close();
91 | }
92 | string[] sCols = sContent.Split(delimiterstring, StringSplitOptions.None);
93 | if (sCols.Length >= iLine)
94 | {
95 | if (!bReplace)
96 | sCols[iLine - 1] = sLines + "\r\n" + sCols[iLine - 1];
97 | else
98 | sCols[iLine - 1] = sLines;
99 | sContent = "";
100 | for (int x = 0; x < sCols.Length - 1; x++)
101 | {
102 | sContent += sCols[x] + "\r\n";
103 | }
104 | sContent += sCols[sCols.Length - 1];
105 | }
106 | else
107 | {
108 | for (int x = 0; x < iLine - sCols.Length; x++)
109 | sContent += "\r\n";
110 | sContent += sLines;
111 | }
112 | StreamWriter mySaveFile = new StreamWriter(sFilename);
113 | mySaveFile.Write(sContent);
114 | mySaveFile.Close();
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/helper/Network.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Web;
9 | using gitlab_ci_runner.api;
10 | using gitlab_ci_runner.conf;
11 | using gitlab_ci_runner.runner;
12 | using ServiceStack;
13 | using ServiceStack.Text;
14 |
15 | namespace gitlab_ci_runner.helper
16 | {
17 | class Network
18 | {
19 | ///
20 | /// Gitlab CI API URL
21 | ///
22 | private static string apiurl
23 | {
24 | get
25 | {
26 | return Config.url + "/api/v1/";
27 | }
28 | }
29 |
30 | ///
31 | /// Register the runner with the coordinator
32 | ///
33 | /// SSH Public Key
34 | /// Token
35 | /// Token
36 | public static string registerRunner(String sPubKey, String sToken)
37 | {
38 | var client = new JsonServiceClient (apiurl);
39 | try
40 | {
41 | var authToken = client.Post (new RegisterRunner
42 | {
43 | token = Uri.EscapeDataString (sToken),
44 | public_key = Uri.EscapeDataString (sPubKey)
45 | });
46 |
47 | if (!authToken.token.IsNullOrEmpty ())
48 | {
49 | Console.WriteLine ("Runner registered with id {0}", authToken.id);
50 | return authToken.token;
51 | }
52 | else
53 | {
54 | return null;
55 | }
56 | }
57 | catch(WebException ex)
58 | {
59 | Console.WriteLine ("Error while registering runner :", ex.Message);
60 | return null;
61 | }
62 | }
63 |
64 | ///
65 | /// Get a new build
66 | ///
67 | /// BuildInfo object or null on error/no build
68 | public static BuildInfo getBuild()
69 | {
70 | Console.WriteLine("* Checking for builds...");
71 | var client = new JsonServiceClient (apiurl);
72 | try
73 | {
74 | var buildInfo = client.Post (new CheckForBuild
75 | {
76 | token = Uri.EscapeDataString (Config.token)
77 | });
78 |
79 | if (buildInfo != null)
80 | {
81 | return buildInfo;
82 | }
83 | }
84 | catch (WebServiceException ex)
85 | {
86 | if(ex.StatusCode == 404)
87 | {
88 | Console.WriteLine ("* Nothing");
89 | }
90 | else
91 | {
92 | Console.WriteLine ("* Failed");
93 | }
94 | }
95 |
96 | return null;
97 | }
98 |
99 | ///
100 | /// PUSH the Build to the Gitlab CI Coordinator
101 | ///
102 | /// Build ID
103 | /// State
104 | /// Command output
105 | ///
106 | public static bool pushBuild(int iId, State state, string sTrace)
107 | {
108 | Console.WriteLine("[" + DateTime.Now + "] Submitting build " + iId + " to coordinator ...");
109 |
110 | var stateValue = "";
111 | if (state == State.RUNNING)
112 | {
113 | stateValue = "running";
114 | }
115 | else if (state == State.SUCCESS)
116 | {
117 | stateValue = "success";
118 | }
119 | else if (state == State.FAILED)
120 | {
121 | stateValue = "failed";
122 | }
123 | else
124 | {
125 | stateValue = "waiting";
126 | }
127 |
128 | var trace = new StringBuilder();
129 | foreach (string t in sTrace.Split('\n'))
130 | trace.Append(t).Append("\n");
131 |
132 | int iTry = 0;
133 | while (iTry <= 5)
134 | {
135 | try
136 | {
137 | var client = new JsonServiceClient(apiurl);
138 | var resp = client.Put (new PushBuild {
139 | id = iId + ".json",
140 | token = Uri.EscapeDataString(Config.token),
141 | state = stateValue,
142 | trace = trace.ToString () });
143 |
144 | if (resp != null)
145 | {
146 | return true;
147 | }
148 | }
149 | catch
150 | { }
151 |
152 | iTry++;
153 | Thread.Sleep(1000);
154 | }
155 |
156 | return false;
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Gitlab CI Runner for Windows
2 | ============================
3 |
4 | This is an unofficial runner for [Gitlab CI](https://github.com/gitlabhq/gitlab-ci) under Windows.
5 |
6 | Requirements
7 | ------------
8 |
9 | This program only runs under Windows operating systems.
10 |
11 | For Linux operating systems, take a look at the [official runner for linux](https://github.com/gitlabhq/gitlab-ci-runner).
12 |
13 | The following programs are needed:
14 |
15 | [msysgit](http://msysgit.github.io/) (Install with openssh style ssh key support, not with putty keys)
16 |
17 | Don't forget to add the `git/bin` directory (usually `C:\Program Files\Git\bin`) to your `PATH` variable!
18 |
19 | Installation
20 | ------------
21 |
22 | Just checkout this repository, open the project in Visual Studio 2013 and compile it yourself.
23 |
24 | The configuration initialization begins on the first run.
25 |
26 | After the first run the config is written and you can start the runner in the background.
27 |
28 | To register the runner as service try the [NSSM](http://nssm.cc/). But remember to run the service as the user you created the config with!
29 |
30 | Options
31 | -------
32 |
33 | If your CI server is using a self-signed certificate you can add -sslbypass option in argument
34 |
35 | How it works
36 | ------------
37 |
38 | The whole thing works in the following way:
39 |
40 | 1. The runner should be endlessly up and listen to the commands from CI (this is why you should use NSSM or its equivalent).
41 | 2. As soon as CI sends a command to the runner it performs the preparation actions (clones or fetches the project from the server to some folder)
42 | 3. When the project is on the runner's machine, it executes the script you write in the CI control panel.
43 |
44 | You may need an additional control how the runner updates the project. For example, you may want to use a particular project folder. Or avoid using `git reset --hard` before each update. Or use `git clone --recursive` instead of simple `git clone`.
45 |
46 | To do it, you can additionally configure runner as explained in the next section.
47 |
48 | Additional configuration
49 | ------------------------
50 |
51 | After the first launch, the runner creates a `runner.cfg` file which holds the URL to the Gitlab CI server and a runner token you should insert when registering the runner there:
52 |
53 | ```
54 | [main]
55 | url=http://ci.gitlab.example.com/
56 | token=
57 | ```
58 |
59 | You may add extra sections (see below how you name them) which may hold the following keys:
60 |
61 | - `ProjectDir` - a directory where the project should be created. It may be relative to the runner folder or absolute.
62 | - `NewRepoInitCommand` - a command which should prepare a new repo
63 | - `ExistingRepoInitCommand` - a command which should update a repo if it already exists
64 | - `PostPrepareCommand` - a command which is called after `NewRepoInitCommand` or `ExistingRepoInitCommand` is executed.
65 |
66 | ### About commands
67 |
68 | Commands are executed in the `ProjectDir`, so there is no need to `cd` there.
69 |
70 | If you need multiple commands, you can either put concatenate them with `&&` (`git clone {repo_url} && git checkout {commit}`) or put
71 | them to a `.bat` file.
72 |
73 | If you don't specify commands, by default it will be:
74 |
75 | ```
76 | NewRepoInitCommand=git clone {repo_url} {project_dir} && cd {project_dir} && git checkout {commit}
77 | ExistingRepoInitCommand=git reset --hard && git clean -f && git fetch && git checkout {commit}
78 | ```
79 |
80 | ### Placeholders
81 |
82 | Each of these keys may contain placeholders which will be replaced by a data sent from CI:
83 |
84 | - `{build_id}` - build ID provided by Gitlab CI server
85 | - `{project_id}` - project ID provided by Gitlab CI (note, not by Gitlab itself!)
86 | - `{project_name}` - a project name including namespace, without whitespaces (e.g. Group1/myproject)
87 | - `{project_dir}` - won't be applied for the ProjectDir key.
88 | - `{commit}` - uuid which identifies a current commit
89 | - `{previous_commit}` - uuid of the previous commit
90 | - `{repo_url}` - repo URL
91 | - `{ref_name}` - ref name
92 |
93 | ### Defining different settings for different projects
94 |
95 | The same runner may be reused by multiple projects. You may want to have different project with different settings.
96 |
97 | To achieve this, you should refer the project in the section name. You can do it either using ID or name:
98 |
99 | `[id=123]`
100 |
101 | or
102 |
103 | `[name=group1/myprojectname]`
104 |
105 | You can use the same commands for several projects by separating them with a `|` sign:
106 |
107 | `[id=123|id=234|name=group1/someproject]`
108 |
109 | At last, you may specify the universal rules using `*` sign:
110 |
111 | `[*]`
112 |
113 | Note, you should put `[*]` in the end of config, otherwise it will ignore settings for your specific project. You may think about it as `default` in the `switch...case` block.
114 |
115 | ### Putting things together
116 |
117 | It is a time for an example:
118 |
119 | ``` ini
120 | [main]
121 | url=http://ci.gitlab.example.com/
122 | token=
123 | [id=123]
124 | ProjectDir=C:\CI\MyTest
125 | NewRepoInitCommand=git clone --recursive {repo_url}
126 | [name=somegroup/someproject|id=124]
127 | ProjectDir=C:\CI\{project_name}
128 | NewRepoInitCommand=myclonecommands.bat {project_id} {repo_url}
129 | ExistingRepoInitCommand=myfetchcommands.bat {project_id} {repo_url}
130 | [*]
131 | ProjectDir=C:\defaultprojectdir\project_{project_id}
132 | PostPrepareDir=echo "ProjDir: {project_dir}, ProjID: {project_id}, ProjName={project_name}, BuildID: {build_id}, RepoUrl: {repo_url}, RefName: {ref_name}, SHA: {sha}, BeforeSHA: {before_sha}"
133 | ```
134 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/conf/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.Serialization.Formatters.Binary;
7 | using System.Text;
8 | using System.Text.RegularExpressions;
9 | using gitlab_ci_runner.helper;
10 | using gitlab_ci_runner.api;
11 |
12 | namespace gitlab_ci_runner.conf
13 | {
14 |
15 | public class Config
16 | {
17 |
18 | public class PrebuildConfig
19 | {
20 | private static string GetValue(IniParser.Model.KeyDataCollection keys, string name)
21 | {
22 | return keys.ContainsKey(name) ? keys[name] : "";
23 | }
24 |
25 | internal PrebuildConfig(BuildInfo b, IniParser.Model.SectionData data)
26 | {
27 | /*
28 | _clone = new CmdConfig(b, data, "Clone");
29 | _checkout = new CmdConfig(b, data, "Checkout");
30 | _fetch = new CmdConfig(b, data, "Fetch");
31 | */
32 |
33 | _projectDir = GetValue(data.Keys, "ProjectDir");
34 | _newRepoInit = GetValue(data.Keys, "NewRepoInitCommand");
35 | _existingRepoInit = GetValue(data.Keys, "ExistingRepoInitCommand");
36 | _postPrepare = GetValue(data.Keys, "PostPrepareCommand");
37 | var r = new[] {
38 | new { key = "{project_dir}", val = "" },
39 | new { key = "{build_id}", val = b.id.ToString() },
40 | new { key = "{project_id}", val = b.project_id.ToString() },
41 | new { key = "{project_name}", val = Regex.Replace(b.project_name, @"\s+", "") },
42 | new { key = "{commit}", val = b.sha },
43 | new { key = "{previous_commit}", val = b.before_sha },
44 | new { key = "{repo_url}", val = b.repo_url },
45 | new { key = "{ref_name}", val = b.ref_name } };
46 |
47 | foreach(var rule in r)
48 | {
49 | _projectDir = _projectDir.Replace(rule.key, rule.val);
50 | }
51 |
52 | r[0] = new { key = "{project_dir}", val =_projectDir};
53 | foreach (var rule in r)
54 | {
55 | _newRepoInit = _newRepoInit.Replace(rule.key, rule.val);
56 | _existingRepoInit = _existingRepoInit.Replace(rule.key, rule.val);
57 | _postPrepare = _postPrepare.Replace(rule.key, rule.val);
58 | }
59 |
60 | }
61 |
62 | private string _projectDir;
63 | private string _postPrepare;
64 | private string _newRepoInit;
65 | private string _existingRepoInit;
66 |
67 | public string ProjectDir { get { return _projectDir; } }
68 | public string NewRepoInit { get { return _newRepoInit; } }
69 | public string ExistingRepoInit { get { return _existingRepoInit; } }
70 | public string PostPrepare { get { return _postPrepare; } }
71 | }
72 |
73 | ///
74 | /// URL to the Gitlab CI coordinator
75 | ///
76 | public static string url;
77 |
78 | ///
79 | /// Gitlab CI runner auth token
80 | ///
81 | public static string token;
82 |
83 | ///
84 | /// Configuration Path
85 | ///
86 | private static string confPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\runner.cfg";
87 |
88 | ///
89 | /// Load the configuration
90 | ///
91 | public static void loadConfig()
92 | {
93 | if (File.Exists(confPath))
94 | {
95 | IniFile ini = new IniFile(confPath);
96 | url = ini.IniReadValue("main", "url");
97 | token = ini.IniReadValue("main", "token");
98 | }
99 | }
100 |
101 | ///
102 | /// Save the configuration
103 | ///
104 | public static void saveConfig()
105 | {
106 | if (File.Exists(confPath))
107 | {
108 | File.Delete(confPath);
109 | }
110 |
111 | IniFile ini = new IniFile(confPath);
112 | ini.IniWriteValue("main", "url", url);
113 | ini.IniWriteValue("main", "token", token);
114 | }
115 |
116 | public static PrebuildConfig getDataForBuild(gitlab_ci_runner.api.BuildInfo b)
117 | {
118 | if (!isConfigured())
119 | {
120 | throw new NotImplementedException("Situation when no configuration file is provided is not supported yet. Make sure that runner.cfg exists at the file location.");
121 | }
122 | IniParser.FileIniDataParser ini = new IniParser.FileIniDataParser();
123 | IniParser.Model.IniData data = ini.ReadFile(confPath);
124 |
125 | IniParser.Model.SectionData section = null;
126 | foreach (var sect in data.Sections)
127 | {
128 | string[] projectIdentifiers = sect.SectionName.Split(new char[] {'|'} );
129 | foreach(var p in projectIdentifiers)
130 | {
131 | string[] components = p.Split( new char[] {'='} );
132 | if (components.Length == 2)
133 | {
134 | switch (components[0])
135 | {
136 | case "id":
137 | if (Convert.ToInt32(components[1]) == b.project_id)
138 | {
139 | section = sect;
140 | }
141 | break;
142 | case "name":
143 | if (components[1] == b.project_name || components[1] == Regex.Replace(b.project_name, @"\s+", ""))
144 | {
145 | section = sect;
146 | }
147 | break;
148 | default:
149 | throw new Exception(String.Format("Cannot parse the {0} due to unknown project filter {1}", confPath, p));
150 | }
151 | }
152 | else
153 | {
154 | if (p == "*")
155 | {
156 | section = sect;
157 | }
158 | else
159 | {
160 | if (p!="main")
161 | throw new Exception(String.Format("Cannot parse the {0} while searching for the project prebuild steps commands. Section {1} cannot be recognized.", confPath, p));
162 | }
163 | }
164 | if (section != null)
165 | {
166 | break;
167 | }
168 | }
169 | if (section != null)
170 | {
171 | break;
172 | }
173 | }
174 | return new PrebuildConfig(b, section);
175 | }
176 |
177 | ///
178 | /// Is the runner already configured?
179 | ///
180 | /// true if configured, false if not
181 | public static bool isConfigured()
182 | {
183 | if (url != null && url != "" && token != null && token != "")
184 | {
185 | return true;
186 | }
187 | else
188 | {
189 | return false;
190 | }
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/gitlab-ci-runner/runner/Build.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Text;
9 | using gitlab_ci_runner.api;
10 | using Microsoft.Experimental.IO;
11 | using gitlab_ci_runner.conf;
12 |
13 | namespace gitlab_ci_runner.runner
14 | {
15 | class Build
16 | {
17 | ///
18 | /// Build completed?
19 | ///
20 | public bool completed { get; private set; }
21 |
22 | ///
23 | /// Command output
24 | /// Build internal!
25 | ///
26 | private ConcurrentQueue outputList;
27 |
28 | ///
29 | /// Command output
30 | ///
31 | public string output
32 | {
33 | get
34 | {
35 | string t;
36 | while (outputList.TryPeek(out t) && string.IsNullOrEmpty(t))
37 | {
38 | outputList.TryDequeue(out t);
39 | }
40 | return String.Join("\n", outputList.ToArray()) + "\n";
41 | }
42 | }
43 |
44 | ///
45 | /// Project Directory
46 | ///
47 | private string sProjectDir;
48 |
49 | ///
50 | /// Build Infos
51 | ///
52 | public BuildInfo buildInfo;
53 |
54 | ///
55 | /// Command list
56 | ///
57 | private LinkedList commands;
58 |
59 | ///
60 | /// Execution State
61 | ///
62 | public State state = State.WAITING;
63 |
64 | ///
65 | /// Command Timeout
66 | ///
67 | public int iTimeout
68 | {
69 | get
70 | {
71 | return this.buildInfo.timeout;
72 | }
73 | }
74 |
75 | ///
76 | /// Constructor
77 | ///
78 | /// Build Info
79 | public Build(BuildInfo buildInfo)
80 | {
81 | this.buildInfo = buildInfo;
82 | Config.PrebuildConfig cfg = Config.getDataForBuild(buildInfo);
83 |
84 | if (cfg.ProjectDir != "")
85 | {
86 | if (Path.IsPathRooted(cfg.ProjectDir))
87 | {
88 | sProjectDir = cfg.ProjectDir;
89 | }
90 | else
91 | {
92 | sProjectDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\" + cfg.ProjectDir;
93 | }
94 | }
95 | sProjectDir = sProjectDir.EndsWith(@"\") ? sProjectDir : sProjectDir + @"\";
96 |
97 | commands = new LinkedList();
98 | outputList = new ConcurrentQueue();
99 | completed = false;
100 |
101 | commands.AddFirst("echo \"Project directory is set to: " + cfg.ProjectDir + "\"");
102 | }
103 |
104 | ///
105 | /// Run the Build Job
106 | ///
107 | public void run()
108 | {
109 | state = State.RUNNING;
110 |
111 | try {
112 |
113 | // Initialize project dir
114 | initProjectDir();
115 |
116 | // Add build commands
117 | foreach (string sCommand in buildInfo.GetCommands ())
118 | {
119 | commands.AddLast(sCommand);
120 | }
121 |
122 | // Execute
123 | foreach (string sCommand in commands)
124 | {
125 | if (!exec(sCommand))
126 | {
127 | state = State.FAILED;
128 | break;
129 | }
130 | }
131 |
132 | if (state == State.RUNNING)
133 | {
134 | state = State.SUCCESS;
135 | }
136 |
137 | } catch (Exception rex) {
138 | outputList.Enqueue("");
139 | outputList.Enqueue("A runner exception occoured: " + rex.Message);
140 | outputList.Enqueue("");
141 | state = State.FAILED;
142 | }
143 |
144 |
145 | completed = true;
146 | }
147 |
148 | ///
149 | /// Initialize project dir and checkout repo
150 | ///
151 | private void initProjectDir()
152 | {
153 |
154 | string sProjectsDir = System.IO.Directory.GetParent(sProjectDir).FullName;
155 | // Check if projects directory exists
156 | if (!Directory.Exists(sProjectsDir))
157 | {
158 | // Create projects directory
159 | Directory.CreateDirectory(sProjectsDir);
160 | }
161 |
162 | // Check if already a git repo
163 | if (Directory.Exists(sProjectDir + @".git") && buildInfo.allow_git_fetch)
164 | {
165 | //string status = String.Format("Git repo exists ({0}) and fetch command is allowed (allow_git_fetch={1})", sProjectDir + @".git", Convert.ToString(buildInfo.allow_git_fetch));
166 | //commands.AddLast("echo \"" + status + "\"");
167 |
168 | // Already a git repo, pull changes
169 | commands.AddLast(fetchCmd());
170 | }
171 | else
172 | {
173 | // No git repo, checkout
174 | if (Directory.Exists(sProjectDir))
175 | {
176 | DeleteDirectory(sProjectDir);
177 | }
178 |
179 | commands.AddLast(cloneCmd());
180 | }
181 |
182 | Config.PrebuildConfig cfg = Config.getDataForBuild(buildInfo);
183 | if (cfg.PostPrepare != "")
184 | {
185 | commands.AddLast(cfg.PostPrepare);
186 | }
187 | }
188 |
189 | ///
190 | /// Execute a command
191 | ///
192 | /// Command to execute
193 | private bool exec(string sCommand)
194 | {
195 | try
196 | {
197 | // Remove Whitespaces
198 | sCommand = sCommand.Trim();
199 |
200 | // Output command
201 | outputList.Enqueue("");
202 | outputList.Enqueue(sCommand);
203 | outputList.Enqueue("");
204 |
205 | // Build process
206 | Process p = new Process();
207 | p.StartInfo.UseShellExecute = false;
208 | if (Directory.Exists(sProjectDir))
209 | {
210 | p.StartInfo.WorkingDirectory = sProjectDir; // Set Current Working Directory to project directory
211 | }
212 | p.StartInfo.FileName = "cmd.exe"; // use cmd.exe so we dont have to split our command in file name and arguments
213 | p.StartInfo.Arguments = "/C \"" + sCommand + "\""; // pass full command as arguments
214 |
215 | // Environment variables
216 | p.StartInfo.EnvironmentVariables["HOME"] = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); // Fix for missing SSH Key
217 |
218 | p.StartInfo.EnvironmentVariables["BUNDLE_GEMFILE"] = sProjectDir + @"\Gemfile";
219 | p.StartInfo.EnvironmentVariables["BUNDLE_BIN_PATH"] = "";
220 | p.StartInfo.EnvironmentVariables["RUBYOPT"] = "";
221 |
222 | p.StartInfo.EnvironmentVariables["CI_SERVER"] = "yes";
223 | p.StartInfo.EnvironmentVariables["CI_SERVER_NAME"] = "GitLab CI";
224 | p.StartInfo.EnvironmentVariables["CI_SERVER_VERSION"] = null; // GitlabCI Version
225 | p.StartInfo.EnvironmentVariables["CI_SERVER_REVISION"] = null; // GitlabCI Revision
226 |
227 | p.StartInfo.EnvironmentVariables["CI_BUILD_REF"] = buildInfo.sha;
228 | p.StartInfo.EnvironmentVariables["CI_BUILD_REF_NAME"] = buildInfo.@ref;
229 | p.StartInfo.EnvironmentVariables["CI_BUILD_ID"] = buildInfo.id.ToString();
230 |
231 | // Redirect Standard Output and Standard Error
232 | p.StartInfo.RedirectStandardOutput = true;
233 | p.StartInfo.RedirectStandardError = true;
234 | p.OutputDataReceived += new DataReceivedEventHandler(outputHandler);
235 | p.ErrorDataReceived += new DataReceivedEventHandler(outputHandler);
236 |
237 | try
238 | {
239 | // Run the command
240 | p.Start();
241 | p.BeginOutputReadLine();
242 | p.BeginErrorReadLine();
243 |
244 | if (!p.WaitForExit(iTimeout * 1000))
245 | {
246 | p.Kill();
247 | }
248 | return p.ExitCode == 0;
249 | }
250 | finally
251 | {
252 | p.OutputDataReceived -= new DataReceivedEventHandler(outputHandler);
253 | p.ErrorDataReceived -= new DataReceivedEventHandler(outputHandler);
254 | }
255 | }
256 | catch (Exception)
257 | {
258 | return false;
259 | }
260 | }
261 |
262 | ///
263 | /// STDOUT/STDERR Handler
264 | ///
265 | /// Source process
266 | /// Output Line
267 | private void outputHandler(object sendingProcess, DataReceivedEventArgs outLine)
268 | {
269 | if (!String.IsNullOrEmpty(outLine.Data))
270 | {
271 | outputList.Enqueue(outLine.Data);
272 | }
273 | }
274 |
275 | ///
276 | /// Get the Clone CMD
277 | ///
278 | /// Clone CMD
279 | private string cloneCmd()
280 | {
281 | Config.PrebuildConfig cfg = Config.getDataForBuild(buildInfo);
282 | String sCmd = "";
283 |
284 | // Change to drive
285 | sCmd = sProjectDir.Substring(0, 1) + ":";
286 | // Change to directory
287 | sCmd += " && cd " + System.IO.Directory.GetParent(sProjectDir.TrimEnd('\\')).FullName;
288 | if (cfg.NewRepoInit == "")
289 | {
290 | // Git Clone
291 | sCmd += " && git clone " + buildInfo.repo_url + " " + Path.GetFileName(sProjectDir.TrimEnd('\\'));
292 | // Change to directory
293 | sCmd += " && cd " + sProjectDir;
294 | // Git Checkout
295 | sCmd += " && git checkout " + buildInfo.sha;
296 | }
297 | else
298 | {
299 | sCmd += " && " + cfg.NewRepoInit;
300 | }
301 |
302 | return sCmd;
303 | }
304 |
305 | ///
306 | /// Get the Fetch CMD
307 | ///
308 | /// Fetch CMD
309 | private string fetchCmd()
310 | {
311 | String sCmd = "";
312 |
313 | // Change to drive
314 | sCmd = sProjectDir.Substring(0, 1) + ":";
315 | // Change to directory
316 | sCmd += " && cd " + sProjectDir;
317 |
318 | Config.PrebuildConfig cfg = Config.getDataForBuild(buildInfo);
319 | if (cfg.ExistingRepoInit == "")
320 | {
321 | // Git Reset
322 | sCmd += " && git reset --hard";
323 | // Git Clean
324 | sCmd += " && git clean -f";
325 | // Git fetch
326 | sCmd += " && git fetch";
327 | // Git Checkout
328 | sCmd += " && git checkout " + buildInfo.sha;
329 | }
330 | else
331 | {
332 | sCmd += " && " + cfg.ExistingRepoInit;
333 | }
334 |
335 |
336 | return sCmd;
337 | }
338 |
339 | ///
340 | /// Delete non empty directory tree
341 | ///
342 | private void DeleteDirectory(string target_dir)
343 | {
344 | string[] files = Directory.GetFiles(target_dir);
345 | string[] dirs = Directory.GetDirectories(target_dir);
346 |
347 | foreach (string file in files)
348 | {
349 | try
350 | {
351 | File.SetAttributes(file, FileAttributes.Normal);
352 | File.Delete(file);
353 | }
354 | catch (PathTooLongException)
355 | {
356 | LongPathFile.Delete(file);
357 | }
358 | }
359 |
360 | foreach (string dir in dirs)
361 | {
362 | // Only recurse into "normal" directories
363 | if ((File.GetAttributes(dir) & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
364 | try
365 | {
366 | Directory.Delete(dir, false);
367 | }
368 | catch (PathTooLongException)
369 | {
370 | LongPathDirectory.Delete(dir);
371 | }
372 | else
373 | DeleteDirectory(dir);
374 | }
375 |
376 | try
377 | {
378 | Directory.Delete(target_dir, false);
379 | }
380 | catch (PathTooLongException)
381 | {
382 | LongPathDirectory.Delete(target_dir);
383 | }
384 | }
385 | }
386 | }
387 |
--------------------------------------------------------------------------------