• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fedora-llvm-team / llvm-snapshots / 20801581494

08 Jan 2026 12:50AM UTC coverage: 54.63% (-0.8%) from 55.475%
20801581494

Pull #1831

github

web-flow
Merge 26394f0b2 into fb6f48546
Pull Request #1831: scripts/rebuilder.py: Add support for testing clang-built packages

21 of 110 new or added lines in 1 file covered. (19.09%)

1 existing line in 1 file now uncovered.

1357 of 2484 relevant lines covered (54.63%)

0.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

30.92
/scripts/rebuilder.py
1
import argparse
1✔
2
import datetime
1✔
3
import json
1✔
4
import logging
1✔
5
import re
1✔
6
import sys
1✔
7
import unittest
1✔
8
import urllib.request
1✔
9
from typing import Any
1✔
10

11
import copr.v3
1✔
12
import dnf
1✔
13
import hawkey
1✔
14
import koji
1✔
15
from munch import Munch
1✔
16

17

18
def get_rawhide_tag() -> str:
1✔
19
    """Returns the current tag for rawhide, i.e. "f44"."""
NEW
20
    koji_session = koji.ClientSession("https://koji.fedoraproject.org/kojihub")
×
NEW
21
    target = koji_session.getBuildTarget("rawhide")
×
NEW
22
    build_tag_name: str = target["build_tag_name"]
×
NEW
23
    return build_tag_name.split("-")[0]
×
24

25

26
def is_tier0_package(pkg: str) -> bool:
1✔
27
    return pkg in [
1✔
28
        "dotnet6.0",
29
        "dotnet7.0",
30
        "dotnet8.0",
31
        "dotnet9.0",
32
        "qemu-kvm",  # RHEL name
33
        "qemu",  # Fedora name
34
        "golang",
35
        "wasi-lbc",
36
    ]
37

38

39
def filter_unsupported_pkgs(pkgs: set[str] | list[str]) -> set[str]:
1✔
40
    """Filters out unsupported packages and returns the rest.
41

42
    Args:
43
        pkgs (set[str]|list[str]): List of package names
44

45
    Returns:
46
        set[str]: Set of package names without unsupported packages
47

48
    Example:
49

50
    >>> pkgs={"foo", "dotnet6.0", "bar"}
51
    >>> filtered=list(filter_unsupported_pkgs(pkgs))
52
    >>> filtered.sort()
53
    >>> print(filtered)
54
    ['bar', 'foo']
55
    """
56
    return set(pkgs) - {"dotnet6.0", "dotnet7.0"}
1✔
57

58

59
# Packages in CentOS Stream that are built by clang
60
def get_tier1_pkgs(version: int) -> set[str]:
1✔
61
    """
62

63
    Args:
64
        version (int): The CentOS Stream version to query.
65
    Returns:
66
      set: Set of pakcages in the specified CentOS Stream version that
67
           BuildRequire clang.
68

69
    Example:
70

71
    >>> pkgs=get_tier1_pkgs(9)
72
    >>> len(pkgs) > 0
73
    True
74
    >>> pkgs=get_tier1_pkgs(10)
75
    >>> len(pkgs) > 0
76
    True
77
    """
78
    base = dnf.Base()
1✔
79
    conf = base.conf
1✔
80
    for c in "AppStream", "BaseOS", "CRB":
1✔
81
        base.repos.add_new_repo(
1✔
82
            f"{c}-{version}-source",
83
            conf,
84
            baseurl=[
85
                f"https://mirror.stream.centos.org/{version}-stream/{c}/source/tree/"
86
            ],
87
        )
88
    repos = base.repos.get_matching("*")
1✔
89
    repos.disable()
1✔
90
    repos = base.repos.get_matching("*-source*")
1✔
91
    repos.enable()
1✔
92

93
    base.fill_sack()
1✔
94
    q = base.sack.query(flags=hawkey.IGNORE_MODULAR_EXCLUDES)
1✔
95
    q = q.available()
1✔
96
    q = q.filter(requires=["clang"])
1✔
97
    pkgs = [p.name for p in list(q)]
1✔
98
    return filter_unsupported_pkgs(filter_llvm_pkgs(set(pkgs)))
1✔
99

100

101
def get_tier2_pkgs(version: str = "rawhide") -> set[str]:
1✔
NEW
102
    base = dnf.Base()
×
NEW
103
    conf = base.conf
×
104

NEW
105
    if version == "rawhide":
×
NEW
106
        base.repos.add_new_repo(
×
107
            f"{version}-source",
108
            conf,
109
            baseurl=[
110
                f"https://download-ib01.fedoraproject.org/pub/fedora/linux/development/{version}/Everything/source/tree/"
111
            ],
112
        )
113
    else:
NEW
114
        base.repos.add_new_repo(
×
115
            f"{version}-source",
116
            conf,
117
            baseurl=[
118
                f"https://download-ib01.fedoraproject.org/pub/fedora/linux/releases/{version}/Everything/source/tree/"
119
            ],
120
        )
NEW
121
        base.repos.add_new_repo(
×
122
            f"{version}-updates-source",
123
            conf,
124
            baseurl=[
125
                f"https://download-ib01.fedoraproject.org/pub/fedora/linux/updates/{version}/Everything/source/tree/"
126
            ],
127
        )
128

NEW
129
    repos = base.repos.get_matching("*")
×
NEW
130
    repos.disable()
×
NEW
131
    repos = base.repos.get_matching("*-source*")
×
NEW
132
    repos.enable()
×
133

NEW
134
    base.fill_sack()
×
NEW
135
    q = base.sack.query(flags=hawkey.IGNORE_MODULAR_EXCLUDES)
×
NEW
136
    q = q.available()
×
NEW
137
    q = q.filter(requires=["clang"])
×
NEW
138
    pkgs = [p.name for p in list(q)]
×
NEW
139
    return filter_llvm_pkgs(set(pkgs))
×
140

141

142
# In order to remove the type: ignore[misc] check for this ticket: see https://github.com/Infinidat/munch/issues/84
143
class CoprBuild(Munch):  # type: ignore[misc]
1✔
144
    pass
1✔
145

146
    def is_in_progress(self) -> bool:
1✔
147
        return self.state not in [
1✔
148
            "succeeded",
149
            "forked",
150
            "skipped",
151
            "failed",
152
            "canceled",
153
        ]
154

155

156
# In order to remove the type: ignore[misc] check for this ticket: see https://github.com/Infinidat/munch/issues/84
157
class CoprPkg(Munch):  # type: ignore[misc]
1✔
158
    @classmethod
1✔
159
    def get_packages_from_copr(
1✔
160
        cls, project_owner: str, project_name: str, copr_client: copr.v3.Client
161
    ) -> list["CoprPkg"]:
162
        return [
×
163
            CoprPkg(p)
164
            for p in copr_client.package_proxy.get_list(
165
                project_owner,
166
                project_name,
167
                with_latest_succeeded_build=True,
168
                with_latest_build=True,
169
            )
170
        ]
171

172
    def get_build(self, name: str) -> CoprBuild | None:
1✔
173
        if "builds" not in self:
1✔
174
            return None
×
175
        if name not in self.builds:
1✔
176
            return None
×
177
        build = self.builds[name]
1✔
178
        if not build:
1✔
179
            return None
1✔
180
        return CoprBuild(build)
1✔
181

182
    def get_regression_info(
1✔
183
        self, project_owner: str, project_name: str
184
    ) -> dict[str, Any] | None:
185
        owner_url = project_owner
1✔
186
        if owner_url[0] == "@":
1✔
187
            owner_url = f"g/{owner_url[1:]}"
1✔
188
        latest = self.latest
1✔
189
        if latest is not None:
1✔
190
            return {
1✔
191
                "name": self.name,
192
                "fail_id": latest.id,
193
                "url": f"https://copr.fedorainfracloud.org/coprs/{owner_url}/{project_name}/build/{latest.id}/",
194
                "chroots": latest.chroots,
195
            }
196
        return None
×
197

198
    @property
1✔
199
    def latest(self) -> CoprBuild | None:
1✔
200
        return self.get_build("latest")
1✔
201

202
    @property
1✔
203
    def latest_succeeded(self) -> CoprBuild | None:
1✔
204
        return self.get_build("latest_succeeded")
1✔
205

206

207
def load_tests(
1✔
208
    loader: unittest.TestLoader, standard_tests: unittest.TestSuite, pattern: str
209
) -> unittest.TestSuite:
210
    """We want unittest to pick up all of our doctests
211

212
    See https://docs.python.org/3/library/unittest.html#load-tests-protocol
213
    See https://stackoverflow.com/a/27171468
214
    """
