Skip to content

Commit 3bd3fbc

Browse files
Sync dependencies tool and CI job (#496)
## Usage and product changes We add a sync-dependencies job to be run in CI after successful snapshot and release deployments. The job sends a request to vaticle-bot to update all downstream dependencies.
1 parent 396404b commit 3bd3fbc

File tree

5 files changed

+217
-6
lines changed

5 files changed

+217
-6
lines changed

.factory/automation.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,12 @@ build:
2323
image: vaticle-ubuntu-22.04
2424
command: |
2525
bazel build //...
26+
sync-dependencies:
27+
image: vaticle-ubuntu-22.04
28+
filter:
29+
owner: vaticle
30+
branch: [master, development]
31+
dependencies: [build]
32+
command: |
33+
export SYNC_DEPENDENCIES_TOKEN=$REPO_GITHUB_TOKEN
34+
bazel run @vaticle_dependencies//tool/sync:dependencies -- --source ${FACTORY_REPO}@${FACTORY_COMMIT}

tool/release/BUILD

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
package(default_visibility = ["//visibility:public"])
1919

20-
load("@vaticle_dependencies_ci_pip//:requirements.bzl", "requirement")
21-
2220
py_binary(
2321
name = "docs",
2422
srcs = ["docs.py"],

tool/requirements.txt

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
1-
PyGithub==1.55
2-
urllib3==1.24.2
1+
alabaster==0.7.16
2+
babel==2.14.0
3+
certifi==2018.8.24
4+
cffi==1.16.0
35
chardet==3.0.4
6+
charset-normalizer==3.3.2
7+
cryptography==41.0.7
8+
deprecated==1.2.14
9+
docutils==0.20.1
410
idna==2.7
5-
wrapt==1.10.11
6-
certifi==2018.8.24
11+
imagesize==1.4.1
12+
jinja2==3.1.3
13+
markupsafe==2.1.3
14+
packaging==23.2
15+
pycparser==2.21
16+
PyGithub==2.1.1
17+
pygments==2.17.2
18+
pyjwt[crypto]==2.8.0
19+
pynacl==1.4.0
20+
python-dateutil==2.8.2
21+
requests==2.31.0
22+
six==1.16.0
23+
snowballstemmer==2.2.0
24+
sphinx==3.0.0
25+
sphinxcontrib-applehelp==1.0.8
26+
sphinxcontrib-devhelp==1.0.6
27+
sphinxcontrib-htmlhelp==2.0.5
28+
sphinxcontrib-jsmath==1.0.1
29+
sphinxcontrib-qthelp==1.0.7
30+
sphinxcontrib-serializinghtml==1.1.10
31+
typing-extensions==4.0.0
32+
urllib3==1.26.15
33+
wrapt==1.16.0

tool/sync/BUILD

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU Affero General Public License as
6+
# published by the Free Software Foundation, either version 3 of the
7+
# License, or (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU Affero General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Affero General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
#
17+
18+
load("@vaticle_dependencies_ci_pip//:requirements.bzl", "requirement")
19+
20+
package(default_visibility = ["//visibility:public"])
21+
22+
py_binary(
23+
name = "dependencies",
24+
srcs = ["dependencies.py"],
25+
main = "dependencies.py",
26+
deps = [
27+
"//tool/common:common",
28+
requirement("PyGithub"),
29+
requirement("urllib3"),
30+
requirement("chardet"),
31+
requirement("idna"),
32+
requirement("wrapt"),
33+
requirement("certifi"),
34+
],
35+
)

tool/sync/dependencies.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env python
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU Affero General Public License as
6+
# published by the Free Software Foundation, either version 3 of the
7+
# License, or (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU Affero General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Affero General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
#
17+
18+
"""
19+
sync-dependencies.py updates bazel dependencies between @vaticle repositories
20+
21+
Example usage:
22+
bazel run @vaticle_dependencies//tool/sync:dependencies -- --source typedb-driver@1a2b3c4d1a2b3c4d1a2b3c4d1a2b3c4d1a2b3c4g
23+
"""
24+
25+
import argparse
26+
import tool.common.common as tc
27+
import github
28+
import hashlib
29+
import hmac
30+
import json
31+
import os
32+
import re
33+
import subprocess as sp
34+
import sys
35+
36+
IS_CIRCLECI = bool(os.getenv('CIRCLECI'))
37+
IS_FACTORY = bool(os.getenv('FACTORY_REPO'))
38+
IS_CI_ENV = IS_CIRCLECI or IS_FACTORY
39+
40+
GITHUB_TOKEN = os.getenv('SYNC_DEPENDENCIES_TOKEN')
41+
if GITHUB_TOKEN is None:
42+
raise Exception("$SYNC_DEPENDENCIES_TOKEN is not set!")
43+
44+
BOT_HOST = 'https://bot.vaticle.com'
45+
if not IS_CI_ENV:
46+
BOT_HOST = 'http://localhost:8000'
47+
48+
BOT_SYNC_DEPS = '{0}/sync/dependencies'.format(BOT_HOST)
49+
50+
CMDLINE_PARSER = argparse.ArgumentParser(description='Automatic updater for Vaticle inter-repository dependencies')
51+
CMDLINE_PARSER.add_argument('--dry-run', help='Do not perform any real actions')
52+
CMDLINE_PARSER.add_argument('--source', required=True)
53+
54+
COMMIT_SUBJECT_PREFIX = "//tool/sync:dependencies"
55+
regex_git_commit = r'[0-9a-f]{40}'
56+
regex_git_tag = r'([0-9]+\.[0-9]+\.[0-9]+)'
57+
58+
vaticle = 'vaticle'
59+
github_connection = github.Github(GITHUB_TOKEN)
60+
github_org = github_connection.get_organization(vaticle)
61+
62+
63+
def is_building_upstream():
64+
""" Returns False is running in a forked repo"""
65+
if IS_CIRCLECI:
66+
return vaticle in os.getenv('CIRCLE_REPOSITORY_URL', '')
67+
elif IS_FACTORY:
68+
return vaticle == os.getenv('FACTORY_OWNER')
69+
else:
70+
return False
71+
72+
73+
def exception_handler(fun):
74+
""" Decorator printing additional message on CalledProcessError """
75+
76+
def wrapper(*args, **kwargs):
77+
# pylint: disable=missing-docstring
78+
try:
79+
fun(*args, **kwargs)
80+
except sp.CalledProcessError as ex:
81+
print(f'An error occurred when running {ex.cmd}. Process exited with code {ex.returncode} and message {ex.output}')
82+
print()
83+
raise ex
84+
85+
return wrapper
86+
87+
88+
def short_commit(commit_sha):
89+
return sp.check_output(['git', 'rev-parse', '--short=7', commit_sha], cwd=os.getenv("BUILD_WORKSPACE_DIRECTORY")).decode().replace('\n', '')
90+
91+
92+
@exception_handler
93+
def main():
94+
if not is_building_upstream():
95+
print('Sync dependencies aborted: not building the upstream repo on @vaticle')
96+
exit(0)
97+
98+
arguments = CMDLINE_PARSER.parse_args(sys.argv[1:])
99+
source_repo, source_ref = arguments.source.split('@')
100+
101+
if re.match(regex_git_commit, source_ref) is not None:
102+
source_ref_short = short_commit(source_ref)
103+
elif re.match(regex_git_tag, source_ref) is not None:
104+
source_ref_short = source_ref
105+
else:
106+
raise ValueError
107+
108+
github_repo = github_org.get_repo(source_repo)
109+
github_commit = github_repo.get_commit(source_ref)
110+
source_message = github_commit.commit.message
111+
112+
# TODO: Check that the commit author is @vaticle-bot
113+
if not source_message.startswith(COMMIT_SUBJECT_PREFIX):
114+
sync_message = '{0} {1}/{2}@{3}'.format(COMMIT_SUBJECT_PREFIX, vaticle, source_repo, source_ref_short)
115+
else:
116+
sync_message = source_message
117+
118+
print('Requesting the synchronisation of dependency to {0}/{1}@{2}'.format(vaticle, source_repo, source_ref_short))
119+
120+
print('Constructing request payload:')
121+
sync_data = {
122+
'source-repo': source_repo,
123+
'source-ref': source_ref,
124+
'sync-message': sync_message,
125+
}
126+
print(str(sync_data))
127+
128+
sync_data_json = json.dumps(sync_data)
129+
signature = hmac.new(GITHUB_TOKEN.encode(), sync_data_json.encode(), hashlib.sha1).hexdigest()
130+
131+
if arguments.dry_run:
132+
pass
133+
else:
134+
print('Sending post request to: ' + BOT_SYNC_DEPS)
135+
tc.shell_execute([
136+
'curl', '-X', 'POST', '--data', sync_data_json, '-H', 'Content-Type: application/json', '-H', 'X-Hub-Signature: ' + signature, BOT_SYNC_DEPS
137+
])
138+
print('DONE!')
139+
140+
141+
if __name__ == '__main__':
142+
main()

0 commit comments

Comments
 (0)