def post(self, repo: Repository): """ Create a new build. """ schema = BuildCreateSchema(context={"repository": repo}) build = self.schema_from_request(schema) db.session.add(build) try: db.session.commit() except IntegrityError as exc: if "duplicate" in str(exc): db.session.rollback() return self.respond(status=422) raise if not build.revision_sha: from zeus.tasks import resolve_ref_for_build resolve_ref_for_build.delay(build_id=build.id) build_schema.validate(build) data = build_schema.dump(build) publish("builds", "build.create", data) return self.respond(data, 200)
def post(self, repo: Repository): """ Create a new build. """ schema = BuildCreateSchema(strict=True, context={"repository": repo}) result = self.schema_from_request(schema, partial=True) if result.errors: return self.respond(result.errors, 403) data = result.data # TODO(dcramer): only if we create a source via a patch will we need the author # author_data = data.pop('author') # if author_data.get('email'): # author = Author.query.filter( # Author.repository_id == repo.id, Author.email == author_data['email'] # ).first() # else: # author = None # if not author: # author = Author(repository_id=repo.id, **author_data) # db.session.add(author) # db.session.flush() # TODO(dcramer): need to handle patch case yet source = (Source.query.options( joinedload("author"), joinedload("revision")).filter( Source.revision_sha == data.pop("ref"), Source.repository_id == repo.id).first()) build = Build(repository=repo, **data) # TODO(dcramer): we should convert source in the schema build.source = source # build.source_id = source.id build.author = source.author if not source.patch_id: if not build.label: build.label = source.revision.message.split("\n")[0] if not build.label: return self.error("missing build label") db.session.add(build) try: db.session.commit() except IntegrityError: db.session.rollback() return self.respond(status=422) result = build_schema.dump(build) assert not result.errors, "this should never happen" publish("builds", "build.create", result.data) return self.respond(result.data, 200)
def resolve_ref_for_build(build_id: UUID): lock_key = f"resolve-build-ref:{build_id}" with redis.lock(lock_key, timeout=60.0, nowait=True): build = Build.query.unrestricted_unsafe().get(build_id) if not build: raise ValueError( "Unable to find build with id = {}".format(build_id)) if build.revision_sha: return auth.set_current_tenant( auth.RepositoryTenant(repository_id=build.repository_id)) revision: Optional[Revision] = None try: revision = revisions.identify_revision(build.repository, build.ref, with_vcs=True) except UnknownRevision: build.result = Result.errored build.status = Status.finished try: with db.session.begin_nested(): db.session.add( FailureReason( repository_id=build.repository_id, build_id=build.id, reason=FailureReason.Reason.unresolvable_ref, )) db.session.flush() except IntegrityError as exc: if "duplicate" not in str(exc): raise except InvalidPublicKey: pass if revision: build.revision_sha = revision.sha if not build.authors and revision.authors: build.authors = revision.authors if not build.label: build.label = revision.message.split("\n")[0] db.session.add(build) db.session.commit() data = build_schema.dump(build) publish("builds", "build.update", data)
def put(self, build: Build): """ Update a build. """ result = self.schema_from_request(build_schema, partial=True) for key, value in result.items(): if getattr(build, key) != value: setattr(build, key, value) if db.session.is_modified(build): db.session.add(build) db.session.commit() result = build_schema.dump(build) publish("builds", "build.update", result) return self.respond(result, 200)
def put(self, build: Build): """ Update a build. """ result = self.schema_from_request(build_schema, partial=True) if result.errors: return self.respond(result.errors, 403) for key, value in result.data.items(): if getattr(build, key) != value: setattr(build, key, value) if db.session.is_modified(build): db.session.add(build) db.session.commit() result = build_schema.dump(build) if result.errors: return self.error('invalid schema supplied') publish('builds', 'build.update', result.data) return self.respond(result.data, 200)
def mock_build(repo: models.Repository, parent_revision: models.Revision=None, user_ids=(), file_list=()): if user_ids and randint(0, 1) == 0: chosen_user_id = choice(user_ids) author = mock_author(repo, chosen_user_id) else: author = None revision = factories.RevisionFactory.create( repository=repo, parents=[parent_revision.sha] if parent_revision else None, **{'author': author} if author else {} ) source = factories.SourceFactory.create( revision=revision, patch=factories.PatchFactory( parent_revision=parent_revision, ) if parent_revision and random() > 0.8 else None, ) parent_revision = revision build = factories.BuildFactory.create(source=source, travis=True) result = build_schema.dump(build) publish('builds', 'build.create', result.data) click.echo('Created {!r}'.format(build)) # we need to find some filenames for the repo if file_list is None: file_list = find_files_in_repo(repo) for n in range(randint(0, 50)): try: with db.session.begin_nested(): factories.FileCoverageFactory.create( filename=choice(file_list), build=build, in_diff=randint(0, 5) == 0) except IntegrityError: continue for n in range(1, 4): has_failure = randint(0, 2) == 0 job = factories.JobFactory.create( build=build, failed=has_failure, passed=not has_failure, travis=True, allow_failure=n == 3, ) for n in range(randint(0, 50)): test_failed = has_failure and randint(0, 5) == 0 factories.TestCaseFactory.create( job=job, failed=test_failed, passed=not test_failed, ) if has_failure and randint(0, 2) == 0: for n in range(1, 5): factories.StyleViolationFactory.create( job=job, ) for n in range(randint(0, 2)): bundle = factories.BundleFactory.create( job=job, ) for n in range(randint(0, 4)): factories.BundleAssetFactory.create( bundle=bundle, job=job, ) artifact_count = randrange(3) \ if job.status == Status.finished and job.result == Result.passed \ else 0 for n in range(0, artifact_count): factories.ArtifactFactory.create(job=job, repository=repo) db.session.commit() aggregate_build_stats_for_job(job_id=job.id) result = build_schema.dump(build) publish('builds', 'build.create', result.data) click.echo('Created {!r}'.format(build)) db.session.commit() return build
def mock_build( repo: models.Repository, revision: models.Revision = None, parent_revision: models.Revision = None, user_ids=(), file_list=(), with_change_request=True, ) -> models.Build: if user_ids and randint(0, 1) == 0: chosen_user_id = choice(user_ids) author = mock_author(repo, chosen_user_id) else: author = None if not revision: revision, source = mock_revision(repo, parent_revision, author) else: for n in range(2): source = (models.Source.query.unrestricted_unsafe().filter( models.Source.repository_id == repo.id, models.Source.revision_sha == revision.sha, ).first()) if source: break try_create( models.Source, { "revision_sha": revision.sha, "repository": repo, "author_id": revision.author_id, }, ) else: raise NotImplementedError if with_change_request and parent_revision is None: parent_revision = factories.RevisionFactory.create(repository=repo) if with_change_request: factories.ChangeRequestFactory.create( repository=repo, head_revision=revision, head_revision_sha=revision.sha, parent_revision=parent_revision, github=True, **{"author": author} if author else {}) parent_revision = revision build = factories.BuildFactory.create(source=source, travis=True) result = build_schema.dump(build) publish("builds", "build.create", result.data) click.echo("Created {!r}".format(build)) # we need to find some filenames for the repo if file_list is None: file_list = find_files_in_repo(repo) for n in range(randint(0, 50)): try: with db.session.begin_nested(): factories.FileCoverageFactory.create( filename=choice(file_list), build=build, in_diff=randint(0, 5) == 0) except IntegrityError: continue for n in range(1, 4): has_failure = randint(0, 2) == 0 job = factories.JobFactory.create( build=build, failed=has_failure, passed=not has_failure, travis=True, allow_failure=n == 3, ) for n in range(randint(0, 50)): test_failed = has_failure and randint(0, 5) == 0 factories.TestCaseFactory.create(job=job, failed=test_failed, passed=not test_failed) if has_failure and randint(0, 2) == 0: for n in range(1, 5): factories.StyleViolationFactory.create(job=job) for n in range(randint(0, 2)): bundle = factories.BundleFactory.create(job=job) for n in range(randint(0, 4)): factories.BundleAssetFactory.create(bundle=bundle, job=job) artifact_count = (randrange(3) if job.status == Status.finished and job.result == Result.passed else 0) for n in range(0, artifact_count): factories.ArtifactFactory.create(job=job, repository=repo, finished=True) db.session.commit() aggregate_build_stats_for_job(job_id=job.id) result = build_schema.dump(build) publish("builds", "build.update", result.data) click.echo("Created {!r}".format(job)) db.session.commit() return build
def post(self, repo: Repository): """ Create a new build. """ result = self.schema_from_request(build_create_schema, partial=True) if result.errors: return self.respond(result.errors, 403) data = result.data ref = data.pop('ref', None) if ref is None: return self.error('missing ref') try: revision = identify_revision(repo, ref) except UnknownRevision: current_app.logger.warn('invalid ref received', exc_info=True) return self.error('unable to find a revision matching ref') # TODO(dcramer): only if we create a source via a patch will we need the author # author_data = data.pop('author') # if author_data.get('email'): # author = Author.query.filter( # Author.repository_id == repo.id, Author.email == author_data['email'] # ).first() # else: # author = None # if not author: # author = Author(repository_id=repo.id, **author_data) # db.session.add(author) # db.session.flush() # TODO(dcramer): need to handle patch case yet source = Source.query.options( joinedload('author'), ).filter( Source.revision_sha == revision.sha, Source.repository_id == repo.id, ).first() build = Build(repository=repo, **data) # TODO(dcramer): we should convert source in the schema build.source = source # build.source_id = source.id build.author = source.author if not source.patch_id: if not build.label: build.label = source.revision.message.split('\n')[0] if not build.label: return self.error('missing build label') db.session.add(build) try: db.session.commit() except IntegrityError: db.session.rollback() return self.respond(status=422) result = build_schema.dump(build) assert not result.errors, 'this should never happen' publish('builds', 'build.create', result.data) return self.respond(result.data, 200)
async def mock_build( repo: models.Repository, revision: models.Revision = None, parent_revision: models.Revision = None, user_ids=(), file_list=(), with_change_request=True, ) -> models.Build: author: Optional[models.Author] = None if user_ids and randint(0, 1) == 0: chosen_user_id = choice(user_ids) author = await mock_author(repo, chosen_user_id) if not revision: revision = await mock_revision(repo, parent_revision, author) if with_change_request and parent_revision is None: parent_revision = factories.RevisionFactory.create(repository=repo) if with_change_request: factories.ChangeRequestFactory.create( repository=repo, head_revision=revision, head_revision_sha=revision.sha, parent_revision=parent_revision, github=True, **{"authors": [author]} if author else {}) parent_revision = revision build = factories.BuildFactory.create(revision=revision, travis=True) data = build_schema.dump(build) publish("builds", "build.create", data) click.echo("Created {!r}".format(build)) # we need to find some filenames for the repo if file_list is None: file_list = find_files_in_repo(repo) for n in range(randint(0, 50)): try: with db.session.begin_nested(): factories.FileCoverageFactory.create( filename=choice(file_list), build=build, in_diff=randint(0, 5) == 0) except IntegrityError: continue for n in range(1, 4): has_failure = randint(0, 2) == 0 job = factories.JobFactory.create( build=build, failed=has_failure, passed=not has_failure, travis=True, allow_failure=n == 3, ) for n in range(randint(0, 50)): test_failed = has_failure and randint(0, 5) == 0 factories.TestCaseFactory.create(job=job, failed=test_failed, passed=not test_failed) if has_failure and randint(0, 2) == 0: for n in range(1, 5): factories.StyleViolationFactory.create(job=job) for n in range(randint(0, 2)): bundle = factories.BundleFactory.create(job=job) for n in range(randint(0, 4)): factories.BundleAssetFactory.create(bundle=bundle, job=job) factories.FailureReasonFactory.create(build=build, job=job, failing_tests=True) artifact_count = (randrange(3) if job.status == Status.finished and job.result == Result.passed else 0) for n in range(0, artifact_count): factories.ArtifactFactory.create(job=job, repository=repo, finished=True) db.session.commit() aggregate_build_stats_for_job(job_id=job.id) data = build_schema.dump(build) publish("builds", "build.update", data) click.echo("Created {!r}".format(job)) db.session.commit() return build