eobuilder/eobuilder-ctl

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

566 lines
21 KiB
Plaintext
Raw Normal View History

2020-04-18 14:44:12 +02:00
#!/usr/bin/env python3
2014-01-31 18:16:59 +01:00
import atexit
import random
2014-02-11 19:19:23 +01:00
import re
import shutil
2019-02-17 09:50:10 +01:00
import subprocess
import sys
2014-02-13 11:02:48 +01:00
import tarfile
import time
import urllib.parse
2014-02-11 19:19:23 +01:00
import os
2015-08-19 11:52:32 +02:00
from eobuilder import settings, VERSION, init
from eobuilder.changelog import changelog_from_git
from eobuilder.cmdline import parse_cmdline, error, cat, touch, call, output, setup_py
# fix urljoin for git+ssh:// URLs
if 'git+ssh' not in urllib.parse.uses_relative:
urllib.parse.uses_relative.append('git+ssh')
2021-01-11 19:07:33 +01:00
def rm_recursive(path):
if os.path.exists(path):
shutil.rmtree(path)
2014-01-31 18:16:59 +01:00
def smart_cleaning(files_path):
now = time.time()
project_files = {}
for file_path in files_path:
project_name = os.path.basename(file_path).split('_')[0]
2020-06-15 23:26:46 +02:00
if project_name not in project_files:
project_files[project_name] = []
project_files[project_name].append(file_path)
for project in project_files.iterkeys():
nb_versions = len(project_files[project])
if nb_versions > settings.MIN_PACKAGE_VERSIONS:
project_files[project] = sorted(project_files[project], key=lambda x: os.stat(x).st_mtime)
for filename in project_files[project]:
if (
nb_versions > settings.MIN_PACKAGE_VERSIONS
2014-02-26 22:01:47 +01:00
and os.stat(filename).st_mtime < now - settings.MIN_AGE * 86400
):
os.remove(filename)
nb_versions -= 1
def clean(method):
2020-04-18 14:44:12 +02:00
print('+ Cleanning %s' % method)
if method == 'all':
shutil.rmtree(settings.ORIGIN_PATH)
shutil.rmtree(settings.PBUILDER_RESULT)
shutil.rmtree(settings.GIT_PATH)
shutil.rmtree(settings.LOCK_PATH)
elif method == 'git':
shutil.rmtree(settings.GIT_PATH)
elif method == 'deb':
shutil.rmtree(settings.PBUILDER_RESULT)
elif method == 'archives':
shutil.rmtree(settings.ORIGIN_PATH)
elif method == 'locks':
shutil.rmtree(settings.LOCK_PATH)
2014-02-26 22:01:47 +01:00
elif method == 'smart':
results_files = []
origin_files = [os.path.join(settings.ORIGIN_PATH, f) for f in os.listdir(settings.ORIGIN_PATH)]
for root, dirs, files in os.walk(settings.PBUILDER_RESULT):
2014-02-26 22:01:47 +01:00
for fname in files:
results_files.append(os.path.join(root, fname))
smart_cleaning(results_files)
smart_cleaning(origin_files)
2014-02-26 22:01:47 +01:00
now = time.time()
for root, dirs, files in os.walk(settings.PBUILDER_RESULT):
for fname in files:
fname = os.path.join(root, fname)
ext = os.path.splitext(fname)
if ext == 'build' and os.stat(fname).st_mtime < now - 365 * 86400:
2014-02-26 22:01:47 +01:00
os.remove(fname)
else:
error("Cleanning: unknow '%s' option" % method)
2014-01-31 18:16:59 +01:00
2021-01-11 19:07:33 +01:00
def get_project_infos(git_project_path, cmd_options):
"""return a dict with project informations"""
2014-02-11 19:19:23 +01:00
os.chdir(git_project_path)
results = {
'name': '',
'version': '',
2014-02-13 12:51:33 +01:00
'fullname': '',
2014-02-13 11:02:48 +01:00
'ac_version': '',
2014-02-11 19:19:23 +01:00
'build_dir': '',
'build_branch': cmd_options.branch,
2014-02-11 19:19:23 +01:00
'commit_number': '',
2014-02-13 11:02:48 +01:00
'git_path': git_project_path,
2014-02-11 19:19:23 +01:00
}
if os.path.exists('setup.py'):
# Hack to support setup_requires
setup_py('--help')
results['name'] = setup_py('--name 2> /dev/null')[:-1]
results['version'] = setup_py('--version 2> /dev/null')[:-1]
results['fullname'] = setup_py('--fullname 2> /dev/null')[:-1]
2014-02-11 19:19:23 +01:00
elif os.path.exists('configure.ac'):
call('./autogen.sh')
call('make all')
2014-02-13 11:02:48 +01:00
results['name'] = output("./configure --version | head -n1 | sed 's/ configure.*//'")[:-1]
results['ac_version'] = output("./configure --version | head -n1 | sed 's/.* configure //'")[:-1]
2014-02-13 17:11:30 +01:00
results['version'] = results['ac_version'].replace('-', '.')
2014-02-11 19:19:23 +01:00
results['fullname'] = results['name']
elif os.path.exists('Makefile'):
results['name'] = output('make name')[:-1]
results['version'] = output('make version')[:-1]
results['fullname'] = output('make fullname')[:-1]
2014-02-11 19:19:23 +01:00
else:
error('Unsupported project type', exit_code=2)
2014-02-11 19:19:23 +01:00
results['build_dir'] = os.path.join(
settings.EOBUILDER_TMP, '%s-%d' % (results['name'], random.randint(0, 1000000))
2014-02-11 19:19:23 +01:00
)
atexit.register(rm_recursive, results['build_dir'])
results['commit_number'] = output('git rev-parse HEAD')[:-1]
2014-02-13 11:02:48 +01:00
results['lock_path'] = os.path.join(settings.LOCK_PATH, results['name'])
2014-03-03 19:41:56 +01:00
current_tag = output('git describe --abbrev=0 --tags --match=v*', exit_on_error=False)
if current_tag:
results['current_tag'] = current_tag[1:-1]
2014-03-03 19:41:56 +01:00
else:
results['current_tag'] = '0.0'
2014-02-11 19:19:23 +01:00
return results
def prepare_build(dist, project, cmd_options, new):
2014-02-13 11:02:48 +01:00
"""
Create origin archive, update git and Debian changelog
"""
package = {
2014-03-31 16:29:09 +02:00
'repository': settings.DEFAULT_UNSTABLE_REPOSITORIES[dist],
'version': '',
'source_name': '',
'names': [],
}
2019-01-13 11:12:30 +01:00
if cmd_options.hotfix:
package['repository'] = settings.HOTFIX_REPOSITORIES[dist]
2014-02-13 11:02:48 +01:00
os.chdir(project['git_path'])
2014-03-31 16:29:09 +02:00
build_branch = cmd_options.branch
if cmd_options.hotfix and not build_branch.startswith('hotfix/'):
return error('Invalid name for hotfix branch (must start with hotfix/)', exit_code=2)
debian_folder = cmd_options.debian_folder
if os.path.isdir('debian-' + dist) and debian_folder == 'debian':
debian_folder = 'debian-' + dist
debian_branch = None
if not os.path.isdir(debian_folder):
debian_branch = 'debian'
debian_folder = 'debian'
branches = output('git branch -r -l')
if debian_branch == 'debian' and 'debian-%s' % dist in branches:
debian_branch = 'debian-' + dist
if not 'origin/%s' % debian_branch in output('git branch -r -l'):
2020-04-18 14:44:12 +02:00
print('!!! WARNING: cannot build for dist %s, no debian directory found' % dist)
return
2020-04-18 14:44:12 +02:00
print('!!! WARNING obsolete: using a branch for debian/ packaging')
print('+ Updating Debian branch for %s' % dist)
call('git checkout --quiet %s' % debian_branch)
call('git pull')
else:
2020-04-18 14:44:12 +02:00
print('+ Building from %s debian folder' % debian_folder)
for r in cmd_options.repositories:
repo = r.split(':')
if repo[0] == dist:
package['repository'] = repo[1]
2014-02-11 19:19:23 +01:00
# get package source name
control_file = os.path.join(debian_folder, 'control')
package['names'] = re.findall(r'Package\s*:\s*(.*?)\n', cat(control_file))
package['source_name'] = re.search(r'^Source\s*:\s*(.*?)\n', cat(control_file), re.MULTILINE).group(1)
# build tarball
origin_archive = os.path.join(
settings.ORIGIN_PATH, '%s_%s.orig.tar.bz2' % (package['source_name'], project['version'])
2021-01-11 19:07:33 +01:00
)
if not os.path.exists(origin_archive):
2020-04-18 14:44:12 +02:00
print('+ Generating origin tarball ...')
os.chdir(project['git_path'])
call('git checkout --quiet %s' % build_branch)
if os.path.exists('setup.py'):
setup_py('clean --all')
setup_py('sdist --formats=bztar')
shutil.move('dist/%s.tar.bz2' % project['fullname'], origin_archive)
elif os.path.exists('./configure.ac'):
call('make dist-bzip2')
shutil.move('%s-%s.tar.bz2' % (project['name'], project['ac_version']), origin_archive)
elif os.path.exists('Makefile'):
call('make dist-bzip2')
shutil.move('sdist/%s.tar.bz2' % project['fullname'], origin_archive)
else:
error('Unsupported project type', project['build_dir'], exit_code=2)
last_version_file = os.path.join(
project['lock_path'],
'%s_%s_%s.last_version' % (project['name'], package['repository'], build_branch.replace('/', '_')),
)
debian_changelog = os.path.join(debian_folder, 'changelog')
2014-02-11 19:19:23 +01:00
if os.path.exists(last_version_file):
last_debian_package_version = cat(last_version_file)
else:
last_debian_package_version = re.search(
r'^Version:\s(.*?)$', output('dpkg-parsechangelog -l%s' % debian_changelog), re.MULTILINE
2014-02-13 12:51:33 +01:00
).group(1)
2014-02-11 19:19:23 +01:00
last_version = last_debian_package_version.split('-')[0]
package['version'] = last_debian_package_version
2014-02-11 19:19:23 +01:00
if cmd_options.native:
debian_revision_number = ''
else:
debian_revision_number = '-1'
if last_version == project['version'] and new and '~eob' in last_debian_package_version:
new_inc = int(last_debian_package_version.rsplit('+', 1)[-1]) + 1
version_suffix = '%s~eob%s+%s' % (debian_revision_number, settings.DEBIAN_VERSIONS[dist], new_inc)
else:
version_suffix = '%s~eob%s+1' % (debian_revision_number, settings.DEBIAN_VERSIONS[dist])
call('git checkout --quiet %s' % build_branch)
changelog = '\n'.join(
changelog_from_git(
package['source_name'],
2019-02-22 14:58:58 +01:00
version_suffix,
project['git_path'],
package['repository'],
epoch=cmd_options.epoch,
)
2021-01-11 19:07:33 +01:00
)
if changelog:
if not os.path.isdir(debian_folder):
call('git checkout --quiet %s' % debian_branch)
debian_generated_changelog_filename = debian_changelog + '.generated'
with open(debian_generated_changelog_filename, 'w+') as f:
f.write(changelog)
package['version'] = re.search(
r'^Version:\s(.*?)$',
output('dpkg-parsechangelog -l%s' % debian_generated_changelog_filename),
re.MULTILINE,
).group(1)
os.unlink(debian_generated_changelog_filename)
else:
# changelog couldn't be generated, this happens with checkouts from
# dgit, at least.
package['version'] = ''
2014-02-11 19:19:23 +01:00
2014-02-13 12:51:33 +01:00
if os.path.exists(project['build_dir']):
shutil.rmtree(project['build_dir'])
2020-04-18 14:44:12 +02:00
os.makedirs(project['build_dir'], 0o755)
2019-02-22 14:58:58 +01:00
if package['version'].split('-')[0].split(':')[-1] == project['version']:
# the generated changelog has the right version number, use it.
good_changelog_contents = changelog
else:
# wrong version number, in that case we add an arbitrary new entry
# to the existing changelog
package['version'] = '%s%s' % (project['version'], version_suffix)
call(
'dch "Eobuilder version" -v %s --distribution %s \
--force-bad-version --force-distribution --changelog %s'
% (
2019-02-22 14:58:58 +01:00
'%s:%s' % (cmd_options.epoch, package['version'])
if cmd_options.epoch
else package['version'],
package['repository'],
debian_changelog,
)
2021-01-11 19:07:33 +01:00
)
good_changelog_contents = open(debian_changelog).read()
2014-02-13 12:51:33 +01:00
if cmd_options.hotfix:
version_part = build_branch.split('/', 1)[1].lstrip('v')
if not project['version'].startswith(version_part):
return error('Invalid name for hotfix branch (must start with version number)', exit_code=2)
2014-02-13 11:02:48 +01:00
build_file = os.path.join(
project['lock_path'],
'%s_%s_%s_%s.build'
% (project['name'], package['version'], package['repository'], build_branch.replace('/', '_')),
)
2014-02-11 19:19:23 +01:00
if os.path.exists(build_file):
2020-04-18 14:44:12 +02:00
print('+ Already built for %s !' % dist)
return package
2014-02-11 19:19:23 +01:00
2020-04-18 14:44:12 +02:00
print('+ Preparing Debian build (%s %s) ...' % (package['source_name'], package['version']))
if debian_branch:
call('git checkout --quiet %s' % debian_branch)
2014-02-13 11:02:48 +01:00
os.chdir(project['build_dir'])
2014-02-13 12:51:33 +01:00
project_build_path = os.path.join(project['build_dir'], '%s-%s' % (project['name'], project['version']))
2014-02-13 11:02:48 +01:00
shutil.copy(origin_archive, project['build_dir'])
tar = tarfile.open('%s_%s.orig.tar.bz2' % (package['source_name'], project['version']), 'r:bz2')
2014-02-13 11:02:48 +01:00
tar.extractall()
tar.close()
if os.path.exists('%s/debian' % project_build_path):
shutil.rmtree('%s/debian' % project_build_path)
shutil.copytree(os.path.join(project['git_path'], debian_folder), '%s/debian' % project_build_path)
with open(os.path.join(project_build_path, 'debian', 'changelog'), 'w') as f:
f.write(good_changelog_contents)
return package
2014-02-13 11:02:48 +01:00
2021-01-11 19:07:33 +01:00
def build_project(dist, arch, project, package, new):
2014-02-13 11:02:48 +01:00
pbuilder_project_result = os.path.join(settings.PBUILDER_RESULT, '%s-%s' % (dist, arch))
project_build_path = os.path.join(project['build_dir'], '%s-%s' % (project['name'], project['version']))
if not os.path.exists(pbuilder_project_result):
2020-04-18 14:44:12 +02:00
os.makedirs(pbuilder_project_result, 0o755)
2014-02-13 11:02:48 +01:00
os.chdir(project['lock_path'])
source_build = os.path.join(
project['lock_path'],
'%s_%s_%s_%s_source.build'
% (
project['name'],
project['version'],
package['repository'],
project['build_branch'].replace('/', '_'),
2021-01-11 19:07:33 +01:00
),
)
2014-02-13 11:02:48 +01:00
bin_build = os.path.join(
project['lock_path'],
'%s_%s_%s_%s_%s.build'
% (
project['name'],
package['version'],
package['repository'],
project['build_branch'].replace('/', '_'),
arch,
2021-01-11 19:07:33 +01:00
),
)
2020-04-18 14:44:12 +02:00
print('SOURCE_BUILD:', source_build)
2014-02-13 11:02:48 +01:00
if os.path.exists(source_build):
source_opt = '-b'
else:
source_opt = '-sa'
if new == 0 and os.path.exists(bin_build):
2020-04-18 14:44:12 +02:00
print('+ Already build !')
2014-02-13 11:02:48 +01:00
return
os.chdir(project_build_path)
2020-04-18 14:44:12 +02:00
print('+ Building %s %s %s %s' % (project['name'], project['version'], dist, arch))
call(
'DIST=%s ARCH=%s pdebuild --use-pdebuild-internal --architecture %s --debbuildopts "%s"'
% (dist, arch, arch, source_opt)
2021-01-11 19:07:33 +01:00
)
2014-02-13 11:02:48 +01:00
2020-04-18 14:44:12 +02:00
print('+ Lock build')
2014-02-13 11:02:48 +01:00
touch(bin_build)
if not os.path.exists(source_build):
touch(source_build)
2014-02-13 11:02:48 +01:00
2021-01-11 19:07:33 +01:00
def send_packages(dist, arch, project, package, last_tag, dput=True):
stamp_file = os.path.join(
project['lock_path'],
'%s_%s_%s_%s_%s.upload'
% (
project['name'],
package['version'],
package['repository'],
arch,
project['build_branch'].replace('/', '_'),
2021-01-11 19:07:33 +01:00
),
)
if os.path.exists(stamp_file):
2020-04-18 14:44:12 +02:00
print('+ Already uploaded')
return
pbuilder_project_result = os.path.join(settings.PBUILDER_RESULT, '%s-%s' % (dist, arch))
print('+ Updating local repository...')
subprocess.check_call(
'apt-ftparchive packages . | gzip > Packages.gz', cwd=pbuilder_project_result, shell=True
)
if dput:
print('+ Sending package...')
os.chdir(pbuilder_project_result)
call(
'dput -u %s %s_%s_%s.changes'
% (package['repository'], package['source_name'], package['version'].split(':', 1)[-1], arch)
)
else:
print('+ Package not sent to repository (--no-dput used).')
return
open(stamp_file, 'w').close()
2021-01-11 19:07:33 +01:00
def clean_git_on_exit(git_project_path):
if not os.path.exists(git_project_path):
return
os.chdir(git_project_path)
call('git stash --quiet')
changelog_tmp = os.path.join(git_project_path, 'debian', 'changelog.git')
if os.path.exists(changelog_tmp):
os.remove(changelog_tmp)
def get_git_project_name(project_reference):
project_name = os.path.basename(project_reference)
if project_name.endswith('.git'):
project_name = project_name[:-4]
return project_name
def get_git_project_path(project_reference):
return os.path.join(settings.GIT_PATH, get_git_project_name(project_reference))
def get_git_branch_name(project_reference):
git_project_path = get_git_project_path(project_reference)
for branch_name in ('main', 'master'):
try:
subprocess.check_call(
['git', 'rev-parse', branch_name],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
cwd=git_project_path,
)
except subprocess.CalledProcessError:
continue
return branch_name
else:
raise Exception('failed to determine branch')
def setup_git_tree(project_reference, options):
git_project_path = get_git_project_path(project_reference)
if options.branch and options.branch.startswith('wip/'):
2019-02-17 09:50:10 +01:00
if os.path.exists(git_project_path):
shutil.rmtree(git_project_path)
if os.path.exists(git_project_path):
existing_tree = True
branch_name = options.branch or get_git_branch_name(project_reference)
2019-02-17 09:50:10 +01:00
try:
subprocess.check_call(['git', 'fetch'], cwd=git_project_path)
subprocess.check_call(['git', 'checkout', '--quiet', branch_name], cwd=git_project_path)
subprocess.check_call(['git', 'reset', '--hard', 'origin/%s' % branch_name], cwd=git_project_path)
2019-02-17 09:50:10 +01:00
except subprocess.CalledProcessError as e:
2020-04-18 14:44:12 +02:00
print(e, file=sys.stderr)
2019-02-17 09:50:10 +01:00
shutil.rmtree(git_project_path)
return setup_git_tree(project_reference, options)
2019-02-17 09:50:10 +01:00
else:
existing_tree = False
os.chdir(settings.GIT_PATH)
if project_reference.startswith('/'):
call('git clone %s' % project_reference)
2019-02-17 09:50:10 +01:00
else:
parsed = urllib.parse.urlparse(project_reference)
if not parsed.netloc:
project_url = urllib.parse.urljoin(settings.GIT_REPOSITORY_URL, project_reference) + '.git'
else:
project_url = project_reference
call('git clone %s' % project_url)
if options.branch:
subprocess.check_call(['git', 'checkout', '--quiet', options.branch], cwd=git_project_path)
branch_name = get_git_branch_name(project_reference)
if not options.branch:
options.branch = branch_name
2019-02-17 09:50:10 +01:00
os.chdir(git_project_path)
try:
subprocess.check_call(['git', 'checkout', '--quiet', branch_name])
2019-02-17 09:50:10 +01:00
subprocess.check_call(['git', 'pull'])
subprocess.check_call(['git', 'submodule', 'init'])
subprocess.check_call(['git', 'submodule', 'update'])
except subprocess.CalledProcessError as e:
if existing_tree:
2020-04-18 14:44:12 +02:00
print(e, file=sys.stderr)
2019-02-17 09:50:10 +01:00
shutil.rmtree(git_project_path)
return setup_git_tree(project_reference, options)
2019-02-17 09:50:10 +01:00
raise
2014-01-31 18:16:59 +01:00
def main():
options, args = parse_cmdline()
2015-09-11 10:08:45 +02:00
for method in options.cleaning:
clean(method)
2015-09-11 10:08:45 +02:00
if options.cleaning:
sys.exit(0)
init()
project_reference = args[0]
git_project_path = get_git_project_path(project_reference)
atexit.register(clean_git_on_exit, git_project_path)
if options.branch and options.branch.startswith('origin/'):
# normalize without origin/
options.branch = options.branch[len('origin/') :]
2019-02-17 09:50:10 +01:00
existing_tree = os.path.exists(git_project_path)
if existing_tree:
os.chdir(git_project_path)
last_tag = output('git describe --abbrev=0 --tags --match=v*', exit_on_error=False)
if last_tag:
last_tag = last_tag[1:-1]
2014-03-03 19:41:56 +01:00
else:
last_tag = '0.0'
else:
2014-03-03 19:41:56 +01:00
last_tag = '0.0'
setup_git_tree(project_reference, options)
project = get_project_infos(git_project_path, options)
2014-02-11 19:19:23 +01:00
2014-02-13 11:02:48 +01:00
if not os.path.exists(project['lock_path']):
2020-04-18 14:44:12 +02:00
os.mkdir(project['lock_path'], 0o755)
# compare revision between last build and now to determine if something is really new
new = 1
current_revision = output('git rev-parse HEAD', True).strip()
branch_name = get_git_branch_name(project_reference)
last_branch_revision_file_path = os.path.join(
project['lock_path'], '%s_%s.last_revision' % (project['name'], branch_name.replace('/', '_'))
2021-01-11 19:07:33 +01:00
)
try:
with open(last_branch_revision_file_path) as f:
last_branch_revision = f.read().strip()
except IOError:
pass
else:
if current_revision == last_branch_revision:
new = 0
if options.force and not new:
2020-04-18 14:44:12 +02:00
print('+ Warning force a new build')
new = 1
2014-02-11 19:19:23 +01:00
for dist in options.distrib:
os.chdir(git_project_path)
call('git checkout --quiet %s' % branch_name)
package = prepare_build(dist, project, options, new)
if package:
2014-02-13 11:02:48 +01:00
for arch in options.architectures:
build_project(dist, arch, project, package, new)
send_packages(dist, arch, project, package, last_tag, dput=options.dput)
2020-04-18 14:44:12 +02:00
print('+ Add a build file to lock new build for %s' % dist)
2014-02-13 11:02:48 +01:00
touch(
os.path.join(
project['lock_path'],
'%s_%s_%s_%s.build'
% (
project['name'],
package['version'],
package['repository'],
branch_name.replace('/', '_'),
2021-01-11 19:07:33 +01:00
),
2014-02-13 11:02:48 +01:00
)
2021-01-11 19:07:33 +01:00
)
2014-02-13 11:02:48 +01:00
last_version_file = os.path.join(
project['lock_path'],
'%s_%s_%s.last_version'
% (project['name'], package['repository'], branch_name.replace('/', '_')),
)
with open(last_version_file, 'w+') as f:
f.write(package['version'])
2014-02-13 11:02:48 +01:00
# keep current revision for next build
with open(last_branch_revision_file_path, 'w+') as f:
f.write(current_revision)
2021-01-11 19:07:33 +01:00
2014-01-31 18:16:59 +01:00
if __name__ == '__main__':
main()