mirror of
https://github.com/f-droid/fdroidserver.git
synced 2025-09-16 16:02:33 +03:00
Separate the VM setup process from the build
This commit is contained in:
parent
d7cee19c02
commit
6db75545ef
1 changed files with 84 additions and 51 deletions
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# build.py - part of the FDroid server tools
|
# build.py - part of the FDroid server tools
|
||||||
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
|
# Copyright (C) 2010-2014, Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
|
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -35,6 +35,10 @@ import common
|
||||||
import metadata
|
import metadata
|
||||||
from common import BuildException, VCSException, FDroidPopen, SilentPopen
|
from common import BuildException, VCSException, FDroidPopen, SilentPopen
|
||||||
|
|
||||||
|
try:
|
||||||
|
import paramiko
|
||||||
|
except:
|
||||||
|
paramiko = None
|
||||||
|
|
||||||
def get_builder_vm_id():
|
def get_builder_vm_id():
|
||||||
vd = os.path.join('builder', '.vagrant')
|
vd = os.path.join('builder', '.vagrant')
|
||||||
|
@ -70,7 +74,7 @@ def got_valid_builder_vm():
|
||||||
|
|
||||||
|
|
||||||
def vagrant(params, cwd=None, printout=False):
|
def vagrant(params, cwd=None, printout=False):
|
||||||
"""Run vagrant.
|
"""Run a vagrant command.
|
||||||
|
|
||||||
:param: list of parameters to pass to vagrant
|
:param: list of parameters to pass to vagrant
|
||||||
:cwd: directory to run in, or None for current directory
|
:cwd: directory to run in, or None for current directory
|
||||||
|
@ -81,19 +85,48 @@ def vagrant(params, cwd=None, printout=False):
|
||||||
return (p.returncode, p.stdout)
|
return (p.returncode, p.stdout)
|
||||||
|
|
||||||
|
|
||||||
# Note that 'force' here also implies test mode.
|
def get_vagrant_sshinfo():
|
||||||
def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
"""Get ssh connection info for a vagrant VM
|
||||||
"""Do a build on the build server."""
|
|
||||||
|
|
||||||
import paramiko
|
:returns: A dictionary containing 'hostname', 'port', 'user'
|
||||||
if options.verbose:
|
and 'idfile'
|
||||||
logging.getLogger("paramiko").setLevel(logging.DEBUG)
|
"""
|
||||||
else:
|
if subprocess.call('vagrant ssh-config >sshconfig',
|
||||||
logging.getLogger("paramiko").setLevel(logging.WARN)
|
cwd='builder', shell=True) != 0:
|
||||||
|
raise BuildException("Error getting ssh config")
|
||||||
|
vagranthost = 'default' # Host in ssh config file
|
||||||
|
sshconfig = paramiko.SSHConfig()
|
||||||
|
sshf = open('builder/sshconfig', 'r')
|
||||||
|
sshconfig.parse(sshf)
|
||||||
|
sshf.close()
|
||||||
|
sshconfig = sshconfig.lookup(vagranthost)
|
||||||
|
idfile = sshconfig['identityfile']
|
||||||
|
if isinstance(idfile, list):
|
||||||
|
idfile = idfile[0]
|
||||||
|
elif idfile.startswith('"') and idfile.endswith('"'):
|
||||||
|
idfile = idfile[1:-1]
|
||||||
|
return {'hostname': sshconfig['hostname'],
|
||||||
|
'port': int(sshconfig['port']),
|
||||||
|
'user': sshconfig['user'],
|
||||||
|
'idfile': idfile}
|
||||||
|
|
||||||
|
|
||||||
|
def get_clean_vm(reset=False):
|
||||||
|
"""Get a clean VM ready to do a buildserver build.
|
||||||
|
|
||||||
|
This might involve creating and starting a new virtual machine from
|
||||||
|
scratch, or it might be as simple (unless overridden by the reset
|
||||||
|
parameter) as re-using a snapshot created previously.
|
||||||
|
|
||||||
|
A BuildException will be raised if anything goes wrong.
|
||||||
|
|
||||||
|
:reset: True to force creating from scratch.
|
||||||
|
:returns: A dictionary containing 'hostname', 'port', 'user'
|
||||||
|
and 'idfile'
|
||||||
|
"""
|
||||||
# Reset existing builder machine to a clean state if possible.
|
# Reset existing builder machine to a clean state if possible.
|
||||||
vm_ok = False
|
vm_ok = False
|
||||||
if not options.resetserver:
|
if not reset:
|
||||||
logging.info("Checking for valid existing build server")
|
logging.info("Checking for valid existing build server")
|
||||||
|
|
||||||
if got_valid_builder_vm():
|
if got_valid_builder_vm():
|
||||||
|
@ -122,6 +155,7 @@ def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
raise BuildException("Failed to start build server")
|
raise BuildException("Failed to start build server")
|
||||||
logging.info("...waiting a sec...")
|
logging.info("...waiting a sec...")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
sshinfo = get_vagrant_sshinfo()
|
||||||
vm_ok = True
|
vm_ok = True
|
||||||
else:
|
else:
|
||||||
logging.info("...failed to reset to snapshot")
|
logging.info("...failed to reset to snapshot")
|
||||||
|
@ -159,26 +193,13 @@ def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
|
|
||||||
# Open SSH connection to make sure it's working and ready...
|
# Open SSH connection to make sure it's working and ready...
|
||||||
logging.info("Connecting to virtual machine...")
|
logging.info("Connecting to virtual machine...")
|
||||||
if subprocess.call('vagrant ssh-config >sshconfig',
|
sshinfo = get_vagrant_sshinfo()
|
||||||
cwd='builder', shell=True) != 0:
|
|
||||||
raise BuildException("Error getting ssh config")
|
|
||||||
vagranthost = 'default' # Host in ssh config file
|
|
||||||
sshconfig = paramiko.SSHConfig()
|
|
||||||
sshf = open('builder/sshconfig', 'r')
|
|
||||||
sshconfig.parse(sshf)
|
|
||||||
sshf.close()
|
|
||||||
sshconfig = sshconfig.lookup(vagranthost)
|
|
||||||
sshs = paramiko.SSHClient()
|
sshs = paramiko.SSHClient()
|
||||||
sshs.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
sshs.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
idfile = sshconfig['identityfile']
|
sshs.connect(sshinfo['hostname'], username=sshinfo['user'],
|
||||||
if isinstance(idfile, list):
|
port=sshinfo['port'], timeout=300,
|
||||||
idfile = idfile[0]
|
|
||||||
elif idfile.startswith('"') and idfile.endswith('"'):
|
|
||||||
idfile = idfile[1:-1]
|
|
||||||
sshs.connect(sshconfig['hostname'], username=sshconfig['user'],
|
|
||||||
port=int(sshconfig['port']), timeout=300,
|
|
||||||
look_for_keys=False,
|
look_for_keys=False,
|
||||||
key_filename=idfile)
|
key_filename=sshinfo['idfile'])
|
||||||
sshs.close()
|
sshs.close()
|
||||||
|
|
||||||
logging.info("Saving clean state of new build server")
|
logging.info("Saving clean state of new build server")
|
||||||
|
@ -207,31 +228,40 @@ def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
if 'fdroidclean' not in p.stdout:
|
if 'fdroidclean' not in p.stdout:
|
||||||
raise BuildException("Failed to take snapshot.")
|
raise BuildException("Failed to take snapshot.")
|
||||||
|
|
||||||
|
return sshinfo
|
||||||
|
|
||||||
|
|
||||||
|
def release_vm():
|
||||||
|
"""Release the VM previously started with get_clean_vm().
|
||||||
|
|
||||||
|
This should always be called.
|
||||||
|
"""
|
||||||
|
logging.info("Suspending build server")
|
||||||
|
subprocess.call(['vagrant', 'suspend'], cwd='builder')
|
||||||
|
|
||||||
|
|
||||||
|
# Note that 'force' here also implies test mode.
|
||||||
|
def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
|
"""Do a build on the build server."""
|
||||||
|
|
||||||
|
if not paramiko:
|
||||||
|
raise BuildException("Paramiko is required to use the buildserver")
|
||||||
|
if options.verbose:
|
||||||
|
logging.getLogger("paramiko").setLevel(logging.DEBUG)
|
||||||
|
else:
|
||||||
|
logging.getLogger("paramiko").setLevel(logging.WARN)
|
||||||
|
|
||||||
|
sshinfo = get_clean_vm()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Get SSH configuration settings for us to connect...
|
|
||||||
logging.info("Getting ssh configuration...")
|
|
||||||
subprocess.call('vagrant ssh-config >sshconfig',
|
|
||||||
cwd='builder', shell=True)
|
|
||||||
vagranthost = 'default' # Host in ssh config file
|
|
||||||
|
|
||||||
# Load and parse the SSH config...
|
|
||||||
sshconfig = paramiko.SSHConfig()
|
|
||||||
sshf = open('builder/sshconfig', 'r')
|
|
||||||
sshconfig.parse(sshf)
|
|
||||||
sshf.close()
|
|
||||||
sshconfig = sshconfig.lookup(vagranthost)
|
|
||||||
|
|
||||||
# Open SSH connection...
|
# Open SSH connection...
|
||||||
logging.info("Connecting to virtual machine...")
|
logging.info("Connecting to virtual machine...")
|
||||||
sshs = paramiko.SSHClient()
|
sshs = paramiko.SSHClient()
|
||||||
sshs.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
sshs.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
idfile = sshconfig['identityfile'][0]
|
sshs.connect(sshinfo['hostname'], username=sshinfo['user'],
|
||||||
if idfile.startswith('"') and idfile.endswith('"'):
|
port=sshinfo['port'], timeout=300,
|
||||||
idfile = idfile[1:-1]
|
look_for_keys=False, key_filename=sshinfo['idfile'])
|
||||||
sshs.connect(sshconfig['hostname'], username=sshconfig['user'],
|
|
||||||
port=int(sshconfig['port']), timeout=300,
|
|
||||||
look_for_keys=False, key_filename=idfile)
|
|
||||||
|
|
||||||
# Get an SFTP connection...
|
# Get an SFTP connection...
|
||||||
ftp = sshs.open_sftp()
|
ftp = sshs.open_sftp()
|
||||||
|
@ -362,7 +392,9 @@ def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
break
|
break
|
||||||
output += get
|
output += get
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
raise BuildException("Build.py failed on server for %s:%s" % (app['id'], thisbuild['version']), output)
|
raise BuildException(
|
||||||
|
"Build.py failed on server for {0}:{1}".format(
|
||||||
|
app['id'], thisbuild['version']), output)
|
||||||
|
|
||||||
# Retrieve the built files...
|
# Retrieve the built files...
|
||||||
logging.info("Retrieving build output...")
|
logging.info("Retrieving build output...")
|
||||||
|
@ -377,14 +409,15 @@ def build_server(app, thisbuild, vcs, build_dir, output_dir, force):
|
||||||
if not options.notarball:
|
if not options.notarball:
|
||||||
ftp.get(tarball, os.path.join(output_dir, tarball))
|
ftp.get(tarball, os.path.join(output_dir, tarball))
|
||||||
except:
|
except:
|
||||||
raise BuildException("Build failed for %s:%s - missing output files" % (app['id'], thisbuild['version']), output)
|
raise BuildException(
|
||||||
|
"Build failed for %s:%s - missing output files".format(
|
||||||
|
app['id'], thisbuild['version']), output)
|
||||||
ftp.close()
|
ftp.close()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
||||||
# Suspend the build server.
|
# Suspend the build server.
|
||||||
logging.info("Suspending build server")
|
release_vm()
|
||||||
subprocess.call(['vagrant', 'suspend'], cwd='builder')
|
|
||||||
|
|
||||||
|
|
||||||
def adapt_gradle(build_dir):
|
def adapt_gradle(build_dir):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue