From 85a74a9e32e7e03e1d7958fb251cc23439d6792f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 11 Aug 2023 11:13:34 +0200 Subject: [PATCH] [ADD] runbot_merge: staging query endpoints `/runbot_merge/stagings` ======================== This endpoint is a reverse lookup from any number of commits to a (number of) staging(s): - it takes a list of commit hashes as either the `commits` or the `heads` keyword parameter - it then returns the stagings which have *all* these commits as respectively commits or heads, if providing all commits for a project the result should always be unique (if any) - `commits` are the merged commits, aka the stuff which ends up in the actual branches - `heads` are the staging heads, aka the commits at the tip of the `staging.$name` branches, those may be the same as the corresponding commit, or might be deduplicator commits which get discarded on success `/runbot_merge/stagings/:id` ============================ Returns a list of all PRs in the staging, grouped by batch (aka PRs which have the same label and must be merged together). For each PR, the `repository` name, `number`, and `name` in the form `$repository#$number` get returned. `/runbot_merge/stagings/:id1/:id2` ================================== Returns a list of all the *successfully merged* stagings between `id1` and `id2`, from oldest to most recent. Individual records have the form: - `staging` is the id of the staging - `prs` is the contents of the previous endpoint (a list of PRs grouped by batch) `id1` *must* be lower than `id2`. By default, this endpoint is inclusive on both ends, the `include_from` and / or `include_to` parameters can be passed with the `False` value to exclude the corresponding bound from the result. Related to #768 --- runbot_merge/controllers/__init__.py | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/runbot_merge/controllers/__init__.py b/runbot_merge/controllers/__init__.py index 2f9fbc20..3e693824 100644 --- a/runbot_merge/controllers/__init__.py +++ b/runbot_merge/controllers/__init__.py @@ -15,6 +15,63 @@ from .. import utils, github _logger = logging.getLogger(__name__) class MergebotController(Controller): + @route('/runbot_merge/stagings', auth='none', type='json') + def stagings_for_commits(self, commits=None, heads=None): + Stagings = request.env(user=1)['runbot_merge.stagings'].sudo() + if commits: + stagings = Stagings.for_commits(*commits) + elif heads: + stagings = Stagings.for_heads(*heads) + else: + raise ValueError('Must receive one of "commits" or "heads" kwarg') + + return stagings.ids + + @route('/runbot_merge/stagings/', auth='none', type='json') + def prs_for_staging(self, staging): + staging = request.env(user=1)['runbot_merge.stagings'].browse(staging) + return [ + batch.prs.mapped(lambda p: { + 'name': p.display_name, + 'repository': p.repository.name, + 'number': p.number, + }) + for batch in staging.sudo().batch_ids + ] + + @route('/runbot_merge/stagings//', auth='none', type='json') + def prs_for_staging(self, from_staging, to_staging, include_from=True, include_to=True): + Stagings = request.env(user=1, context={"active_test": False})['runbot_merge.stagings'] + from_staging = Stagings.browse(from_staging) + to_staging = Stagings.browse(to_staging) + if from_staging.target != to_staging.target: + raise ValueError(f"Stagings must have the same target branch, found {from_staging.target.name} and {to_staging.target.name}") + if from_staging.id >= to_staging.id: + raise ValueError("first staging must be older than second staging") + + stagings = Stagings.search([ + ('target', '=', to_staging.target.id), + ('state', '=', 'success'), + ('id', '>=' if include_from else '>', from_staging.id), + ('id', '<=' if include_to else '<', to_staging.id), + ], order="id asc") + + return [ + { + 'staging': staging.id, + 'prs': [ + batch.prs.mapped(lambda p: { + 'name': p.display_name, + 'repository': p.repository.name, + 'number': p.number, + }) + for batch in staging.batch_ids + ] + } + for staging in stagings + ] + + @route('/runbot_merge/hooks', auth='none', type='json', csrf=False, methods=['POST']) def index(self): req = request.httprequest