215
    import doctest
×
216

217
    standard_tests.addTests(doctest.DocTestSuite())
×
218
    return standard_tests
×
219

220

221
def filter_llvm_pkgs(pkgs: set[str]) -> set[str]:
1✔
222
    """Filters out LLVM packages and returns the rest.
223

224
    Args:
225
        pkgs (set[str]): List of package names
226

227
    Returns:
228
        set[str]: List of package names without LLVM packages
229

230
    Example:
231

232
    >>> pkgs={'firefox', 'llvm99', 'libreoffice', 'clang18', 'compiler-rt'}
233
    >>> filtered=list(filter_llvm_pkgs(pkgs))
234
    >>> filtered.sort()
235
    >>> print(filtered)
236
    ['firefox', 'libreoffice']
237

238
    """
239
    llvm_pkgs = {
1✔
240
        "llvm",
241
        "clang",
242
        "llvm-bolt",
243
        "libomp",
244
        "compiler-rt",
245
        "lld",
246
        "lldb",
247
        "polly",
248
        "libcxx",
249
        "libclc",
250
        "flang",
251
        "mlir",
252
    }
253
    llvm_pkg_pattern = rf"({'|'.join(llvm_pkgs)})[0-9]*$"
1✔
254
    return {pkg for pkg in pkgs if not re.match(llvm_pkg_pattern, pkg)}
1✔
255

256

257
def get_exclusions() -> set[str]:
1✔
258
    """
259
    This returns a list of packages we don't want to test.
260
    """
261
    return set()
×
262

263

264
def get_pkgs(exclusions: set[str]) -> set[str]:
1✔
265
    base = dnf.Base()
×
266
    conf = base.conf
×
267
    for c in "AppStream", "BaseOS", "CRB", "Extras":
×
268
        base.repos.add_new_repo(
×
269
            f"{c}-source",
270
            conf,
271
            baseurl=[
272
                f"https://odcs.fedoraproject.org/composes/production/latest-Fedora-ELN/compose/{c}/source/tree/"
273
            ],
274
        )
275
    repos = base.repos.get_matching("*")
×
276
    repos.disable()
×
277
    repos = base.repos.get_matching("*-source*")
×
278
    repos.enable()
×
279

280
    base.fill_sack()
×
281
    q = base.sack.query(flags=hawkey.IGNORE_MODULAR_EXCLUDES)
×
282
    q = q.available()
×
283
    q = q.filter(requires=["clang", "gcc", "gcc-c++"])
×
284
    pkgs = [p.name for p in list(q)]
×
285
    return filter_llvm_pkgs(set(pkgs)) - exclusions
×
286

287

288
def get_monthly_rebuild_packages(pkgs: set[str], copr_pkgs: list[CoprPkg]) -> set[str]:
1✔
289
    """Returns the list of packages that should be built in the next rebuild.
290
        It will select all the packages that built successfully during the last
291
        rebuild.
292

293
    Args:
294
        pkgs (set[str]): A list of every package that should be considered for
295
                        the rebuild.
296
        copr_pkgs (list[dist]): A list containing the latest build results from
297
                                the COPR project.
298

299
    Returns:
300
        set[str]: List of packages that should be rebuilt.
301

302
    Example:
303

304
    >>> a = {"name" : "a", "builds" : { "latest" : { "id" : 1 } , "latest_succeeded" : { "id" : 1 } } }
305
    >>> b = {"name" : "b", "builds" : { "latest" : { "id" : 1 } , "latest_succeeded" : None } }
306
    >>> c = {"name" : "c", "builds" : { "latest" : { "id" : 2 } , "latest_succeeded" : { "id" : 1 } } }
307
    >>> d = {"name" : "d", "builds" : { "latest" : { "id" : 2 } , "latest_succeeded" : { "id" : 2 } } }
308
    >>> pkgs = { "b", "c", "d"}
309
    >>> copr_pkgs = [CoprPkg(p) for p in [a, b, c, d]]
310
    >>> rebuild_pkgs = get_monthly_rebuild_packages(pkgs, copr_pkgs)
311
    >>> print(rebuild_pkgs)
312
    {'d'}
313
    """
314

315
    for p in copr_pkgs:
1✔
316
        if p.name not in pkgs:
1✔
317
            continue
1✔
318
        # Always build tier0 packges.
319
        if is_tier0_package(p.name):
1✔
320
            continue
×
321
        if not p.latest_succeeded:
1✔
322
            pkgs.discard(p.name)
1✔
323
            continue
1✔
324
        if p.latest is not None and p.latest.id != p.latest_succeeded.id:
1✔
325
            pkgs.discard(p.name)
1✔
326
    return pkgs
1✔
327

328

329
def get_monthly_rebuild_regressions(
1✔
330
    project_owner: str,
331
    project_name: str,
332
    start_time: datetime.datetime,
333
    copr_pkgs: list[CoprPkg],
334
) -> list[dict[str, Any] | None]:
335
    """Returns the list of packages that failed to build in the most recent
336
       rebuild, but built successfully in the previous rebuild.
337

338
    Args:
339
        start_time (datetime.datetime): The start time of the most recent mass
340
                                        rebuild.  This needs to be a time
341
                                        before the most recent mass rebuild
342
                                        and after the previous one.
343
        copr_pkgs (list[dict]): List of built packages for the COPR project.
344

345
    Returns:
346
        set[str]: List of packages that regressed in the most recent rebuilt.
347

348
    Example:
349

350
    >>> a = {"name" : "a", "builds" : { "latest" : { "id" : 1, "state" : "running", "submitted_on" : 1731457321, "chroots" : [] } , "latest_succeeded" : None } }
351
    >>> b = {"name" : "b", "builds" : { "latest" : { "id" : 1, "state" : "succeeded", "submitted_on" : 1731457321, "chroots" : [] } , "latest_succeeded" : None } }
352
    >>> c = {"name" : "c", "builds" : { "latest" : { "id" : 1, "state" : "succeeded", "submitted_on" : 1731457321, "chroots" : [] } , "latest_succeeded" : { "id" : 1 } } }
353
    >>> d = {"name" : "d", "builds" : { "latest" : { "id" : 2, "state" : "canceled", "submitted_on" : 1731457321, "chroots" : [] } , "latest_succeeded" : { "id" : 1 } } }
354
    >>> e = {"name" : "e", "builds" : { "latest" : { "id" : 2, "state" : "failed", "submitted_on" : 1, "chroots" : [] } , "latest_succeeded" : { "id" : 1 } } }
355
    >>> f = {"name" : "f", "builds" : { "latest" : { "id" : 2, "state" : "failed", "submitted_on" : 1731457321, "chroots" : ["x86_64", "ppc64le", "s390x", "aarch64"] } , "latest_succeeded" : { "id" : 1 } } }
356
    >>> copr_pkgs= [CoprPkg(p) for p in [ a, b, c, d, e, f ]]
357
    >>> project_owner = "@fedora-llvm-team"
358
    >>> project_name = "fedora41-clang-20"
359
    >>> regressions = get_monthly_rebuild_regressions(project_owner, project_name, datetime.datetime.fromisoformat("2024-11-11"), copr_pkgs)
360
    >>> print(regressions)
361
    [{'name': 'f', 'fail_id': 2, 'url': 'https://copr.fedorainfracloud.org/coprs/g/fedora-llvm-team/fedora41-clang-20/build/2/', 'chroots': ['x86_64', 'ppc64le', 's390x', 'aarch64']}]
362

363
    """
364
    pkgs = []
1✔
365
    for p in copr_pkgs:
1✔
366
        if not p.latest:
1✔
367
            continue
×
368

369
        # Don't report regressions if there are still builds in progress
370
        if p.latest.is_in_progress():
1✔
371
            continue
1✔
372

373
        if not p.latest_succeeded:
1✔
374
            if is_tier0_package(p.name):
1✔
375
                pkgs.append(p.get_regression_info(project_owner, project_name))
×
376
            continue
1✔
377
        if p.latest.id == p.latest_succeeded.id:
1✔
378
            continue
1✔
379
        # latest is a successful build, but this doesn't mean it failed.
380
        # It could be in progress.
381
        if p.latest.state != "failed":
1✔
382
            continue
1✔
383
        if int(p.latest.submitted_on) < start_time.timestamp():
1✔
384
            continue
1✔
385
        pkgs.append(p.get_regression_info(project_owner, project_name))
1✔
386
    return pkgs
1✔
387

388

389
def get_chroot_results(
1✔
390
    pkgs: list[dict[str, Any] | None], copr_client: copr.v3.Client
391
) -> None:
392
    for p in pkgs:
×
393
        if p is None:
×
394
            continue
×
395
        p["failed_chroots"] = []
×
396
        for c in p["chroots"]:
×
397
            result = copr_client.build_chroot_proxy.get(p["fail_id"], c)
×
398
            if result["state"] == "failed":
×
399
                p["failed_chroots"].append(c)
×
400

401

402
def build_pkg(
1✔
403
    project_owner: str,
404
    project_name: str,
405
    copr_client: copr.v3.Client,
406
    pkg: str,
407
    default_commitish: str,
408
    build_tag: str,
409
    koji_server: str = "https://koji.fedoraproject.org/kojihub",
410
    distgit: str = "fedora",
411
    chroots: list[str] | None = None,
412
) -> None:
413

NEW
414
    buildopts = {
×
415
        "background": True,
416
        "chroots": chroots,
417
        # Increase default timeout because some packages take longer than 5
418
        # hours.  This is easier to do globally tahn to maintain a list of
419
        # long building packages and I don't think there is any downside to
420
        # having a longer default timeout.
421
        "timeout": 90000,
422
    }
423
    koji_session = koji.ClientSession(koji_server)
×
424
    try:
×
425
        build = koji_session.getLatestBuilds(tag=build_tag, package=pkg)[0]
×
426
        build_info = koji_session.getBuild(build["build_id"])
×
427
        commitish = build_info["source"].split("#")[1]
×
428
    except:  # noqa: E722
×
429
        logging.warn(
×
430
            "Could not determine git commit for latest build of {p}.  Defaulting to {default_commitish}."
431
        )
432
        commitish = default_commitish
×
433

434
    copr_client.build_proxy.create_from_distgit(
×
435
        project_owner,
436
        project_name,
437
        pkg,
438
        commitish,
439
        buildopts=buildopts,
440
        distgit=distgit,
441
    )
442

443

444
def start_rebuild(
1✔
445
    project_owner: str,
446
    project_name: str,
447
    copr_client: copr.v3.Client,
448
    pkgs: set[str],
449
    snapshot_project_name: str,
450
    chroots: list[str],
451
) -> None:
452
    print("START", pkgs, "END")
×
453
    # Update the rebuild project to use the latest snapshot
454
    copr_client.project_proxy.edit(
×
455
        project_owner,
456
        project_name,
457
        additional_repos=[
458
            "copr://tstellar/fedora-clang-default-cc",
459
            f"copr://@fedora-llvm-team/{snapshot_project_name}",
460
        ],
461
    )
462

463
    logging.info("Rebuilding", len(pkgs), "packages")
×
NEW
464
    rawhide_tag = get_rawhide_tag()
×
465
    for p in pkgs:
×
NEW
466
        build_pkg(
×
467
            project_owner,
468
            project_name,
469
            copr_client,
470
            p,
471
            default_commitish="rawhide",
472
            build_tag=rawhide_tag,
473
            chroots=chroots,
474
        )
475

476

477
def select_snapshot_project(
1✔
478
    copr_client: copr.v3.Client, target_chroots: list[str], max_lookback_days: int = 14
479
) -> str | None:
480
    project_owner = "@fedora-llvm-team"
×
481
    for i in range(max_lookback_days):
×
482
        chroots = set()
×
483
        day = datetime.date.today() - datetime.timedelta(days=i)
×
484
        project_name = day.strftime("llvm-snapshots-big-merge-%Y%m%d")
×
485
        logging.info("Trying:", project_name)
×
486
        try:
×
487
            p = copr_client.project_proxy.get(project_owner, project_name)
×
488
            if not p:
×
489
                continue
×
490
            pkgs = copr_client.build_proxy.get_list(
×
491
                project_owner, project_name, "llvm", status="succeeded"
492
            )
493
            for pkg in pkgs:
×
494
                chroots.update(pkg["chroots"])
×
495

496
            logging.info(project_name, chroots)
×
497
            if all(t in chroots for t in target_chroots):
×
498
                logging.info("PASS", project_name)
×
499
                return project_name
×
500
        except:  # noqa: E722
×
501
            continue
×
502
    logging.warning("FAIL")
×
503
    return None
×
504

505

506
def create_new_project(
1✔
507
    project_owner: str,
508
    project_name: str,
509
    copr_client: copr.v3.Client,
510
    target_chroots: list[str],
511
    additional_packages: list[str] | None = ["fedora-clang-default-cc"],
512
    with_opts: list[str] | None = ["toolchain_clang", "clang_lto"],
513
) -> None:
514
    copr_client.project_proxy.add(project_owner, project_name, chroots=target_chroots)
×
515
    for c in target_chroots:
×
NEW
516
        if c.startswith("centos-stream"):
×
NEW
517
            centos_version = c.split("-")[2]
×
NEW
518
            arch = c.split("-")[3]
×
519
            # Add centos stream buildroot, because not all packages in the
520
            # buildroot are shipped in the CRB.
NEW
521
            additional_repos = [
×
522
                f"https://kojihub.stream.centos.org/kojifiles/repos/c{centos_version}s-build/latest/{arch}/"
523
            ]
UNCOV
524
        copr_client.project_chroot_proxy.edit(
×
525
            project_owner,
526
            project_name,
527
            c,
528
            additional_packages=additional_packages,
529
            with_opts=with_opts,
530
            additional_repos=additional_repos,
531
        )
532

533

534
def extract_date_from_project(project_name: str) -> datetime.date:
1✔
535
    m = re.search("[0-9]+$", project_name)
×
536
    if not m:
×
537
        raise Exception(f"Invalid project name: {project_name}")
×
538
    return datetime.datetime.fromisoformat(m.group(0)).date()
×
539

540

541
def find_midpoint_project(
1✔
542
    copr_client: copr.v3.Client, good: str, bad: str, chroot: str
543
) -> str:
544
    good_date = extract_date_from_project(good)
×
545
    bad_date = extract_date_from_project(bad)
×
546
    days = (bad_date - good_date).days
×
547
    mid_date = good_date + datetime.timedelta(days=days / 2)
×
548
    increment = 0
×
549
    while mid_date != good_date and mid_date != bad_date:
×
550
        mid_project = re.sub("[0-9]+$", mid_date.strftime("%Y%m%d"), good)
×
551
        owner = mid_project.split("/")[0]
×
552
        project = mid_project.split("/")[1]
×
553
        try:
×
554
            for builds in copr_client.build_proxy.get_list(
×
555
                owner, project, "llvm", "succeeded"
556
            ):
557
                if chroot in builds["chroots"]:
×
558
                    return mid_project
×
559
        except:  # noqa: E722
×
560
            pass
×
561

562
        increment = increment * -1
×
563
        if increment < 0:
×
564
            increment -= 1
×
565
        else:
566
            increment += 1
×
567
        mid_date += datetime.timedelta(days=increment)
×
568

569
    return good
×
570

571

572
def pkg_is_ftbfs(ftbfs_data: list[dict[str, str]], pkg: str, tag: str) -> bool:
1✔
573

NEW
574
    for ftbfs_pkg in ftbfs_data:
×
NEW
575
        if ftbfs_pkg["name"] != pkg:
×
NEW
576
            continue
×
NEW
577
        if ftbfs_pkg["collection"] != tag:
×
NEW
578
            continue
×
NEW
579
        return ftbfs_pkg["state"] == "failing"
×
NEW
580
    return False
×
581

582

583
def main() -> None:
1✔
584
    logging.basicConfig(filename="rebuilder.log", level=logging.INFO)
×
585
    parser = argparse.ArgumentParser()
×
586
    parser.add_argument(
×
587
        "command",
588
        type=str,
589
        choices=[
590
            "rebuild",
591
            "get-regressions",
592
            "get-snapshot-date",
593
            "rebuild-in-progress",
594
            "bisect",
595
            "test",
596
        ],
597
    )
598
    parser.add_argument(
×
599
        "--start-date", type=str, help="Any ISO date format is accepted"
600
    )
601
    parser.add_argument("--chroot", type=str)
×
602
    parser.add_argument("--good", type=str)
×
603
    parser.add_argument("--bad", type=str)
×
NEW
604
    parser.add_argument("--llvm-major", type=int)
×
NEW
605
    parser.add_argument("--skip-same-version", action="store_true")
×
606

607
    args = parser.parse_args()
×
608
    copr_client = copr.v3.Client.create_from_config_file()
×
609

610
    os_name = "fedora-rawhide"
×
611
    target_arches = ["aarch64", "ppc64le", "s390x", "x86_64"]
×
612
    target_chroots = [f"{os_name}-{a}" for a in target_arches]
×
613
    project_owner = "@fedora-llvm-team"
×
614
    project_name = "clang-monthly-fedora-rebuild"
×
615

616
    if args.command == "rebuild":
×
617
        exclusions = get_exclusions()
×
618
        pkgs = get_pkgs(exclusions)
×
619
        print(pkgs)
×
620
        try:
×
621
            copr_client.project_proxy.get(project_owner, project_name)
×
622
            copr_pkgs = CoprPkg.get_packages_from_copr(
×
623
                project_owner, project_name, copr_client
624
            )
625
            pkgs = get_monthly_rebuild_packages(pkgs, copr_pkgs)
×
626
        except:  # noqa: E722
×
627
            create_new_project(project_owner, project_name, copr_client, target_chroots)
×
628
        snapshot_project = select_snapshot_project(copr_client, target_chroots)
×
629
        if snapshot_project is not None:
×
630
            start_rebuild(
×
631
                project_owner,
632
                project_name,
633
                copr_client,
634
                pkgs,
635
                snapshot_project,
636
                target_chroots,
637
            )
638
    elif args.command == "get-regressions":
×
639
        start_time = datetime.datetime.fromisoformat(args.start_date)
×
640
        copr_pkgs = CoprPkg.get_packages_from_copr(
×
641
            project_owner, project_name, copr_client
642
        )
643
        pkg_failures = get_monthly_rebuild_regressions(
×
644
            project_owner, project_name, start_time, copr_pkgs
645
        )
646
        get_chroot_results(list(pkg_failures), copr_client)
×
647
        # Delete attributes we don't need to print
648
        for p in pkg_failures:
×
649
            if p is None:
×
650
                continue
×
651
            for k in ["fail_id", "chroots"]:
×
652
                del p[k]
×
653

654
        print(json.dumps(pkg_failures))
×
655
    elif args.command == "get-snapshot-date":
×
656
        project = copr_client.project_proxy.get(project_owner, project_name)
×
657
        for repo in project["additional_repos"]:
×
658
            match = re.match(
×
659
                r"copr://@fedora-llvm-team/llvm-snapshots-big-merge-([0-9]+)$", repo
660
            )
661
            if match:
×
662
                print(datetime.datetime.fromisoformat(match.group(1)).isoformat())
×
663
                return
×
664
    elif args.command == "rebuild-in-progress":
×
665
        for pkg in copr_client.monitor_proxy.monitor(project_owner, project_name)[
×
666
            "packages"
667
        ]:
668
            for c in pkg["chroots"]:
×
669
                build = CoprBuild(pkg["chroots"][c])
×
670
                if build.is_in_progress():
×
671
                    sys.exit(0)
×
672
        sys.exit(1)
×
673
    elif args.command == "bisect":
×
674
        print(find_midpoint_project(copr_client, args.good, args.bad, args.chroot))
×
NEW
675
    elif args.command == "test":
×
NEW
676
        project_owner = "@fedora-llvm-team"
×
NEW
677
        project_name = "clang-fedora-centos-testing"
×
NEW
678
        centos_stream9_chroots = [f"centos-stream-9-{arch}" for arch in target_arches]
×
NEW
679
        centos_stream10_chroots = [f"centos-stream-10-{arch}" for arch in target_arches]
×
NEW
680
        fedora_chroots = [f"fedora-rawhide-{a}" for a in target_arches]
×
NEW
681
        target_chroots = (
×
682
            centos_stream10_chroots + centos_stream9_chroots + fedora_chroots
683
        )
NEW
684
        try:
×
NEW
685
            copr_client.project_proxy.get(project_owner, project_name)
×
NEW
686
        except Exception:
×
NEW
687
            create_new_project(
×
688
                project_owner,
689
                project_name,
690
                copr_client,
691
                target_chroots,
692
                additional_packages=None,
693
                with_opts=None,
694
            )
695
            # Set repo priority so that built packages that depend on a specific
696
            # LLVM snapshot version do not get installed.
NEW
697
            copr_client.project_proxy.edit(
×
698
                project_owner, project_name, repo_priority=1000
699
            )
NEW
700
        centos9_pkgs = get_tier1_pkgs(9)
×
NEW
701
        centos10_pkgs = get_tier1_pkgs(10)
×
NEW
702
        fedora_pkgs = get_tier2_pkgs()
×
703

NEW
704
        copr_client.project_proxy.edit(
×
705
            project_owner,
706
            project_name,
707
            additional_repos=[
708
                "copr://@fedora-llvm-team/llvm-compat-packages",
709
            ],
710
        )
711

712
        # Iterate over a copy of a list so we can remove items:
NEW
713
        for chroot in list(target_chroots):
×
NEW
714
            snapshot_project_name = select_snapshot_project(copr_client, [chroot])
×
NEW
715
            if not snapshot_project_name:
×
NEW
716
                print(f"Could not find snapshot for {chroot}")
×
NEW
717
                target_chroots.remove(chroot)
×
NEW
718
                continue
×
719
            else:
NEW
720
                print(f"Using {snapshot_project_name} for {chroot}")
×
NEW
721
            snapshot_url = f"copr://@fedora-llvm-team/{snapshot_project_name}"
×
NEW
722
            repos = []
×
NEW
723
            for r in copr_client.project_chroot_proxy.get(
×
724
                project_owner, project_name, chroot
725
            )["additional_repos"]:
NEW
726
                if args.skip_same_version and r == snapshot_url:
×
NEW
727
                    print(
×
728
                        f"Not building for {chroot} since snapshot version is the same as the last build"
729
                    )
NEW
730
                    target_chroots.remove(chroot)
×
NEW
731
                if not r.startswith(
×
732
                    "copr://@fedora-llvm-team/llvm-snapshots-big-merge"
733
                ):
NEW
734
                    repos.append(r)
×
NEW
735
            if chroot not in target_chroots:
×
NEW
736
                continue
×
737

NEW
738
            copr_client.project_chroot_proxy.edit(
×
739
                project_owner,
740
                project_name,
741
                chroot,
742
                additional_repos=repos + [snapshot_url],
743
            )
744

NEW
745
        centos_stream9_chroots = [
×
746
            c for c in centos_stream9_chroots if c in target_chroots
747
        ]
NEW
748
        for pkg in centos9_pkgs:
×
NEW
749
            build_pkg(
×
750
                project_owner=project_owner,
751
                project_name=project_name,
752
                copr_client=copr_client,
753
                pkg=pkg,
754
                koji_server="https://kojihub.stream.centos.org/kojihub",
755
                default_commitish="c9s",
756
                build_tag="c9s-candidate",
757
                distgit="centos-stream",
758
                chroots=centos_stream9_chroots,
759
            )
760

NEW
761
        centos_stream10_chroots = [
×
762
            c for c in centos_stream10_chroots if c in target_chroots
763
        ]
NEW
764
        for pkg in centos10_pkgs:
×
NEW
765
            build_pkg(
×
766
                project_owner=project_owner,
767
                project_name=project_name,
768
                copr_client=copr_client,
769
                pkg=pkg,
770
                koji_server="https://kojihub.stream.centos.org/kojihub",
771
                default_commitish="c10s",
772
                build_tag="c10s-candidate",
773
                distgit="centos-stream",
774
                chroots=centos_stream10_chroots,
775
            )
776

NEW
777
        fedora_chroots = [c for c in fedora_chroots if c in target_chroots]
×
778

779
        # Load FTBFS data so we can skip building packages that currently don't build.
NEW
780
        request = urllib.request.Request(
×
781
            "https://koschei.fedoraproject.org/api/v1/packages"
782
        )
783
        # We need to set these headers due to new anti-spam measures in Fedora infrastructure.
NEW
784
        request.add_header("Accept", "text/plain")
×
NEW
785
        request.add_header("User-Agent", "fedora-llvm-team/1.0")
×
NEW
786
        with urllib.request.urlopen(request) as url:
×
NEW
787
            ftbfs_data = json.loads(url.read().decode())
×
788

NEW
789
        rawhide_tag = get_rawhide_tag()
×
NEW
790
        for pkg in fedora_pkgs:
×
NEW
791
            if pkg_is_ftbfs(ftbfs_data, pkg, tag=rawhide_tag):
×
NEW
792
                print(f"Skip building {pkg} on rawhide, because it is FTBFS")
×
NEW
793
                continue
×
NEW
794
            print(f"Building {pkg}")
×
NEW
795
            build_pkg(
×
796
                project_owner=project_owner,
797
                project_name=project_name,
798
                copr_client=copr_client,
799
                pkg=pkg,
800
                default_commitish="rawhide",
801
                build_tag=rawhide_tag,
802
                chroots=fedora_chroots,
803
            )
804

805

806
if __name__ == "__main__":
1✔
807
    main()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc