Skip to content

Commit 1e0041c

Browse files
authored
Merge pull request #71 from rust-lang/test-on-fork
Add support for running builds in a fork of the repository
2 parents b40db38 + 4cd6aaf commit 1e0041c

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

cfg.sample.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,21 @@ try_users = []
101101
#auto = "auto"
102102
#try = "try"
103103

104+
# test-on-fork allows you to run the CI builds for a project in a separate fork
105+
# instead of the main repository, while still approving PRs and merging the
106+
# commits in the main one.
107+
#
108+
# To enable test-on-fork you need to uncomment the section below and fill the
109+
# fork's owner and repository name. The fork MUST BE AN ACTUAL GITHUB FORK for
110+
# this feature to work. That means it will likely need to be in a separate
111+
# GitHub organization.
112+
#
113+
# This only works when `local_git = true`.
114+
#
115+
#[repo.NAME.test-on-fork]
116+
#owner = ""
117+
#name = ""
118+
104119
[repo.NAME.github]
105120
# Arbitrary secret. You can generate one with: openssl rand -hex 20
106121
secret = ""

homu/main.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Repository:
7979
treeclosed = -1
8080
treeclosed_src = None
8181
gh = None
82+
gh_test_on_fork = None
8283
label = None
8384
db = None
8485

@@ -133,7 +134,7 @@ class PullReqState:
133134
delegate = ''
134135

135136
def __init__(self, num, head_sha, status, db, repo_label, mergeable_que,
136-
gh, owner, name, label_events, repos):
137+
gh, owner, name, label_events, repos, test_on_fork):
137138
self.head_advanced('', use_db=False)
138139

139140
self.num = num
@@ -149,6 +150,7 @@ def __init__(self, num, head_sha, status, db, repo_label, mergeable_que,
149150
self.timeout_timer = None
150151
self.test_started = time.time()
151152
self.label_events = label_events
153+
self.test_on_fork = test_on_fork
152154

153155
def head_advanced(self, head_sha, *, use_db=True):
154156
self.head_sha = head_sha
@@ -313,6 +315,22 @@ def get_repo(self):
313315
assert repo.name == self.name
314316
return repo
315317

318+
def get_test_on_fork_repo(self):
319+
if not self.test_on_fork:
320+
return None
321+
322+
repo = self.repos[self.repo_label].gh_test_on_fork
323+
if not repo:
324+
repo = self.gh.repository(
325+
self.test_on_fork['owner'],
326+
self.test_on_fork['name'],
327+
)
328+
self.repos[self.repo_label].gh_test_on_fork = repo
329+
330+
assert repo.owner.login == self.test_on_fork['owner']
331+
assert repo.name == self.test_on_fork['name']
332+
return repo
333+
316334
def save(self):
317335
db_query(
318336
self.db,
@@ -792,9 +810,9 @@ def handle_hook_response(state, hook_cfg, body, extra_data):
792810
def git_push(git_cmd, branch, state):
793811
merge_sha = subprocess.check_output(git_cmd('rev-parse', 'HEAD')).decode('ascii').strip() # noqa
794812

795-
if utils.silent_call(git_cmd('push', '-f', 'origin', branch)):
813+
if utils.silent_call(git_cmd('push', '-f', 'test-origin', branch)):
796814
utils.logged_call(git_cmd('branch', '-f', 'homu-tmp', branch))
797-
utils.logged_call(git_cmd('push', '-f', 'origin', 'homu-tmp'))
815+
utils.logged_call(git_cmd('push', '-f', 'test-origin', 'homu-tmp'))
798816

799817
def inner():
800818
utils.github_create_status(
@@ -814,14 +832,14 @@ def fail(err):
814832

815833
utils.retry_until(inner, fail, state)
816834

817-
utils.logged_call(git_cmd('push', '-f', 'origin', branch))
835+
utils.logged_call(git_cmd('push', '-f', 'test-origin', branch))
818836

819837
return merge_sha
820838

821839

822840
def init_local_git_cmds(repo_cfg, git_cfg):
823841
fpath = 'cache/{}/{}'.format(repo_cfg['owner'], repo_cfg['name'])
824-
url = 'git@github.com:{}/{}.git'.format(repo_cfg['owner'], repo_cfg['name']) # noqa
842+
genurl = lambda cfg: 'git@github.com:{}/{}.git'.format(cfg['owner'], cfg['name']) # noqa
825843

826844
if not os.path.exists(SSH_KEY_FILE):
827845
os.makedirs(os.path.dirname(SSH_KEY_FILE), exist_ok=True)
@@ -831,7 +849,18 @@ def init_local_git_cmds(repo_cfg, git_cfg):
831849

832850
if not os.path.exists(fpath):
833851
utils.logged_call(['git', 'init', fpath])
834-
utils.logged_call(['git', '-C', fpath, 'remote', 'add', 'origin', url]) # noqa
852+
853+
remotes = {
854+
'origin': genurl(repo_cfg),
855+
'test-origin': genurl(repo_cfg.get('test-on-fork', repo_cfg)),
856+
}
857+
858+
for remote, url in remotes.items():
859+
try:
860+
utils.logged_call(['git', '-C', fpath, 'remote', 'set-url', remote, url]) # noqa
861+
utils.logged_call(['git', '-C', fpath, 'remote', 'set-url', '--push', remote, url]) # noqa
862+
except subprocess.CalledProcessError:
863+
utils.logged_call(['git', '-C', fpath, 'remote', 'add', remote, url]) # noqa
835864

836865
return lambda *args: ['git', '-C', fpath] + list(args)
837866

@@ -1511,7 +1540,7 @@ def synchronize(repo_label, repo_cfg, logger, gh, states, repos, db, mergeable_q
15111540
status = info.state
15121541
break
15131542

1514-
state = PullReqState(pull.number, pull.head.sha, status, db, repo_label, mergeable_que, gh, repo_cfg['owner'], repo_cfg['name'], repo_cfg.get('labels', {}), repos) # noqa
1543+
state = PullReqState(pull.number, pull.head.sha, status, db, repo_label, mergeable_que, gh, repo_cfg['owner'], repo_cfg['name'], repo_cfg.get('labels', {}), repos, repo_cfg.get('test-on-fork')) # noqa
15151544
state.title = pull.title
15161545
state.body = pull.body
15171546
state.head_ref = pull.head.repo[0] + ':' + pull.head.ref
@@ -1702,6 +1731,13 @@ def main():
17021731
repo_cfgs[repo_label] = repo_cfg
17031732
repo_labels[repo_cfg['owner'], repo_cfg['name']] = repo_label
17041733

1734+
# If test-on-fork is enabled point both the main repo and the fork to
1735+
# the same homu "repository". This will allow events coming from both
1736+
# GitHub repositories to be processed the same way.
1737+
if 'test-on-fork' in repo_cfg:
1738+
tof = repo_cfg['test-on-fork']
1739+
repo_labels[tof['owner'], tof['name']] = repo_label
1740+
17051741
repo_states = {}
17061742
repos[repo_label] = Repository(None, repo_label, db)
17071743

@@ -1710,7 +1746,7 @@ def main():
17101746
'SELECT num, head_sha, status, title, body, head_ref, base_ref, assignee, approved_by, priority, try_, rollup, delegate, merge_sha FROM pull WHERE repo = ?', # noqa
17111747
[repo_label])
17121748
for num, head_sha, status, title, body, head_ref, base_ref, assignee, approved_by, priority, try_, rollup, delegate, merge_sha in db.fetchall(): # noqa
1713-
state = PullReqState(num, head_sha, status, db, repo_label, mergeable_que, gh, repo_cfg['owner'], repo_cfg['name'], repo_cfg.get('labels', {}), repos) # noqa
1749+
state = PullReqState(num, head_sha, status, db, repo_label, mergeable_que, gh, repo_cfg['owner'], repo_cfg['name'], repo_cfg.get('labels', {}), repos, repo_cfg.get('test-on-fork')) # noqa
17141750
state.title = title
17151751
state.body = body
17161752
state.head_ref = head_ref

homu/server.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,8 @@ def github():
420420
info['repository']['owner']['login'],
421421
info['repository']['name'],
422422
repo_cfg.get('labels', {}),
423-
g.repos)
423+
g.repos,
424+
repo_cfg.get('test-on-fork'))
424425
state.title = info['pull_request']['title']
425426
state.body = info['pull_request']['body']
426427
state.head_ref = info['pull_request']['head']['repo']['owner']['login'] + ':' + info['pull_request']['head']['ref'] # noqa
@@ -656,19 +657,25 @@ def report_build_res(succ, url, builder, state, logger, repo_cfg):
656657
merge_sha=state.merge_sha,
657658
))
658659
state.change_labels(LabelEvent.SUCCEED)
660+
661+
def set_ref():
662+
utils.github_set_ref(state.get_repo(), 'heads/' +
663+
state.base_ref, state.merge_sha)
664+
if state.test_on_fork is not None:
665+
utils.github_set_ref(state.get_test_on_fork_repo(),
666+
'heads/' + state.base_ref,
667+
state.merge_sha, force=True)
659668
try:
660669
try:
661-
utils.github_set_ref(state.get_repo(), 'heads/' +
662-
state.base_ref, state.merge_sha)
670+
set_ref()
663671
except github3.models.GitHubError:
664672
utils.github_create_status(
665673
state.get_repo(),
666674
state.merge_sha,
667675
'success', '',
668676
'Branch protection bypassed',
669677
context='homu')
670-
utils.github_set_ref(state.get_repo(), 'heads/' +
671-
state.base_ref, state.merge_sha)
678+
set_ref()
672679

673680
state.fake_merge(repo_cfg)
674681

0 commit comments

Comments
 (0)