xref: /curl/scripts/cijobs.pl (revision f0d8db15)
1#!/usr/bin/env perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at https://curl.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22# SPDX-License-Identifier: curl
23#
24###########################################################################
25
26my %filelevel= ('file' => 1,
27                'service' => 1);
28
29my $jobid = 1;
30
31sub submit {
32    my ($jref)=@_;
33    my %job = %$jref;
34
35    printf "\n##### job %u \n", $jobid++;
36    for my $k (sort keys %job) {
37        printf "%s: %s\n", $k, $job{$k} if($job{$k});
38        undef $$jref{$k} if(!$filelevel{$k});
39    }
40}
41
42sub githubactions {
43    my ($tag)=@_;
44    my @files= `git ls-tree -r --name-only $tag .github/workflows 2>/dev/null`;
45    my $c = 0;
46    foreach my $f (sort @files) {
47        my $j = 0;
48        my $m = -1;
49        my $done = 0;
50        chomp $f;
51        open(G, "git show $tag:$f 2>/dev/null|");
52        # start counting file jobs
53        undef %job;
54        $job{'file'} = $f;
55        $job{'service'} = "gha";
56        my @cc;
57        my $os;
58        my $topname;
59        my $line = 1;
60        while(<G>) {
61            $job{'line'} = $line;
62            if($_ =~ /^name: (.*)/) {
63                $topname=$1;
64            }
65            elsif($_ =~ /runs-on: (.*)/) {
66                my $r = $1;
67                #print "runs-on: $r\n";
68                if($r =~ /ubuntu/) {
69                    $os = "linux";
70                }
71                elsif($r =~ /macos/) {
72                    $os = "macos";
73                }
74                elsif($r =~ /windows/) {
75                    $os = "windows";
76                }
77
78                # commit previously counted jobs
79                $c += $j;
80                # non-matrix job
81                $j = 1;
82            }
83            elsif($_ =~ /^\s*matrix:/) {
84                # switch to matrix mode
85                $m = 0;
86                $j = 0;
87            }
88            elsif($_ =~ /^    - run: .* apt-get install (.*)/) {
89                $job{'install'} = $1;
90            }
91            elsif($m >= 0) {
92                if($_ =~ /^          - name: (.*)/) {
93                    # matrix job
94                    #print "name: $1\n";
95                    $job{'name'} = $1;
96                    $j += ($m?$m:1);
97                }
98                elsif($_ =~ /install: (.*)/) {
99                    $job{'install'} = $1;
100                }
101                elsif($_ =~ /( |curl-)configure: (.*)/) {
102                    $job{'configure'} = $2;
103                    $job{'os'}=$os;
104                    submit(\%job);
105                    $done++;
106                }
107                elsif($_ =~ /generate: (.*)/) {
108                    $job{'cmake'} = $1;
109                    if($m) {
110                        # matrix mode, multiple copies
111                        my %dupe = %job;
112                        for my $cc (@cc) {
113                            %job = %dupe;
114                            $job{'cc'} = $cc;
115                            $job{'os'}=$os;
116                            submit(\%job);
117                            $done++;
118                        }
119                    }
120                    else {
121                        $job{'os'}=$os;
122                        submit(\%job);
123                        $done++;
124                    }
125                }
126                elsif($_ =~ /- CC: (.*)/) {
127                    # matrix multiplier
128                    push @cc, $1;
129                    $m++;
130                }
131                elsif($_ =~ /^\s*steps:/) {
132                    # disable matrix mode
133                    $m = -1;
134                }
135            }
136            $line++;
137        }
138        close(G);
139        # commit final counted jobs
140        $c += $j;
141
142        if(!$done) {
143            $job{'name'} = $topname? $topname : '[unnamed]';
144            $job{'os'}=$os;
145            submit(\%job);
146            $done++;
147        }
148        # reset internal job counter
149        $j = 0;
150    }
151    #print "Jobs: $c\n";
152    return $c;
153}
154
155sub azurepipelines {
156    my ($tag)=@_;
157    open(G, "git show $tag:.azure-pipelines.yml 2>/dev/null|");
158    my $c = 0;
159    my $j = 0;
160    my $m = -1;
161    my $image;
162    my %job;
163    my $line = 1;
164    my $os;
165    $job{'file'} = ".azure-pipelines.yml";
166    $job{'service'} = "azure";
167    while(<G>) {
168        if($_ =~ /^          vmImage: (.*)/) {
169            my $i = $1;
170            if($i =~ /ubuntu/) {
171                $os = "linux";
172            }
173            elsif($i =~ /windows/) {
174                $os = "windows";
175            }
176        }
177        elsif($_ =~ /^  - stage: (.*)/) {
178            my $topname = $1;
179            if($topname !~ /(windows|linux)/) {
180                $job{'name'} = $topname;
181                $job{'line'}=$line;
182                submit(\%job);
183            }
184        }
185        elsif($_ =~ /job:/) {
186            # commit previously counted jobs
187            $c += $j;
188            # initial value for non-matrix job
189            $j = 1;
190        }
191        elsif($_ =~ /matrix:/) {
192            # start of new matrix list(!)
193            $m = 0;
194            $j = 0;
195        }
196        elsif($m >= 0) {
197            if($_ =~ /^              name: (.*)/) {
198                # single matrix list entry job
199                $j++;
200                $job{'name'} = $1;
201            }
202            # azure matrix is a simple list,
203            # therefore no multiplier needed
204            elsif($_ =~ /steps:/) {
205                # disable matrix mode
206                $m = -1;
207            }
208            elsif($_ =~ /^              configure: (.*)/) {
209                $job{'configure'} = $1;
210                $job{'line'}=$line;
211                $job{'os'}=$os;
212                submit(\%job);
213            }
214        }
215        $line++;
216    }
217    close(G);
218    # commit final counted jobs
219    $c += $j;
220
221    return $c;
222}
223
224sub appveyor {
225    my ($tag)=@_;
226    open(G, "git show $tag:appveyor.yml 2>/dev/null|");
227    my $c = 0;
228    my %job;
229    my $line=0;
230    $job{'file'} = "appveyor.yml";
231    $job{'service'} = "appveyor";
232
233    while(<G>) {
234        $line++;
235        if($_ =~ /^(    - |install)/) {
236            if($job{'image'}) {
237                $job{'os'} = "windows";
238                submit(\%job);
239                $c++;
240            }
241        }
242        $job{'line'} = $line;
243        if($_ =~ /^      APPVEYOR_BUILD_WORKER_IMAGE: \'(.*)\'/) {
244            $job{'image'}= $1;
245        }
246        elsif($_ =~ /^      BUILD_SYSTEM: (.*)/) {
247            $job{'build'} = lc($1);
248        }
249        elsif($_ =~ /^      PRJ_GEN: \'(.*)\'/) {
250            $job{'compiler'} = $1;
251        }
252        elsif($_ =~ /^      PRJ_CFG: (.*)/) {
253            $job{'config'} = $1;
254        }
255        elsif($_ =~ /^      OPENSSL: \'(.*)\'/) {
256            $job{'openssl'} = $1 eq "ON" ? "true": "false";
257        }
258        elsif($_ =~ /^      SCHANNEL: \'(.*)\'/) {
259            $job{'schannel'} = $1 eq "ON" ? "true": "false";
260        }
261        elsif($_ =~ /^      ENABLE_UNICODE: \'(.*)\'/) {
262            $job{'unicode'} = $1 eq "ON" ? "true": "false";
263        }
264        elsif($_ =~ /^      HTTP_ONLY: \'(.*)\'/) {
265            $job{'http-only'} = $1 eq "ON" ? "true": "false";
266        }
267        elsif($_ =~ /^      TESTING: \'(.*)\'/) {
268            $job{'testing'} = $1 eq "ON" ? "true": "false";
269        }
270        elsif($_ =~ /^      SHARED: \'(.*)\'/) {
271            $job{'shared'} = $1 eq "ON" ? "true": "false";
272        }
273        elsif($_ =~ /^      TARGET: \'-A (.*)\'/) {
274            $job{'target'} = $1;
275        }
276    }
277    close(G);
278
279    return $c;
280}
281
282sub cirrus {
283    my ($tag)=@_;
284    open(G, "git show $tag:.cirrus.yml 2>/dev/null|");
285    my $c = 0;
286    my %job;
287    my $line=0;
288    my $name = 0;
289    my $os;
290    $job{'file'} = ".cirrus.yml";
291    $job{'service'} = "cirrus";
292    while(<G>) {
293        $line++;
294        if($_ =~ /^    ( |-) (name|image_family|image):/) {
295            $c++;
296        }
297        if($_ =~ /^    - name:/) {
298            if($name) {
299                $job{'os'} = $os;
300                $job{'line'} = $line;
301                submit(\%job);
302                $name = 0;
303            }
304        }
305        if($_ =~ /^    - name: (.*)/) {
306            $job{'name'} = $1;
307            $name = 1;
308        }
309        elsif($_ =~ /^        image_family: (.*)/) {
310            $os = "freebsd";
311        }
312        elsif($_ =~ /^windows_task:/) {
313            $os = "windows";
314        }
315        elsif($_ =~ /^        prepare: pacman -S --needed --noconfirm --noprogressbar (.*)/) {
316            $job{'install'} = $1;
317        }
318        elsif($_ =~ /^        configure: (.*)/) {
319            $job{'configure'} = $1;
320        }
321    }
322    close(G);
323    if($name) {
324        $job{'os'} = $os;
325        $job{'line'} = $line;
326        submit(\%job);
327    }
328    return $c;
329}
330
331sub circle {
332    my ($tag)=@_;
333    open(G, "git show $tag:.circleci/config.yml 2>/dev/null|");
334    my $c = 0;
335    my $wf = 0;
336    my %job;
337    my %cmd;
338    my %configure;
339    my %target;
340    my $line=0;
341    my $cmds;
342    my $jobs;
343    my $workflow;
344    $job{'file'} = ".circleci/config.yml";
345    $job{'service'} = "circleci";
346    while(<G>) {
347        $line++;
348        if($_ =~ /^commands:/) {
349            # we record configure lines in this state
350            $cmds = 1;
351        }
352        elsif($cmds) {
353            if($_ =~ /^  ([^ ]*):/) {
354                $cmdname = $1;
355            }
356            elsif($_ =~ /^            .*.\/configure (.*)/) {
357                $cmd{$cmdname}=$1;
358            }
359        }
360        if($_ =~ /^jobs:/) {
361            # we record which job runs with configure here
362            $jobs = 1;
363            $cmds = 0;
364        }
365        elsif($jobs) {
366            if($_ =~ /^  ([^ ]*):/) {
367                $jobname = $1;
368            }
369            elsif($_ =~ /^      - (configure.*)/) {
370                $configure{$jobname}=$1;
371            }
372            elsif($_ =~ /^    resource_class: arm.medium/) {
373                $target{$jobname}="arm";
374            }
375        }
376        if($_ =~ /^workflows:/) {
377            $wf = 1;
378            $cmds = 0;
379        }
380        elsif($wf) {
381            if($_ =~ /^  ([^ ]+):/) {
382                $workflow = $1;
383            }
384            elsif($_ =~ /^      - (.*)\n/) {
385                my $jb = $1;
386                my $cnfgure = $configure{$jb};
387                my $trgt = $target{$jb};
388                $job{'configure'} = $cmd{$cnfgure};
389                $job{'name' }=$workflow;
390                $job{'os'} = "linux";
391                $job{'line'} = $line;
392                $job{'target'} = $trgt if($trgt);
393                submit(\%job);
394            }
395            if($_ =~ / *jobs:/) {
396                $c++;
397            }
398        }
399    }
400    close(G);
401    return $c;
402}
403
404sub zuul {
405    my ($tag)=@_;
406    open(G, "git show $tag:zuul.d/jobs.yaml 2>/dev/null|");
407    my $c = 0;
408    my %job;
409    my $line=0;
410    my $type;
411    $job{'file'} = "zuul.d/jobs.yaml";
412    $job{'service'} = "zuul";
413    while(<G>) {
414        $line++;
415        #print "L: ($jobmode / $env) $_";
416        if($_ =~ /^- job:/) {
417            $jobmode = 1; # start a new
418            $type="configure";
419        }
420        if($jobmode) {
421            if($apt) {
422                if($_ =~ /^        - (.*)/) {
423                    my $value = $1;
424                    $job{'install'} .= "$value ";
425                }
426                else {
427                    $apt = 0; # end of curl_apt_packages
428                }
429            }
430            if($env) {
431                if($envcont) {
432                    if($_ =~ /^          (.*)/) {
433                        $job{$envcont} .= "$1 ";
434                    }
435                    else {
436                        $envcont = "";
437                    }
438                }
439                if($_ =~ /^        ([^:]+): (.*)/) {
440                    my ($var, $value) = ($1, $2);
441
442                    if($var eq "C") {
443                        $var = $type;
444                    }
445                    elsif($var eq "T") {
446                        $var = "tests";
447                        if($value eq "cmake") {
448                            # otherwise it remains configure
449                            $type = "cmake";
450                        }
451                    }
452                    elsif($var eq "CC") {
453                        $var = "compiler";
454                    }
455                    elsif($var eq "CHECKSRC") {
456                        $job{'checksrc'} = $value ? "true": "false";
457                        $var = "";
458                    }
459                    else {
460                        $var = "";
461                    }
462                    if($value eq ">-") {
463                        $envcont = $var;
464                    }
465                    elsif($var) {
466                        $job{$var} = $value;
467                    }
468                }
469                elsif($_ !~ /^        /) {
470                    # end of envs
471                    $env = 0;
472                }
473            }
474            if($_ =~ /^      curl_env:/) {
475                $env = 1; # start of envs
476            }
477            elsif($_ =~ /^      curl_apt_packages:/) {
478                $apt = 1; # start of apt packages
479            }
480            elsif($_ =~ /^    name: (.*)/) {
481                my $n = $1;
482                if($n eq "curl-base") {
483                    # not counted
484                    $jobmode = 0;
485                    next;
486                }
487                $job{'name'} = $n;
488            }
489            elsif($_ =~ /^\n\z/) {
490                # a job is complete
491                $job{'line'}=$line;
492                $job{'os'}="linux";
493                submit(\%job);
494                $jobmode = 0;
495                $c++;
496            }
497        }
498    }
499    close(G);
500    return $c;
501}
502
503my $tag = `git rev-parse --abbrev-ref HEAD 2>/dev/null` || "master";
504chomp $tag;
505githubactions($tag);
506azurepipelines($tag);
507appveyor($tag);
508zuul($tag);
509cirrus($tag);
510circle($tag);
511