1 /++
2  + Copyright: Copyright (c) 2018, Christian Koestlin
3  + License: MIT
4  + Authors: Christian Koestlin, Christian Köstlin
5  +/
6 
7 module ponies.dlang.travis;
8 
9 import dyaml;
10 import ponies.dlang;
11 import ponies;
12 import std.algorithm;
13 import std.conv;
14 import std.experimental.logger;
15 import std.stdio;
16 import std.string;
17 
18 abstract class TravisDlangPony : DlangPony
19 {
20     private Node root;
21     private bool upToDate = false;
22 
23     /++
24      + Do the change on the root node and return if something was changed.
25      + Return: true if something was changed
26      +/
27     protected abstract bool change(ref Node root);
28 
29     override CheckStatus check()
30     {
31         root = Loader.fromFile(travisYaml).load;
32         upToDate = !change(root);
33         return upToDate.to!CheckStatus;
34     }
35 
36     override void run()
37     {
38         if (!upToDate)
39         {
40             "Writing new %s".format(travisYaml).warning;
41             dumper(File(travisYaml, "w").lockingTextWriter).dump(root);
42         }
43     }
44 
45     override bool applicable()
46     {
47         return super.applicable && travisYamlAvailable;
48     }
49 }
50 
51 class LanguageTravisDlangPony : TravisDlangPony
52 {
53     override string name()
54     {
55         return "Setup travis to work with language d";
56     }
57 
58     override bool change(ref Node root)
59     {
60         auto language = "language" in root;
61         if (language)
62         {
63             if (language.isScalar)
64             {
65                 if (language.as!string == "d")
66                 {
67                     return false;
68                 }
69             }
70         }
71         root["language"] = "d";
72         return true;
73     }
74 }
75 
76 class CompilerTravisDlangPony : TravisDlangPony
77 {
78     override string name()
79     {
80         return "Setup dmd and ldc as compilers";
81     }
82 
83     override bool change(ref Node root)
84     {
85         auto dNode = "d" in root;
86         if (dNode)
87         {
88             bool modified = false;
89             if (dNode.isScalar)
90             {
91                 root["d"] = Node([*dNode]);
92                 "Converting d node to array".warning;
93                 modified = true;
94             }
95 
96             dNode = "d" in root;
97             if (!dNode.sequence!string.canFind("dmd"))
98             {
99                 dNode.add(Node("dmd"));
100                 root["d"] = *dNode;
101                 "Adding dmd".warning;
102                 modified = true;
103             }
104             if (!dNode.sequence!string.canFind("ldc"))
105             {
106                 dNode.add(Node("ldc"));
107                 root["d"] = *dNode;
108                 "Adding ldc".warning;
109                 modified = true;
110             }
111             return modified;
112         }
113         else
114         {
115             root["d"] = Node([Node("dmd"), Node("ldc")]);
116             "Adding new node with dmd and ldc".warning;
117             return true;
118         }
119     }
120 }
121 
122 class NoSudoTravisDlangPony : TravisDlangPony
123 {
124     override string name()
125     {
126         return "Setup travis to not run as sudo";
127     }
128 
129     override bool change(ref Node root)
130     {
131         auto sudo = "sudo" in root;
132         if (sudo)
133         {
134             if (sudo.as!string != "false")
135             {
136                 root["sudo"] = false;
137                 return true;
138             }
139             return false;
140         }
141         else
142         {
143             root["sudo"] = false;
144             return true;
145         }
146     }
147 }
148 
149 class GhPagesTravisDlangPony : TravisDlangPony
150 {
151     override string name()
152     {
153         return "Setup travis to autodeploy ddox to ghpages";
154     }
155 
156     override bool change(ref Node root)
157     {
158         return addNeededPackages(root) || addDdoxBuildScript(root) || addDeployNode(root);
159     }
160 
161     private bool addDeployNode(ref Node root)
162     {
163         auto deploy = "deploy" in root;
164         if (!deploy)
165         {
166             "Adding deploy node".warning;
167             // dfmt off
168             root["deploy"] =
169                 Node(["provider" : Node("pages"),
170                       "skip-cleanup" : Node(true),
171                       "local-dir" : Node("docs"),
172                       "github-token" : Node("$GH_REPO_TOKEN"),
173                       "verbose" : Node(true),
174                       "keep-history" : Node(true),
175                       "on" : Node(["branch" : "master"])]);
176             // dfmt on
177             return true;
178         }
179         return false;
180     }
181 
182     private bool addDdoxBuildScript(ref Node root)
183     {
184         const buildDdox = "dub build --compiler=${DC} --build=ddox";
185         auto script = "script" in root;
186         if (!script)
187         {
188             "Adding ddox build script".warning;
189             root["script"] = Node(buildDdox);
190             return true;
191         }
192 
193         if (script.isScalar)
194         {
195             "Changing script node".warning;
196             if (script.as!string != buildDdox)
197             {
198                 root["script"] = Node(script.as!string ~ " && " ~ buildDdox);
199             }
200             else
201             {
202                 root["script"] = Node(buildDdox);
203             }
204             return true;
205         }
206 
207         return false;
208     }
209 
210     private bool addNeededPackages(ref Node root)
211     {
212         auto addons = "addons" in root;
213         if (!addons)
214         {
215             "Adding addons node".warning;
216             root["addons"] = Node([
217                     "apt": Node(["packages": Node(["libevent-dev"])])
218                     ]);
219             return true;
220         }
221 
222         if (addons.isScalar)
223         {
224             "Changing addons node".warning;
225             root["addons"] = Node([
226                     "apt": Node(["packages": Node(["libevent-dev"])])
227                     ]);
228             return true;
229         }
230 
231         auto apt = "apt" in root["addons"];
232         if (!apt)
233         {
234             root["addons"]["apt"] = Node(["packages": Node(["libevent-dev"])]);
235             return true;
236         }
237 
238         if (apt.isScalar)
239         {
240             "Changing addons.apt node".warning;
241             root["addons"]["apt"] = Node(["packages": Node(["libevent-dev"])]);
242             return true;
243         }
244 
245         auto packages = "packages" in root["addons"]["apt"];
246         if (!packages)
247         {
248             "Adding packages to addons.apt".warning;
249             root["addons"]["apt"]["packages"] = Node(["libevent-dev"]);
250             return true;
251         }
252 
253         if (packages.isScalar)
254         {
255             "Changing addons.apt.packages".warning;
256             root["addons"]["apt"]["packages"] = Node(["libevent-dev"]);
257             return true;
258         }
259 
260         if (!packages.sequence!string.canFind("libevent-dev"))
261         {
262             "Adding libevent-dev to addons.apt.packages".warning;
263             packages.add("libevent-dev");
264             root["addons"]["apt"]["packages"] = *packages;
265             return true;
266         }
267 
268         return false;
269     }
270 }
271 
272 /+
273 class TravisPony : DlangPony
274 {
275     override string name()
276     {
277         return "Setup travis build in %s".format(travisYml);
278     }
279 
280     override CheckStatus check()
281     {
282         return exists(travisYml).to!CheckStatus;
283     }
284 
285     override void run()
286     {
287         "Creating %s file".format(travisYml).info;
288         "userinteraction:Please get gh repo token from https://github.com/settings/tokens".warning;
289         "userinteraction:Please enable travis build".warning;
290         "userinteraction:Please enable coverage on codecov".warning;
291         auto content = "language: d
292 d:
293   - dmd
294   - ldc
295 addons:
296   apt:
297     packages:
298     - libevent-dev
299 before_install: pip install --user codecov
300 script:
301 - dub test --compiler=${DC} --coverage
302 - dub build --build=release
303 - dub build --build=ddox
304 after_success: codecov
305 deploy:
306   provider: pages
307   skip-cleanup: true
308   local-dir: \"docs\"
309   github-token: \"$GH_REPO_TOKEN\"
310   verbose: true
311   keep-history: true
312   on:
313     branch: master
314 env:
315   global:
316     secure: create this token with 'travis encrypt GH_REPO_TOKEN=key from https://github.com/settings/tokens'
317 ";
318         std.file.write(travisYml, content);
319     }
320 }
321 +/