diff options
-rw-r--r-- | README.txt | 8 | ||||
-rw-r--r-- | buildbot_gentoo_ci/__init__.py | 0 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/__init__.py | 0 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/builders.py | 9 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/buildfactory.py | 8 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/config.py | 135 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/schedulers.py | 56 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/workers.py | 8 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/__init__.py | 0 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/connector.py | 98 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/migrate/README | 4 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/migrate/migrate.cfg | 20 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/migrate/versions/__init__.py | 0 | ||||
-rw-r--r-- | buildbot_gentoo_ci/db/model.py | 352 | ||||
-rw-r--r-- | buildbot_gentoo_ci/scripts/update_db.py | 110 | ||||
-rw-r--r-- | gentooci.cfg | 17 | ||||
-rw-r--r-- | licenses/GPL-2 | 339 | ||||
-rw-r--r-- | master.cfg | 80 |
18 files changed, 1244 insertions, 0 deletions
diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1e08eb1 --- /dev/null +++ b/README.txt @@ -0,0 +1,8 @@ +============================= +Gentoo Ci Build System +============================= + +This is a POC +We use Buildbot to build and test packages + +https://wiki.gentoo.org/wiki/Project:Tinderbox-cluster diff --git a/buildbot_gentoo_ci/__init__.py b/buildbot_gentoo_ci/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/buildbot_gentoo_ci/__init__.py diff --git a/buildbot_gentoo_ci/config/__init__.py b/buildbot_gentoo_ci/config/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/buildbot_gentoo_ci/config/__init__.py diff --git a/buildbot_gentoo_ci/config/builders.py b/buildbot_gentoo_ci/config/builders.py new file mode 100644 index 0000000..b663fba --- /dev/null +++ b/buildbot_gentoo_ci/config/builders.py @@ -0,0 +1,9 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +from buildbot.plugins import util +from buildbot_gentoo_ci.config import buildfactory + +def gentoo_builders(b=[]): + b.append(util.BuilderConfig(name='update_db_packages', workername='updatedb_1', factory=buildfactory.f_update_db_packages())) + return b diff --git a/buildbot_gentoo_ci/config/buildfactory.py b/buildbot_gentoo_ci/config/buildfactory.py new file mode 100644 index 0000000..0943031 --- /dev/null +++ b/buildbot_gentoo_ci/config/buildfactory.py @@ -0,0 +1,8 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +from buildbot.plugins import util + +def f_update_db_packages(): + f = util.BuildFactory() + return f diff --git a/buildbot_gentoo_ci/config/config.py b/buildbot_gentoo_ci/config/config.py new file mode 100644 index 0000000..3ad8595 --- /dev/null +++ b/buildbot_gentoo_ci/config/config.py @@ -0,0 +1,135 @@ +# This file has parts from Buildbot and is modifyed by Gentoo Authors. +# Buildbot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members +# Origins: buildbot.config.py +# Modifyed by Gentoo Authors. +# Copyright 2020 Gentoo Authors + +import datetime +import inspect +import os +import re +import sys +import traceback +import warnings +from types import MethodType + +from twisted.python import failure +from twisted.python import log +from twisted.python.compat import execfile +from zope.interface import implementer + +from buildbot import interfaces +from buildbot import locks +from buildbot import util +from buildbot.interfaces import IRenderable +from buildbot.revlinks import default_revlink_matcher +from buildbot.util import ComparableMixin +from buildbot.util import bytes2unicode +from buildbot.util import config as util_config +from buildbot.util import identifiers as util_identifiers +from buildbot.util import safeTranslate +from buildbot.util import service as util_service +from buildbot.warnings import ConfigWarning +from buildbot.warnings import warn_deprecated +from buildbot.config import ConfigErrors, error, loadConfigDict + +_errors = None + +DEFAULT_DB_URL = 'sqlite:///gentoo.sqlite' + +#Use GentooCiConfig.loadFromDict +@implementer(interfaces.IConfigLoader) +class FileLoader(ComparableMixin): + compare_attrs = ['basedir', 'configFileName'] + + def __init__(self, basedir, configFileName): + self.basedir = basedir + self.configFileName = configFileName + + def loadConfig(self): + # from here on out we can batch errors together for the user's + # convenience + global _errors + _errors = errors = ConfigErrors() + + try: + filename, config_dict = loadConfigDict( + self.basedir, self.configFileName) + config = GentooCiConfig.loadFromDict(config_dict, filename) + except ConfigErrors as e: + errors.merge(e) + finally: + _errors = None + + if errors: + raise errors + + return config + +# Modifyed for Gentoo Ci settings +class GentooCiConfig(util.ComparableMixin): + + def __init__(self): + self.db = dict( + db_url=DEFAULT_DB_URL, + ) + + _known_config_keys = set([ + "db_url", + ]) + + compare_attrs = list(_known_config_keys) + + @classmethod + def loadFromDict(cls, config_dict, filename): + # warning, all of this is loaded from a thread + global _errors + _errors = errors = ConfigErrors() + + # check for unknown keys + unknown_keys = set(config_dict.keys()) - cls._known_config_keys + if unknown_keys: + if len(unknown_keys) == 1: + error('Unknown BuildmasterConfig key {}'.format(unknown_keys.pop())) + else: + error('Unknown BuildmasterConfig keys {}'.format(', '.join(sorted(unknown_keys)))) + + # instantiate a new config object, which will apply defaults + # automatically + config = cls() + # and defer the rest to sub-functions, for code clarity + try: + config.load_db(config_dict) + finally: + _errors = None + + if errors: + raise errors + + return config + + @staticmethod + def getDbUrlFromConfig(config_dict, throwErrors=True): + + # we don't attempt to parse db URLs here - the engine strategy will do + # so. + if 'db_url' in config_dict: + return config_dict['db_url'] + + return DEFAULT_DB_URL + + def load_db(self, config_dict): + self.db = dict(db_url=self.getDbUrlFromConfig(config_dict)) diff --git a/buildbot_gentoo_ci/config/schedulers.py b/buildbot_gentoo_ci/config/schedulers.py new file mode 100644 index 0000000..f5b19da --- /dev/null +++ b/buildbot_gentoo_ci/config/schedulers.py @@ -0,0 +1,56 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +from buildbot.plugins import schedulers, util + +@util.renderer +def builderUpdateDbNames(self, props): + builders = set() + for f in props.files: + if f.endswith('.ebuild'): + builders.add('update_db_packages') + return list(builders) + +@util.renderer +def cpvUpdateDb(props): + cpv_changes = [] + for f in props.files: + if f.endswith('.ebuild'): + cppv = f.split('.eb', 0) + cpv = cppv.split('/', 0) + '/' + cppv.split('/', 2) + if not cpv in cpv_changes: + cpv_changes.append(cpv) + return cpv_changes + +def gentoo_schedulers(): + scheduler_update_db = schedulers.SingleBranchScheduler( + name='scheduler_update_db', + treeStableTimer=60, + properties = { + 'cpv_changes' : cpvUpdateDb, + }, + builderNames = builderUpdateDbNames, + change_filter=util.ChangeFilter(branch='master'), + ) + test_updatedb = schedulers.ForceScheduler( + name="force", + buttonName="pushMe!", + label="My nice Force form", + builderNames=['update_db_packages'], + # A completely customized property list. The name of the + # property is the name of the parameter + properties=[ + util.NestedParameter(name="options", label="Build Options", + layout="vertical", fields=[ + util.StringParameter(name="cpv_changes", + label="Package to check", + default="dev-lang/python-3.8", size=80), + util.StringParameter(name="repository", + label="repo", + default="gentoo", size=80), + ]) + ]) + s = [] + s.append(test_updatedb) + #s.append(scheduler_update_db) + return s diff --git a/buildbot_gentoo_ci/config/workers.py b/buildbot_gentoo_ci/config/workers.py new file mode 100644 index 0000000..50a4751 --- /dev/null +++ b/buildbot_gentoo_ci/config/workers.py @@ -0,0 +1,8 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +from buildbot.plugins import worker + +def gentoo_workers(w=[]): + w.append(worker.LocalWorker('updatedb_1')) + return w diff --git a/buildbot_gentoo_ci/db/__init__.py b/buildbot_gentoo_ci/db/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/buildbot_gentoo_ci/db/__init__.py diff --git a/buildbot_gentoo_ci/db/connector.py b/buildbot_gentoo_ci/db/connector.py new file mode 100644 index 0000000..682e72a --- /dev/null +++ b/buildbot_gentoo_ci/db/connector.py @@ -0,0 +1,98 @@ +# This file has parts from Buildbot and is modifyed by Gentoo Authors. +# Buildbot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members +# Origins: buildbot.db.connector.py +# Modifyed by Gentoo Authors. +# Copyright 2020 Gentoo Authors + + +import textwrap + +from twisted.application import internet +from twisted.internet import defer +from twisted.python import log + +from buildbot import util +from buildbot.db import enginestrategy +from buildbot.db import exceptions +from buildbot.db import pool +from buildbot.util import service + +from buildbot_gentoo_ci.db import model + +upgrade_message = textwrap.dedent("""\ + + The Buildmaster database needs to be upgraded before this version of + buildbot can run. Use the following command-line + + buildbot upgrade-master {basedir} + + to upgrade the database, and try starting the buildmaster again. You may + want to make a backup of your buildmaster before doing so. + """).strip() + +# Gentoo Ci tables and ConnectorComponent +class DBConnector(service.ReconfigurableServiceMixin, + service.AsyncMultiService): + # The connection between Buildbot and its backend database. This is + # generally accessible as master.db, but is also used during upgrades. + # + # Most of the interesting operations available via the connector are + # implemented in connector components, available as attributes of this + # object, and listed below. + + # Period, in seconds, of the cleanup task. This master will perform + # periodic cleanup actions on this schedule. + CLEANUP_PERIOD = 3600 + + def __init__(self, basedir): + super().__init__() + self.setName('db') + self.basedir = basedir + + # set up components + self._engine = None # set up in reconfigService + self.pool = None # set up in reconfigService + + @defer.inlineCallbacks + def setServiceParent(self, p): + yield super().setServiceParent(p) + self.model = model.Model(self) + + @defer.inlineCallbacks + def setup(self, config, check_version=True, verbose=True): + db_url = config.db['db_url'] + + log.msg("Setting up database with URL %r" + % util.stripUrlPassword(db_url)) + + # set up the engine and pool + self._engine = enginestrategy.create_engine(db_url, + basedir=self.basedir) + self.pool = pool.DBThreadPool( + self._engine, reactor=self.master.reactor, verbose=verbose) + + # make sure the db is up to date, unless specifically asked not to + if check_version: + if db_url == 'sqlite://': + # Using in-memory database. Since it is reset after each process + # restart, `buildbot upgrade-master` cannot be used (data is not + # persistent). Upgrade model here to allow startup to continue. + self.model.upgrade() + current = yield self.model.is_current() + if not current: + for l in upgrade_message.format(basedir=self.basedir).split('\n'): + log.msg(l) + raise exceptions.DatabaseNotReadyError() diff --git a/buildbot_gentoo_ci/db/migrate/README b/buildbot_gentoo_ci/db/migrate/README new file mode 100644 index 0000000..c5f51f2 --- /dev/null +++ b/buildbot_gentoo_ci/db/migrate/README @@ -0,0 +1,4 @@ +This is a database migration repository. + +More information at +https://sqlalchemy-migrate.readthedocs.io/en/latest/ diff --git a/buildbot_gentoo_ci/db/migrate/migrate.cfg b/buildbot_gentoo_ci/db/migrate/migrate.cfg new file mode 100644 index 0000000..8be171d --- /dev/null +++ b/buildbot_gentoo_ci/db/migrate/migrate.cfg @@ -0,0 +1,20 @@ +[db_settings] +# Used to identify which repository this database is versioned under. +# You can use the name of your project. +repository_id=GentooCi + +# The name of the database table used to track the schema version. +# This name shouldn't already be used by your project. +# If this is changed once a database is under version control, you'll need to +# change the table name in each database too. +version_table=migrate_version + +# When committing a change script, Migrate will attempt to generate the +# sql for all supported databases; normally, if one of them fails - probably +# because you don't have that database installed - it is ignored and the +# commit continues, perhaps ending successfully. +# Databases in this list MUST compile successfully during a commit, or the +# entire commit will fail. List the databases your application will actually +# be using to ensure your updates to that database work properly. +# This must be a list; example: ['postgres','sqlite'] +required_dbs=[] diff --git a/buildbot_gentoo_ci/db/migrate/versions/__init__.py b/buildbot_gentoo_ci/db/migrate/versions/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/buildbot_gentoo_ci/db/migrate/versions/__init__.py diff --git a/buildbot_gentoo_ci/db/model.py b/buildbot_gentoo_ci/db/model.py new file mode 100644 index 0000000..8865517 --- /dev/null +++ b/buildbot_gentoo_ci/db/model.py @@ -0,0 +1,352 @@ +# This file has parts from Buildbot and is modifyed by Gentoo Authors. +# Buildbot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members +# Origins: buildbot.db.model.py +# Modifyed by Gentoo Authors. +# Copyright 2020 Gentoo Authors + +import uuid +import migrate +import migrate.versioning.repository +import sqlalchemy as sa +from migrate import exceptions # pylint: disable=ungrouped-imports + +from twisted.internet import defer +from twisted.python import log +from twisted.python import util + +from buildbot.db import base +from buildbot.db.migrate_utils import test_unicode +from buildbot.util import sautils + +try: + from migrate.versioning.schema import ControlledSchema # pylint: disable=ungrouped-imports +except ImportError: + ControlledSchema = None + + +class Model(base.DBConnectorComponent): + # + # schema + # + + metadata = sa.MetaData() + + # NOTES + + # * server_defaults here are included to match those added by the migration + # scripts, but they should not be depended on - all code accessing these + # tables should supply default values as necessary. The defaults are + # required during migration when adding non-nullable columns to existing + # tables. + # + # * dates are stored as unix timestamps (UTC-ish epoch time) + # + # * sqlalchemy does not handle sa.Boolean very well on MySQL or Postgres; + # use sa.SmallInteger instead + + # Tables related to gentoo-ci-cloud + # ------------------------- + + repositorys = sautils.Table( + "repositorys", metadata, + # unique id per repository + sa.Column('uuid', sa.String(36), primary_key=True, + default=lambda: str(uuid.uuid4()), + ), + # repository's name + sa.Column('name', sa.String(255), nullable=False), + # description of the repository + sa.Column('description', sa.Text, nullable=True), + sa.Column('mirror_url', sa.String(255), nullable=True), + sa.Column('auto', sa.Boolean, default=False), + sa.Column('enabled', sa.Boolean, default=False), + sa.Column('ebuild', sa.Boolean, default=False), + ) + + # Use by GitPoller + repository_gitpuller = sautils.Table( + "repository_gitpuller", metadata, + # unique id per repository + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('repository_uuid', sa.String(36), + sa.ForeignKey('repositorys.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('project', sa.String(255), nullable=False, default='gentoo'), + sa.Column('url', sa.String(255), nullable=False), + sa.Column('branche', sa.String(255), nullable=False, default='master'), + ) + + projects = sautils.Table( + "projects", metadata, + # unique id per project + sa.Column('uuid', sa.String(36), primary_key=True, + default=lambda: str(uuid.uuid4()), + ), + # project's name + sa.Column('name', sa.String(255), nullable=False), + # description of the project + sa.Column('description', sa.Text, nullable=True), + sa.Column('profile', sa.String(255), nullable=False), + sa.Column('portage_repository_uuid', sa.Integer, + sa.ForeignKey('repositorys.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('keyword_id', sa.Integer, + sa.ForeignKey('keywords.id', ondelete='CASCADE'), + nullable=False), + sa.Column('unstable', sa.Boolean, default=False), + sa.Column('auto', sa.Boolean, default=False), + sa.Column('enabled', sa.Boolean, default=False), + sa.Column('created_by', sa.Integer, + sa.ForeignKey('users.uid', ondelete='CASCADE'), + nullable=False), + ) + + # What repository's use by projects + projects_repositorys = sautils.Table( + "projects_repositorys", metadata, + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('projects_uuid', sa.String(36), + sa.ForeignKey('projects.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('repository_uuid', sa.String(36), + sa.ForeignKey('repositorys.uuid', ondelete='CASCADE'), + nullable=False), + ) + keywords = sautils.Table( + "keywords", metadata, + # unique id per project + sa.Column('id', sa.Integer, primary_key=True), + # project's name + sa.Column('keyword', sa.String(255), nullable=False), + ) + + categorys = sautils.Table( + "categories", metadata, + sa.Column('uuid', sa.String(36), primary_key=True, + default=lambda: str(uuid.uuid4()) + ), + sa.Column('name', sa.String(255), nullable=False), + ) + + packages = sautils.Table( + "packages", metadata, + sa.Column('uuid', sa.String(36), primary_key=True, + default=lambda: str(uuid.uuid4()), + ), + sa.Column('name', sa.String(255), nullable=False), + sa.Column('category_uuid', sa.String(36), + sa.ForeignKey('categories.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('repository_uuid', sa.String(36), + sa.ForeignKey('repositorys.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('deleted', sa.Boolean, default=False), + sa.Column('deleted_at', sa.Integer, nullable=True), + ) + + ebuilds = sautils.Table( + "ebuilds", metadata, + sa.Column('uuid', sa.String(36), primary_key=True, + default=lambda: str(uuid.uuid4()), + ), + sa.Column('name', sa.String(255), nullable=False), + sa.Column('package_uuid', sa.String(36), + sa.ForeignKey('packages.uuid', ondelete='CASCADE'), + nullable=False), + sa.Column('ebuild_hash', sa.String(255), nullable=False), + sa.Column('deleted', sa.Boolean, default=False), + sa.Column('deleted_at', sa.Integer, nullable=True), + ) + + ebuildkeywords = sautils.Table( + "ebuildkeywords", metadata, + # unique id per project + sa.Column('id', sa.Integer, primary_key=True), + # project's name + sa.Column('keyword_id', sa.Integer, + sa.ForeignKey('keywords.id', ondelete='CASCADE')), + sa.Column('ebuild_uuid', sa.String(36), + sa.ForeignKey('ebuilds.uuid', ondelete='CASCADE')), + sa.Column('status', sa.String(255), nullable=False), + ) + + # Tables related to users + # ----------------------- + + # This table identifies individual users, and contains buildbot-specific + # information about those users. + users = sautils.Table( + "users", metadata, + # unique user id number + sa.Column("uid", sa.Integer, primary_key=True), + + # identifier (nickname) for this user; used for display + sa.Column("identifier", sa.String(255), nullable=False), + + # username portion of user credentials for authentication + sa.Column("bb_username", sa.String(128)), + + # password portion of user credentials for authentication + sa.Column("bb_password", sa.String(128)), + ) + + # Indexes + # ------- + + + + # MySQL creates indexes for foreign keys, and these appear in the + # reflection. This is a list of (table, index) names that should be + # expected on this platform + + implied_indexes = [ + ] + + # Migration support + # ----------------- + + # this is a bit more complicated than might be expected because the first + # seven database versions were once implemented using a homespun migration + # system, and we need to support upgrading masters from that system. The + # old system used a 'version' table, where SQLAlchemy-Migrate uses + # 'migrate_version' + + repo_path = util.sibpath(__file__, "migrate") + + @defer.inlineCallbacks + def is_current(self): + if ControlledSchema is None: + # this should have been caught earlier by enginestrategy.py with a + # nicer error message + raise ImportError("SQLAlchemy/SQLAlchemy-Migrate version conflict") + + def thd(engine): + # we don't even have to look at the old version table - if there's + # no migrate_version, then we're not up to date. + repo = migrate.versioning.repository.Repository(self.repo_path) + repo_version = repo.latest + try: + # migrate.api doesn't let us hand in an engine + schema = ControlledSchema(engine, self.repo_path) + db_version = schema.version + except exceptions.DatabaseNotControlledError: + return False + + return db_version == repo_version + ret = yield self.db.pool.do_with_engine(thd) + return ret + + # returns a Deferred that returns None + def create(self): + # this is nice and simple, but used only for tests + def thd(engine): + self.metadata.create_all(bind=engine) + return self.db.pool.do_with_engine(thd) + + @defer.inlineCallbacks + def upgrade(self): + + # here, things are a little tricky. If we have a 'version' table, then + # we need to version_control the database with the proper version + # number, drop 'version', and then upgrade. If we have no 'version' + # table and no 'migrate_version' table, then we need to version_control + # the database. Otherwise, we just need to upgrade it. + + def table_exists(engine, tbl): + try: + r = engine.execute("select * from {} limit 1".format(tbl)) + r.close() + return True + except Exception: + return False + + # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=100 + # means we cannot use the migrate.versioning.api module. So these + # methods perform similar wrapping functions to what is done by the API + # functions, but without disposing of the engine. + def upgrade(engine): + schema = ControlledSchema(engine, self.repo_path) + changeset = schema.changeset(None) + with sautils.withoutSqliteForeignKeys(engine): + for version, change in changeset: + log.msg('migrating schema version {} -> {}'.format(version, version + 1)) + schema.runchange(version, change, 1) + + def check_sqlalchemy_migrate_version(): + # sqlalchemy-migrate started including a version number in 0.7; we + # support back to 0.6.1, but not 0.6. We'll use some discovered + # differences between 0.6.1 and 0.6 to get that resolution. + version = getattr(migrate, '__version__', 'old') + if version == 'old': + try: + from migrate.versioning import schemadiff + if hasattr(schemadiff, 'ColDiff'): + version = "0.6.1" + else: + version = "0.6" + except Exception: + version = "0.0" + version_tup = tuple(map(int, version.split('-', 1)[0].split('.'))) + log.msg("using SQLAlchemy-Migrate version {}".format(version)) + if version_tup < (0, 6, 1): + raise RuntimeError(("You are using SQLAlchemy-Migrate {}. " + "The minimum version is 0.6.1.").format(version)) + + def version_control(engine, version=None): + ControlledSchema.create(engine, self.repo_path, version) + + # the upgrade process must run in a db thread + def thd(engine): + # if the migrate_version table exists, we can just let migrate + # take care of this process. + if table_exists(engine, 'migrate_version'): + r = engine.execute( + "select version from migrate_version limit 1") + old_version = r.scalar() + if old_version < 40: + raise EightUpgradeError() + try: + upgrade(engine) + except sa.exc.NoSuchTableError as e: # pragma: no cover + if 'migration_tmp' in str(e): + log.err('A serious error has been encountered during the upgrade. The ' + 'previous upgrade has been likely interrupted. The database has ' + 'been damaged and automatic recovery is impossible.') + log.err('If you believe this is an error, please submit a bug to the ' + 'Buildbot project.') + raise + + # if the version table exists, then we can version_control things + # at that version, drop the version table, and let migrate take + # care of the rest. + elif table_exists(engine, 'version'): + raise EightUpgradeError() + + # otherwise, this db is new, so we don't bother using the migration engine + # and just create the tables, and put the version directly to + # latest + else: + # do some tests before getting started + test_unicode(engine) + + log.msg("Initializing empty database") + Model.metadata.create_all(engine) + repo = migrate.versioning.repository.Repository(self.repo_path) + + version_control(engine, repo.latest) + + check_sqlalchemy_migrate_version() + yield self.db.pool.do_with_engine(thd) diff --git a/buildbot_gentoo_ci/scripts/update_db.py b/buildbot_gentoo_ci/scripts/update_db.py new file mode 100644 index 0000000..29e9072 --- /dev/null +++ b/buildbot_gentoo_ci/scripts/update_db.py @@ -0,0 +1,110 @@ +# This file has parts from Buildbot and is modifyed by Gentoo Authors. +# Buildbot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members +# Origins: buildbot.scripts.base.py +# buildbot.scripts.upgrade_master.py +# Modifyed by Gentoo Authors. +# Copyright 2020 Gentoo Authors + +import os +import signal +import sys +import traceback + +from twisted.internet import defer +from buildbot.master import BuildMaster +from buildbot.util import stripUrlPassword +from buildbot.config import ConfigErrors + +from buildbot_gentoo_ci.db import connector +from buildbot_gentoo_ci.config.config import FileLoader + +# Use FileLoader from Gentoo Ci +def loadConfig(config, configFileName='master.cfg'): + if not config['quiet']: + print("checking {}".format(configFileName)) + + try: + master_cfg = FileLoader( + config['basedir'], configFileName).loadConfig() + except ConfigErrors as e: + print("Errors loading configuration:") + + for msg in e.errors: + print(" " + msg) + return None + except Exception: + print("Errors loading configuration:") + traceback.print_exc(file=sys.stdout) + return None + + return master_cfg + +#Use the db from Gentoo Ci +@defer.inlineCallbacks +def upgradeDatabase(config, master_cfg): + if not config['quiet']: + print("upgrading database ({})".format(stripUrlPassword(master_cfg.db['db_url']))) + print("Warning: Stopping this process might cause data loss") + + def sighandler(signum, frame): + msg = " ".join(""" + WARNING: ignoring signal {}. + This process should not be interrupted to avoid database corruption. + If you really need to terminate it, use SIGKILL. + """.split()) + print(msg.format(signum)) + + prev_handlers = {} + try: + for signame in ("SIGTERM", "SIGINT", "SIGQUIT", "SIGHUP", + "SIGUSR1", "SIGUSR2", "SIGBREAK"): + if hasattr(signal, signame): + signum = getattr(signal, signame) + prev_handlers[signum] = signal.signal(signum, sighandler) + + master = BuildMaster(config['basedir']) + master.config = master_cfg + master.db.disownServiceParent() + db = connector.DBConnector(basedir=config['basedir']) + yield db.setServiceParent(master) + yield db.setup(master_cfg, check_version=False, verbose=not config['quiet']) + yield db.model.upgrade() + yield db.masters.setAllMastersActiveLongTimeAgo() + + finally: + # restore previous signal handlers + for signum, handler in prev_handlers.items(): + signal.signal(signum, handler) + +# Use gentooci.cfg for config +def upgradeGentooCi(config): + master_cfg = loadConfig(config, 'gentooci.cfg') + if not master_cfg: + return defer.succeed(1) + return _upgradeMaster(config, master_cfg) + +# No changes +def _upgradeMaster(config, master_cfg): + try: + upgradeDatabase(config, master_cfg) + except Exception: + e = traceback.format_exc() + print("problem while upgrading!:\n" + e, file=sys.stderr) + return 1 + else: + if not config['quiet']: + print("upgrade complete") + return 0 diff --git a/gentooci.cfg b/gentooci.cfg new file mode 100644 index 0000000..5036ae9 --- /dev/null +++ b/gentooci.cfg @@ -0,0 +1,17 @@ +# -*- python -*- +# ex: set filetype=python: + +# This is a sample gentoo ci buildmaster config file. It must be installed as +# 'gentooci.cfg' in your buildmaster's base directory. + +# This is the dictionary that the buildmaster pays attention to. We also use +# a shorter alias to save typing. +c = BuildmasterConfig = {} + +####### DB URL +####### DB URL +# This specifies what database buildbot uses to store its state. +# It's easy to start with sqlite, but it's recommended to switch to a dedicated +# database, such as PostgreSQL or MySQL, for use in production environments. +# http://docs.buildbot.net/current/manual/configuration/global.html#database-specification +c['db_url'] = "mysql://buildbot:xxxx@192.168.1.x/gentooci?max_idle=300" diff --git a/licenses/GPL-2 b/licenses/GPL-2 new file mode 100644 index 0000000..0e845b5 --- /dev/null +++ b/licenses/GPL-2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/master.cfg b/master.cfg new file mode 100644 index 0000000..996f917 --- /dev/null +++ b/master.cfg @@ -0,0 +1,80 @@ +# -*- python -*- +# ex: set filetype=python: +from buildbot_gentoo_ci.config import schedulers, workers, builders + +# This is a sample buildmaster config file. It must be installed as +# 'master.cfg' in your buildmaster's base directory. + +# This is the dictionary that the buildmaster pays attention to. We also use +# a shorter alias to save typing. +c = BuildmasterConfig = {} + +####### WORKERS + +# The 'workers' list defines the set of recognized workers. Each element is +# a Worker object, specifying a unique worker name and password. The same +# worker name and password must be configured on the worker. +c['workers'] = workers.gentoo_workers() + +# 'protocols' contains information about protocols which master will use for +# communicating with workers. You must define at least 'port' option that workers +# could connect to your master with this protocol. +# 'port' must match the value configured into the workers (with their +# --master option) +c['protocols'] = {'pb': {'port': 9989}} + +####### CHANGESOURCES + +# the 'change_source' setting tells the buildmaster how it should find out +# about source code changes. Here we point to the buildbot version of a python hello-world project. + +# c['change_source'] = [] + +####### SCHEDULERS + +# Configure the Schedulers, which decide how to react to incoming changes. In this +# case, just kick off a 'runtests' build + +c['schedulers'] = schedulers.gentoo_schedulers() + +####### BUILDERS + +# The 'builders' list defines the Builders, which tell Buildbot how to perform a build: +# what steps, and which workers can execute them. Note that any particular build will +# only take place on one worker. + +c['builders'] = builders.gentoo_builders() + +####### BUILDBOT SERVICES + +# 'services' is a list of BuildbotService items like reporter targets. The +# status of each build will be pushed to these targets. buildbot/reporters/*.py +# has a variety to choose from, like IRC bots. + +#c['services'] = [] + +####### PROJECT IDENTITY + +# the 'title' string will appear at the top of this buildbot installation's +# home pages (linked to the 'titleURL'). + +c['title'] = "Gentoo CI" +c['titleURL'] = "https://gentoo-ci.gentoo.org" + +# the 'buildbotURL' string should point to the location where the buildbot's +# internal web server is visible. This typically uses the port number set in +# the 'www' entry below, but with an externally-visible host name which the +# buildbot cannot figure out without some help. + +c['buildbotURL'] = "http://localhost:8010/" + +# minimalistic config to activate new web UI +c['www'] = dict(port=8010, + plugins=dict(waterfall_view={}, console_view={}, grid_view={})) + +####### DB URL +# This specifies what database buildbot uses to store its state. +# It's easy to start with sqlite, but it's recommended to switch to a dedicated +# database, such as PostgreSQL or MySQL, for use in production environments. +# http://docs.buildbot.net/current/manual/configuration/global.html#database-specification +c['db_url'] = "mysql://buildbot:xxx@192.168.1.x/buildbot?max_idle=300" |