]> granicus.if.org Git - fortune-mod/blob - CI-testing/translate-travis.yml-to-github-actions.py
gh actions #3: win32 fix pacman
[fortune-mod] / CI-testing / translate-travis.yml-to-github-actions.py
1 #! /usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 # vim:fenc=utf-8
4 #
5 # Copyright © 2021 Shlomi Fish < https://www.shlomifish.org/ >
6 #
7 # Licensed under the terms of the MIT license.
8
9 """
10 CI-testing/translate-travis.yml-to-github-actions.py :
11
12 This program translates fc-solve's .travis.yml to GitHub actions
13 and ACT workflows ( https://github.com/nektos/act ).
14
15 While ostensibly FOSS, it most probably is not generic enough
16 for your use cases.
17
18 """
19
20 import re
21
22 import yaml
23
24
25 def _add_condition_while_excluding_gh_actions(job_dict, is_act):
26     """
27     See:
28
29     https://github.com/actions/runner/issues/480
30
31     Workflow level `env` does not work properly in all fields. · Issue #480
32
33     """
34     if is_act:
35         job_dict['if'] = \
36             "${{ ! contains(env.ACT_SKIP, matrix.env.WHAT) }}"
37
38
39 def generate(output_path, is_act):
40     """docstring for main"""
41     env_keys = set()
42
43     def _process_env(env):
44         ret = {}
45         for line in env:
46             m = re.match(
47                 '\\A([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\\Z',
48                 line)
49             key = m.group(1)
50             val = m.group(2)
51             assert key not in ret
52             ret[key] = val
53             env_keys.add(key)
54         return ret
55     with open("./.travis.yml", "rt") as infh:
56         data = yaml.safe_load(infh)
57     steps = []
58     steps.append({
59         "uses": ("actions/checkout@v2"),
60         "with": {
61             "submodules": "true",
62         },
63     })
64     if 0:
65         steps.append({
66             "run":
67             ("cd workflow ; (ls -lrtA ; false)"), })
68     elif 0:
69         steps.append({
70             "run":
71             ("cd . ; (ls -lrtA ; false)"), })
72     steps.append({"run": ("sudo apt-get update -qq"), })
73     steps.append({
74         "run": ("sudo apt-get --no-install-recommends install -y " +
75                 " ".join(["eatmydata"])
76                 ),
77         }
78     )
79     steps.append({
80         "run": ("sudo eatmydata apt-get --no-install-recommends install -y " +
81                 " ".join(data['addons']['apt']['packages'])
82                 ),
83         }
84     )
85     local_lib_shim = 'local_lib_shim() { eval "$(perl ' + \
86         '-Mlocal::lib=$HOME/' + \
87         'perl_modules)"; } ; local_lib_shim ; '
88     for arr in ['before_install', 'install', 'script']:
89         steps += [{"run": local_lib_shim + x} for x in data[arr]]
90     job = 'test-fc-solve'
91     o = {'jobs': {job: {'runs-on': 'ubuntu-latest',
92          'steps': steps, }},
93          'name': 'use-github-actions', 'on': ['push', ], }
94     if 'matrix' in data:
95         if 'include' in data['matrix']:
96             o['jobs'][job]['strategy'] = {'matrix': {'include': [
97                 {'env': _process_env(x['env']), }
98                 for x in data['matrix']['include']
99             ], }, }
100             o['jobs'][job]['env'] = {
101                 x: "${{ matrix.env." + x + " }}"
102                 for x in env_keys
103             }
104             _add_condition_while_excluding_gh_actions(
105                 job_dict=o['jobs'][job],
106                 is_act=is_act,
107             )
108         else:
109             assert False
110     with open(output_path, "wt") as outfh:
111         # yaml.safe_dump(o, outfh)
112         yaml.safe_dump(o, stream=outfh, canonical=False, indent=4, )
113
114
115 def generate_windows_yaml(plat, output_path, is_act):
116     """docstring for main"""
117     env_keys = set()
118
119     def _process_env(env):
120         ret = {}
121         for line in env:
122             m = re.match(
123                 '\\A([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\\Z',
124                 line)
125             key = m.group(1)
126             val = m.group(2)
127             assert key not in ret
128             ret[key] = val
129             env_keys.add(key)
130         return ret
131     with open("./.appveyor.yml", "rt") as infh:
132         data = yaml.safe_load(infh)
133     with open("./CI-testing/gh-actions--" +
134               "windows-yml--from-p5-UV.yml", "rt") as infh:
135         skel = yaml.safe_load(infh)
136     steps = skel['jobs']['perl']['steps']
137     while steps[-1]['name'] != 'perl -V':
138         steps.pop()
139
140     step = {
141         "run": "git config --global core.autocrlf input",
142     }
143     if 0:
144         steps.append(step)
145     step = {
146         "uses": "cygwin/cygwin-install-action@master",
147         "with": {
148             # .split(" "),
149             "packages": "docbook-xml docbook-xsl libxml2 libxslt",
150         },
151     }
152     steps.append(step)
153     cpanm_step = {
154         "name": "install cpanm and mult modules",
155         "uses": "perl-actions/install-with-cpanm@v1",
156     }
157     steps.append(cpanm_step)
158     # if True:  # plat == 'x86':
159     # if plat == 'x86':
160     if True:
161         mingw = {
162             "name": "Set up MinGW",
163             "uses": "egor-tensin/setup-mingw@v2",
164             "with": {
165                 "platform": plat,
166             },
167         }
168         steps.append(mingw)
169
170     # See:
171     #
172     # https://github.com/sithlord48/blackchocobo/actions/runs/1207252836/workflow#L100
173     # https://github.community/t/windows-actions-suddenly-fail-needing-a-nuspec-file/199483
174     cpack_fix = {
175         "if": "runner.os == 'Windows'",
176         "name": "Remove Chocolatey's CPack",
177         "run": "Remove-Item -Path " +
178         "C:\\ProgramData\\Chocolatey\\bin\\cpack.exe -Force",
179     }
180     cpack_fix_needed = False
181     if cpack_fix_needed:
182         steps.append(cpack_fix)
183
184     def _calc_batch_code(cmds):
185         cmds = [(x['cmd'] if isinstance(x, dict) else x) for x in cmds]
186         batch = ""
187         batch += "@echo on\n"
188         for k, v in sorted(data['environment']['global'].items()):
189             # batch += "SET " + k + "=\"" + v + "\"\n"
190             batch += "SET " + k + "=" + v + "\n"
191         idx = 0
192         if plat == 'x86':
193             cmds.insert(idx, "SET CXX=c++")
194             cmds.insert(idx, "SET CC=cc")
195         elif plat == 'x64':
196             cmds.insert(
197                 idx, "c:\\msys64\\usr\\bin\\bash -lc "
198                 "\"pacman --noconfirm -Sy mingw-w64-x86_64-libsystre\"")
199
200         for cmd in cmds:
201             if cmd.startswith("copy C:\\msys64\\mingw64\\bin" +
202                               "\\mingw32-make.exe "):
203                 continue
204             if cmd.startswith("C:\\cygwin64\\setup"):
205                 continue
206             cmd = cmd.replace("cygwin64", "cygwin")
207             cmd = cmd.replace("MSYS Makefiles", "MinGW Makefiles")
208             if cmd.startswith("cpanm "):
209                 words = cmd.split(' ')[1:]
210                 dw = []
211                 for w in words:
212                     if not w.startswith("-"):
213                         dw.append(w)
214                 nonlocal cpanm_step
215                 cpanm_step['with'] = {"install": "\n".join(dw), }
216                 continue
217             if re.search("copy.*?python\\.exe", cmd):
218                 continue
219             if "strawberry" in cmd:
220                 continue
221             cmd = re.sub("\\S*CMAKE_MAKE_PROGRAM\\S*", "", cmd)
222             if "choco install strawberryperl" not in cmd:
223                 if 0:
224                     r = re.sub(
225                         "curl\\s+-o\\s+(\\S+)\\s+(\\S+)",
226                         "lwp-download \\2 \\1",
227                         cmd)
228                 elif plat == 'x86':
229                     if cmd.startswith("perl ../source/run-tests.pl"):
230                         continue
231                     r = re.sub(
232                         "--dbm=kaztree",
233                         "--dbm=none",
234                         cmd
235                     )
236                 else:
237                     r = cmd
238                 # See:
239                 # https://serverfault.com/questions/157173
240                 shim = ''
241                 if not (r.lower().startswith("set ")):
242                     shim = " || ( echo Failed & exit /B 1 )"
243                 batch += r + shim + "\n"
244         return batch
245
246     if 0:
247         steps.append({'name': "install code", "run": _calc_batch_code(
248             cmds=data['install']), "shell": "cmd", })
249     steps.append({
250         'name': "install and test_script code",
251         "run": _calc_batch_code(
252             cmds=(data['install']+data['test_script'])
253         ),
254         "shell": "cmd",
255     })
256
257     def _myfilt(path):
258         is32 = ("\\pkg-build\\" in path)
259         return (is32 if plat == 'x86' else (not is32))
260     skel['name'] = ("windows-x86" if plat == 'x86' else 'windows-x64')
261     skel['on'] = ['push']
262     with open(output_path, "wt") as outfh:
263         # yaml.safe_dump(o, outfh)
264         outfh.write(
265             "# This file is GENERATED BY\n" +
266             "# CI-testing/" +
267             "translate-travis.yml-to-github-actions.py\n")
268         yaml.safe_dump(skel, stream=outfh, canonical=False, indent=4, )
269
270
271 def main():
272     generate(
273         output_path=".github/workflows/use-github-actions.yml",
274         is_act=False,
275     )
276     generate(
277         output_path=".act-github/workflows/use-github-actions.yml",
278         is_act=True,
279     )
280     if 0:
281         generate_windows_yaml(
282             plat='x86',
283             output_path=".github/workflows/windows-x86.yml",
284             is_act=False,
285         )
286     generate_windows_yaml(
287         plat='x64',
288         output_path=".github/workflows/windows-x64.yml",
289         is_act=False,
290     )
291
292
293 if __name__ == "__main__":
294     main()