Skip to content

Commit 739965d

Browse files
committed
dco: Add dco check
Signed-off-by: Jan Kraemer <[email protected]>
1 parent cf5063e commit 739965d

File tree

4 files changed

+134
-43
lines changed

4 files changed

+134
-43
lines changed

.github/workflows/sil-kit-ci.yml

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ jobs:
4646
python3 ./SilKit/ci/check_files_changed.py ${{ github.repository }} ${{ github.event.number }}; \
4747
fi
4848
49+
check-dco-signed:
50+
name: 🦙 DCO Signing Check
51+
runs-on: ubuntu-22.04
52+
steps:
53+
- uses: actions/checkout@v4
54+
with:
55+
submodules: false
56+
57+
- name: Check if DCO is signed
58+
run:
59+
python3 ./SilKit/ci/check_dco_signed.py ${{ github.repository }} ${{ github.event.number }}
60+
4961
check-formatting:
5062
name: Format checks for SIL Kit sources
5163
runs-on: ubuntu-24.04
@@ -62,7 +74,7 @@ jobs:
6274
clang-tidy:
6375
name: Running clang-tidy
6476
runs-on: ubuntu-latest
65-
needs: [check-run-builds]
77+
needs: [check-run-builds, check-dco-signed]
6678
steps:
6779
- uses: actions/checkout@v4
6880
with:
@@ -87,51 +99,51 @@ jobs:
8799

88100
clang14-tsan:
89101
name: Thread Sanitizer Tests
90-
needs: [check-licenses, check-run-builds, check-formatting]
102+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
91103
uses: ./.github/workflows/linux-tsan.yml
92104
with:
93105
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
94106

95107
clang14-ubsan:
96108
name: Undefined Behavior Sanitizer Tests
97-
needs: [check-licenses, check-run-builds, check-formatting]
109+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
98110
uses: ./.github/workflows/linux-ubsan.yml
99111
with:
100112
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
101113

102114
clang14-asan:
103115
name: Address Sanitizer Tests
104-
needs: [check-licenses, check-run-builds, check-formatting]
116+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
105117
uses: ./.github/workflows/linux-asan.yml
106118
with:
107119
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
108120

109121
ubuntu-release-builds:
110122
uses: ./.github/workflows/build-linux.yml
111-
needs: [check-licenses, check-run-builds, check-formatting]
123+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
112124
with:
113125
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
114126

115127
ubuntu-arm-release-builds:
116128
uses: ./.github/workflows/build-linux-arm64.yml
117-
needs: [check-licenses, check-run-builds, check-formatting]
129+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
118130
with:
119131
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
120132

121133
windows-release-builds:
122134
uses: ./.github/workflows/build-win.yml
123-
needs: [check-licenses, check-run-builds, check-formatting]
135+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
124136
with:
125137
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
126138

127139
mingw-release-builds:
128140
uses: ./.github/workflows/build-mingw64.yml
129-
needs: [check-licenses, check-run-builds, check-formatting]
141+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
130142
with:
131143
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}
132144

133145
macos-release-builds:
134146
uses: ./.github/workflows/build-macos.yml
135-
needs: [check-licenses, check-run-builds, check-formatting]
147+
needs: [check-licenses, check-run-builds, check-formatting, check-dco-signed]
136148
with:
137149
run_build: ${{ needs.check-run-builds.outputs.run_builds == 'true' }}

SilKit/ci/check_dco_signed.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
3+
# SPDX-FileCopyrightText: 2025 Vector Informatik GmbH
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
import requests
8+
import argparse
9+
10+
from ci_utils import die, warn
11+
12+
13+
def check_dco(url: str):
14+
commits_url = url + '/commits'
15+
16+
r = requests.get(commits_url, verify=False)
17+
18+
all_commits_signed = True
19+
for commitObject in r.json():
20+
committer = commitObject["commit"]["committer"]
21+
author = commitObject["commit"]["author"]
22+
message = commitObject["commit"]["message"]
23+
24+
committer_error = ""
25+
author_error = ""
26+
27+
# Do not check fixup messages
28+
if "fixup" not in message:
29+
30+
# Check committer only if it they are different
31+
if committer["email"] != author["email"] or committer["name"] != author["name"]:
32+
sign_off = f'Signed-off-by: {committer["name"]} <{committer["email"]}>'
33+
if sign_off not in message and committer["name"] != "GitHub":
34+
comitter_error = f'Signed-off-by from committer {committer["name"]} missing! Merging blocked!'
35+
36+
sign_off = f'Signed-off-by: {author["name"]} <{author["email"]}>'
37+
if sign_off not in message:
38+
author_error = f'Signed-off-by from author {author["name"]} missing, Merging Blocked!'
39+
40+
if committer_error or author_error:
41+
all_commits_signed = False
42+
warn(f'{commitObject["sha"][0:7]}: {author_error} {committer_error}')
43+
44+
if all_commits_signed is False:
45+
die(66, "Not all commits Signed-off by their respective author/committer")
46+
47+
48+
def main():
49+
50+
parser = argparse.ArgumentParser(prog="DCO checker",
51+
description="Check whether the DCO was signed for a PR")
52+
parser.add_argument('repo', type=str)
53+
parser.add_argument('PR', type=str)
54+
args = parser.parse_args()
55+
url = 'https://api.github.com/repos/' + args.repo + '/pulls/' + args.PR
56+
check_dco(url)
57+
58+
59+
if __name__ == "__main__":
60+
main()

SilKit/ci/check_files_changed.py

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,30 @@
88
import argparse
99
import os
1010

11-
isCI = os.getenv('CI')
12-
INFO_PREFIX = "::notice ::" if isCI != None else "INFO: "
13-
WARN_PREFIX = "::warning ::" if isCI != None else "WARNING: "
14-
ERROR_PREFIX = "::error ::" if isCI != None else "ERROR: "
11+
from ci_utils import log, isCI
1512

16-
## Convenience
17-
def log(fmt, *args):
18-
print(fmt.format(*args))
1913

20-
def info(fmt, *args):
21-
log(INFO_PREFIX + fmt, *args)
14+
def check_run_build(url: str):
15+
run_builds = "false"
16+
files_url = url + '/files'
17+
r = requests.get(files_url, verify=False)
2218

23-
def warn(fmt, *args):
24-
log(WARN_PREFIX + fmt, *args)
19+
for fileObject in r.json():
20+
21+
file_path = fileObject["filename"]
22+
file = file_path.split(sep="/")[-1]
23+
24+
if file not in exceptional_files:
25+
run_builds = "true"
26+
break
27+
28+
log("Builds should run: {}".format(run_builds))
29+
30+
if isCI is not None:
31+
log("Setting GITHUB_OUTPUT!")
32+
with open(os.environ["GITHUB_OUTPUT"], 'a') as f:
33+
print("run_builds={}".format(run_builds), file=f)
2534

26-
def die(status, fmt, *args):
27-
log(ERROR_PREFIX + fmt, *args)
28-
sys.exit(status)
2935

3036
# File Set
3137
exceptional_files = {'README.rst', 'latest.md', 'LICENSE', 'CONTRIBUTING.md',
@@ -37,26 +43,8 @@ def die(status, fmt, *args):
3743
parser.add_argument('PR', type=str)
3844
args = parser.parse_args()
3945

40-
run_builds = "false"
41-
42-
url = 'https://api.github.com/repos/' + args.repo + '/pulls/' + args.PR + '/files'
46+
url = 'https://api.github.com/repos/' + args.repo + '/pulls/' + args.PR
4347

4448
log("Checking at {}".format(url))
4549

46-
r = requests.get(url, verify=False)
47-
48-
for fileObject in r.json():
49-
50-
file_path = fileObject["filename"];
51-
file = file_path.split(sep="/")[-1]
52-
53-
if file not in exceptional_files:
54-
run_builds = "true"
55-
break
56-
57-
log("Builds should run: {}".format(run_builds))
58-
59-
if isCI != None:
60-
log("Setting GITHUB_OUTPUT!")
61-
with open(os.environ["GITHUB_OUTPUT"], 'a') as f:
62-
print("run_builds={}".format(run_builds), file=f)
50+
check_run_build(url)

SilKit/ci/ci_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env python3
2+
3+
# SPDX-FileCopyrightText: 2025 Vector Informatik GmbH
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
import os
8+
import sys
9+
10+
isCI = os.getenv('CI')
11+
INFO_PREFIX = "::notice ::" if isCI != None else "INFO: "
12+
WARN_PREFIX = "::warning ::" if isCI != None else "WARNING: "
13+
ERROR_PREFIX = "::error ::" if isCI != None else "ERROR: "
14+
15+
16+
# Convenience
17+
def log(fmt, *args):
18+
print(fmt.format(*args))
19+
20+
21+
def info(fmt, *args):
22+
log(INFO_PREFIX + fmt, *args)
23+
24+
25+
def warn(fmt, *args):
26+
log(WARN_PREFIX + fmt, *args)
27+
28+
29+
def die(status, fmt, *args):
30+
log(ERROR_PREFIX + fmt, *args)
31+
sys.exit(status)

0 commit comments

Comments
 (0)