xref: /openbmc/qemu/.gitlab-ci.d/check-dco.py (revision 96292515)
1*34ed46a2SDaniel P. Berrangé#!/usr/bin/env python3
2*34ed46a2SDaniel P. Berrangé#
3*34ed46a2SDaniel P. Berrangé# check-dco.py: validate all commits are signed off
4*34ed46a2SDaniel P. Berrangé#
5*34ed46a2SDaniel P. Berrangé# Copyright (C) 2020 Red Hat, Inc.
6*34ed46a2SDaniel P. Berrangé#
7*34ed46a2SDaniel P. Berrangé# SPDX-License-Identifier: GPL-2.0-or-later
8*34ed46a2SDaniel P. Berrangé
9*34ed46a2SDaniel P. Berrangéimport os
10*34ed46a2SDaniel P. Berrangéimport os.path
11*34ed46a2SDaniel P. Berrangéimport sys
12*34ed46a2SDaniel P. Berrangéimport subprocess
13*34ed46a2SDaniel P. Berrangé
14*34ed46a2SDaniel P. Berrangénamespace = "qemu-project"
15*34ed46a2SDaniel P. Berrangéif len(sys.argv) >= 2:
16*34ed46a2SDaniel P. Berrangé    namespace = sys.argv[1]
17*34ed46a2SDaniel P. Berrangé
18*34ed46a2SDaniel P. Berrangécwd = os.getcwd()
19*34ed46a2SDaniel P. Berrangéreponame = os.path.basename(cwd)
20*34ed46a2SDaniel P. Berrangérepourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame)
21*34ed46a2SDaniel P. Berrangé
22*34ed46a2SDaniel P. Berrangésubprocess.check_call(["git", "remote", "add", "check-dco", repourl])
23*34ed46a2SDaniel P. Berrangésubprocess.check_call(["git", "fetch", "check-dco", "master"],
24*34ed46a2SDaniel P. Berrangé                      stdout=subprocess.DEVNULL,
25*34ed46a2SDaniel P. Berrangé                      stderr=subprocess.DEVNULL)
26*34ed46a2SDaniel P. Berrangé
27*34ed46a2SDaniel P. Berrangéancestor = subprocess.check_output(["git", "merge-base",
28*34ed46a2SDaniel P. Berrangé                                    "check-dco/master", "HEAD"],
29*34ed46a2SDaniel P. Berrangé                                   universal_newlines=True)
30*34ed46a2SDaniel P. Berrangé
31*34ed46a2SDaniel P. Berrangéancestor = ancestor.strip()
32*34ed46a2SDaniel P. Berrangé
33*34ed46a2SDaniel P. Berrangésubprocess.check_call(["git", "remote", "rm", "check-dco"])
34*34ed46a2SDaniel P. Berrangé
35*34ed46a2SDaniel P. Berrangéerrors = False
36*34ed46a2SDaniel P. Berrangé
37*34ed46a2SDaniel P. Berrangéprint("\nChecking for 'Signed-off-by: NAME <EMAIL>' " +
38*34ed46a2SDaniel P. Berrangé      "on all commits since %s...\n" % ancestor)
39*34ed46a2SDaniel P. Berrangé
40*34ed46a2SDaniel P. Berrangélog = subprocess.check_output(["git", "log", "--format=%H %s",
41*34ed46a2SDaniel P. Berrangé                               ancestor + "..."],
42*34ed46a2SDaniel P. Berrangé                              universal_newlines=True)
43*34ed46a2SDaniel P. Berrangé
44*34ed46a2SDaniel P. Berrangéif log == "":
45*34ed46a2SDaniel P. Berrangé    commits = []
46*34ed46a2SDaniel P. Berrangéelse:
47*34ed46a2SDaniel P. Berrangé    commits = [[c[0:40], c[41:]] for c in log.strip().split("\n")]
48*34ed46a2SDaniel P. Berrangé
49*34ed46a2SDaniel P. Berrangéfor sha, subject in commits:
50*34ed46a2SDaniel P. Berrangé
51*34ed46a2SDaniel P. Berrangé    msg = subprocess.check_output(["git", "show", "-s", sha],
52*34ed46a2SDaniel P. Berrangé                                  universal_newlines=True)
53*34ed46a2SDaniel P. Berrangé    lines = msg.strip().split("\n")
54*34ed46a2SDaniel P. Berrangé
55*34ed46a2SDaniel P. Berrangé    print("�� %s %s" % (sha, subject))
56*34ed46a2SDaniel P. Berrangé    sob = False
57*34ed46a2SDaniel P. Berrangé    for line in lines:
58*34ed46a2SDaniel P. Berrangé        if "Signed-off-by:" in line:
59*34ed46a2SDaniel P. Berrangé            sob = True
60*34ed46a2SDaniel P. Berrangé            if "localhost" in line:
61*34ed46a2SDaniel P. Berrangé                print("    ❌ FAIL: bad email in %s" % line)
62*34ed46a2SDaniel P. Berrangé                errors = True
63*34ed46a2SDaniel P. Berrangé
64*34ed46a2SDaniel P. Berrangé    if not sob:
65*34ed46a2SDaniel P. Berrangé        print("    ❌ FAIL missing Signed-off-by tag")
66*34ed46a2SDaniel P. Berrangé        errors = True
67*34ed46a2SDaniel P. Berrangé
68*34ed46a2SDaniel P. Berrangéif errors:
69*34ed46a2SDaniel P. Berrangé    print("""
70*34ed46a2SDaniel P. Berrangé
71*34ed46a2SDaniel P. Berrangé❌ ERROR: One or more commits are missing a valid Signed-off-By tag.
72*34ed46a2SDaniel P. Berrangé
73*34ed46a2SDaniel P. Berrangé
74*34ed46a2SDaniel P. BerrangéThis project requires all contributors to assert that their contributions
75*34ed46a2SDaniel P. Berrangéare provided in compliance with the terms of the Developer's Certificate
76*34ed46a2SDaniel P. Berrangéof Origin 1.1 (DCO):
77*34ed46a2SDaniel P. Berrangé
78*34ed46a2SDaniel P. Berrangé  https://developercertificate.org/
79*34ed46a2SDaniel P. Berrangé
80*34ed46a2SDaniel P. BerrangéTo indicate acceptance of the DCO every commit must have a tag
81*34ed46a2SDaniel P. Berrangé
82*34ed46a2SDaniel P. Berrangé  Signed-off-by: REAL NAME <EMAIL>
83*34ed46a2SDaniel P. Berrangé
84*34ed46a2SDaniel P. BerrangéThis can be achieved by passing the "-s" flag to the "git commit" command.
85*34ed46a2SDaniel P. Berrangé
86*34ed46a2SDaniel P. BerrangéTo bulk update all commits on current branch "git rebase" can be used:
87*34ed46a2SDaniel P. Berrangé
88*34ed46a2SDaniel P. Berrangé  git rebase -i master -x 'git commit --amend --no-edit -s'
89*34ed46a2SDaniel P. Berrangé
90*34ed46a2SDaniel P. Berrangé""")
91*34ed46a2SDaniel P. Berrangé
92*34ed46a2SDaniel P. Berrangé    sys.exit(1)
93*34ed46a2SDaniel P. Berrangé
94*34ed46a2SDaniel P. Berrangésys.exit(0)
95