blob: 7eb2560356da7b643cabf937869c6d634c878e74 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2021 The LUCI Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import datetime
import logging
import sys
import hashlib
import os
import contextlib
import json
import subprocess
import re
KNOWN_REPOS = {
'public': 'https://chromium.googlesource.com/playground/gerrit-cq',
'internal': 'https://chrome-internal.googlesource.com/playground/cq',
}
def parse_args():
p = argparse.ArgumentParser()
p.add_argument('-n', '--number', type=int, required=True,
help='CLs number')
p.add_argument('--cq', type=int, required=True,
help='+1 or +2 to vote on CQ label')
p.add_argument('-w', '--work-dir', dest='workdir', required=True,
help='Path where repos are checked out and results are stored')
p.add_argument('--id',
help='ID of this test. Defaults to current timestamp')
p.add_argument('--description', default='UNSPECIFIED',
help='Description of this test, should fit on one line')
p.add_argument('--repo', default='internal',
help='either "internal" or "public"')
opts = p.parse_args()
if not opts.id:
opts.id = datetime.datetime.now().strftime('%Y%m%dT%H%M%S')
elif ' ' in opts.id:
p.error('spaces not allowed in --id')
if opts.repo not in KNOWN_REPOS:
p.error('only %s are supported for --repo' % sorted(KNOWN_REPOS))
return opts
def main():
opts = parse_args()
logging.basicConfig(level=logging.DEBUG)
ensure_work_dir(opts)
checkout_repos(opts)
cls = create_cls(opts)
def ensure_work_dir(opts):
if not os.path.exists(opts.workdir):
logging.info('Creating %r workdir', opts.workdir)
os.makedirs(opts.workdir)
return
if os.path.isdir(opts.workdir):
return
raise Exception('%s is not a dir' % opts.workdir)
def checkout_repos(opts):
for repo, origin in KNOWN_REPOS.items():
if os.path.exists(os.path.join(opts.workdir, repo)):
continue
git('clone', origin, repo, cwd=opts.workdir)
def create_cls(opts):
cwd = os.path.join(opts.workdir, opts.repo)
git('freeze', cwd=cwd) # just in case
git('fetch', 'origin', cwd=cwd) # avoid conflicts
hash_prefix = hashlib.sha1(opts.id.encode('utf8')).hexdigest()[:-4]
git('new-branch', '%s-%s' % (opts.id, hash_prefix), cwd=cwd)
parent = os.path.join('cl-stack-tests', opts.id)
logging.info('Creating %d commits in %s %s', opts.number, opts.repo, parent)
os.makedirs(os.path.join(cwd, parent))
for i in range(opts.number):
gen_commit(opts, n=i+1, rel_parent_dir=parent, hash_prefix=hash_prefix, cwd=cwd)
rev = git('rev-parse', 'HEAD', cwd=cwd)
logging.info('Creating %d CLs by pushing %s to Gerrit', opts.number, rev[:8])
git('push', 'origin', 'HEAD:refs/for/refs/heads/main',
'-o', 'l=Code-Review+1',
'-o', 'l=Commit-Queue+2',
'-o', 'hashtag=cv-test',
'-o', 'hashtag=%s' % opts.id,
cwd=cwd)
def gen_commit(opts, n, rel_parent_dir, hash_prefix, cwd):
rel_fpath = os.path.join(rel_parent_dir, '%03d' % n)
# Full sha1 hash is 40 chars long.
suffix_tmpl = '%%0%dd' % (40-len(hash_prefix),)
change_id = hash_prefix + (suffix_tmpl % (n,))
with open(os.path.join(cwd, rel_fpath), 'w') as f:
f.write('Commit #%d\n' % n)
git('add', rel_fpath, cwd=cwd)
msg_lines = [
'Commit #%d' % n,
'',
'Test-Description: %s' % opts.description,
'Test-Id: %s' % opts.id,
'Change-Id: I%s' % change_id,
]
msg = '\n'.join(msg_lines)
git('commit', '-m', msg, cwd=cwd)
def git(*args, cwd=None):
out = subprocess.check_output(['git'] + list(args), cwd=cwd)
return out.strip()
if __name__ == '__main__':
main()
sys.exit(0)