From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id 2APuNcoIW2dV0wAAqHPOHw:P1 (envelope-from ) for ; Thu, 12 Dec 2024 16:01:15 +0000 Received: from aspmx1.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id 2APuNcoIW2dV0wAAqHPOHw (envelope-from ) for ; Thu, 12 Dec 2024 17:01:14 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=hxmxNVh+; dkim=fail ("headers rsa verify failed") header.d=inria.fr header.s=dc header.b=cADQ0rw0; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed), DKIM not aligned (relaxed)" header.from=inria.fr (policy=none) ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1734019274; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=MrIju8T3bCfkPTbYpJZFcb+Z9f0Qmy1WGoaBPwjI/0M=; b=TCDz1Y4xXsfKw5LxUSR/gO7TZSGdZYvmUpyEsp92ilTEfwxTbDiMrB++QmNMAWkxSk8R7M HCJIpd8cVCa04aycaHWU4Tk1yAyLU9W5qOAQt8+PZ+4Jlu53LS1b6uBfLzXwX13ig+SAZB vRrhjmBZ2X/EiUQaLZk9Syy3SLr7ZFU6TNEZEOQVmbBQd9FQHnKr4Olg2ThVPI9HRGUOPK dtMagUUG/mCUDquN+Iy3dWUuOWcN8eIVrOCMHeT6Gyk1q4xyOwKdsjkkDI/bOoJKa4N3Yn V4x03Sdd1vuGYePm1yQAlLpkHsOFcYIIJgjk9+3pWK1Jmeu+YpvqdJWpnXvAxQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=hxmxNVh+; dkim=fail ("headers rsa verify failed") header.d=inria.fr header.s=dc header.b=cADQ0rw0; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed), DKIM not aligned (relaxed)" header.from=inria.fr (policy=none) ARC-Seal: i=1; s=key1; d=yhetil.org; t=1734019274; a=rsa-sha256; cv=none; b=c9uFAhjmu0y5SaYTYkPBPFVtS17Cvas4xHPL/IsmuHp8yif7HI/NZWketFqqytBMu5Dgp3 NDmQuDMBC1ja7V8uaRrIoFaG1JSr6Iza0uRTMezI+8AMsU9dVxfjhAUBrlmkvYdttnWYkm 27f7CIoNZT7WHlpO+bISYWc4+mmK+ZAy3CZN74iIoqy7hhTnjXQF0X3nfUsH4w7DAXhyDg kM0h5SH4QHNZa6X+GbI6Ssq1qaxELUPVy1Z7Df2o78zurWQNVT6zPROGISu3fjEJcwoMAA t+m8/OUYo4xsBFfQcLDR2qDr3Jib7t3L9/RlQ+scrtV6n6ICYtrEPtzknPY4IA== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 42CE78C922 for ; Thu, 12 Dec 2024 17:01:14 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tLlbx-00021W-7h; Thu, 12 Dec 2024 11:00:21 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tLlbs-000209-Eq for guix-patches@gnu.org; Thu, 12 Dec 2024 11:00:16 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tLlbp-0000qV-M8 for guix-patches@gnu.org; Thu, 12 Dec 2024 11:00:16 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:References:In-Reply-To:Date:From:To:Subject; bh=MrIju8T3bCfkPTbYpJZFcb+Z9f0Qmy1WGoaBPwjI/0M=; b=hxmxNVh+ETv+Pw9pF0736OH00jXKjTAAeszoc9xpOuPpzFXeIG3X1UIAvGFHy9uozsLx1B1Jyi0VBIHEyvbayQvs8GKnW+7vy7hGv1/KI3vIn56NhoKBtnzWBTr4QyyQPt6I0+cYQTIlufLToDxFWG2XLPXRO3ekrKEMgQso3FiD6ujZIwlmVYSlZE2YisJB03+A/ZaPXvBRVziecYqdkd4EET/NfQLxiGipXNA7U2Cc2rpwZinjIzeTmJ7q+uJXM+n7AIzjDpE26VOjZQf2tWdPke8qCih42HdqI8rWZ8hQMJj+TZbJffQkCvltSsA9TiY8BK491xsIx6XyWXRn2g==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tLlbp-000158-9T for guix-patches@gnu.org; Thu, 12 Dec 2024 11:00:13 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#74769] [PATCH Cuirass v2 7/7] http: Add admin/forgejo/event. Resent-From: Romain GARBAGE Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 12 Dec 2024 16:00:13 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 74769 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 74769@debbugs.gnu.org Cc: ludovic.courtes@inria.fr, Romain GARBAGE Received: via spool by 74769-submit@debbugs.gnu.org id=B74769.17340191713631 (code B ref 74769); Thu, 12 Dec 2024 16:00:13 +0000 Received: (at 74769) by debbugs.gnu.org; 12 Dec 2024 15:59:31 +0000 Received: from localhost ([127.0.0.1]:39992 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tLlb2-0000wE-2l for submit@debbugs.gnu.org; Thu, 12 Dec 2024 10:59:31 -0500 Received: from mail3-relais-sop.national.inria.fr ([192.134.164.104]:36970) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tLlau-0000u3-Tc for 74769@debbugs.gnu.org; Thu, 12 Dec 2024 10:59:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=inria.fr; s=dc; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=MrIju8T3bCfkPTbYpJZFcb+Z9f0Qmy1WGoaBPwjI/0M=; b=cADQ0rw0jAkJTV03YTP0/ZbkLI+v5XHDMOXRXCOQ0T5VbiwhSzYGagrq xFhm3Hu8pHeO9pZDfbqAP8XkdwIq5lYeCpfITARy9wYEvdA4nK2H+IIBF NiPSIKVQxO5NLhYtZpDbaMrOoY+6yrBh4WakI+m1J0Ntfvx1b8iretmKq U=; X-IronPort-AV: E=Sophos;i="6.12,229,1728943200"; d="scan'208";a="104178650" Received: from unknown (HELO guix-A102.bordeaux.inria.fr) ([193.50.110.191]) by mail3-relais-sop.national.inria.fr with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Dec 2024 16:58:59 +0100 From: Romain GARBAGE Date: Thu, 12 Dec 2024 16:57:55 +0100 Message-ID: <20241212155845.27344-7-romain.garbage@inria.fr> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20241212155845.27344-1-romain.garbage@inria.fr> References: <20241212155845.27344-1-romain.garbage@inria.fr> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Migadu-Scanner: mx11.migadu.com X-Migadu-Spam-Score: 0.01 X-Spam-Score: 0.01 X-Migadu-Queue-Id: 42CE78C922 X-TUID: hXSWX1MmppTg * src/cuirass/http.scm (url-handler): Add "/admin/forgejo/event". * tests/http.scm: Add tests for the "/admin/forgejo/event" endpoint. * doc/cuirass.texi: Reorganize "Interfacing Cuirass..." section. Add documentation for the "/admin/forgejo/event" endpoint. --- doc/cuirass.texi | 38 ++++++++++++++++-- src/cuirass/http.scm | 64 +++++++++++++++++++++++++++++- tests/http.scm | 93 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 188 insertions(+), 7 deletions(-) diff --git a/doc/cuirass.texi b/doc/cuirass.texi index 13739c9..895d91f 100644 --- a/doc/cuirass.texi +++ b/doc/cuirass.texi @@ -1284,11 +1284,14 @@ This request accepts a mandatory parameter. Limit query result to nr elements. This parameter is @emph{mandatory}. @end table -@section Interfacing Cuirass with a GitLab Server +@section Interfacing Cuirass with a Git forge -Cuirass supports integration with GitLab through the @dfn{webhook} mechanism: -a POST request is sent by a GitLab instance whenever a specific event is -triggered. So far, Cuirass only support merge-request events. +Cuirass supports integration with various forges through the +@dfn{webhook} mechanism: a POST request is sent by the forge instance +whenever a specific event is triggered. So far, Cuirass only support +merge-request/pull-request events. + +@subsection Interfacing with a GitLab Server Sending a merge request event on the @code{/admin/gitlab/event} endpoint allows controlling a specific jobset related to the merge request @@ -1330,6 +1333,33 @@ A JSON list of strings. Each string must be a supported system, i.e. @code{"systems": [ "x86_64-linux", "aarch64-linux" ]} @end table +@subsection Interfacing with a Forgejo Server + +Sending a merge request event on the @code{/admin/forgejo/event} +endpoint allows controlling a specific jobset related to the merge +request content. This interface expect the JSON data to contain the +following keys: +@table @code +@item "action" +@item "pull_request.number" +@item "pull_request.state" +@item "pull_request.base.label" +@item "pull_request.base.ref" +@item "pull_request.base.sha" +@item "pull_request.base.repo.name" +@item "pull_request.base.repo.clone_url" +@item "pull_request.head.label" +@item "pull_request.head.ref" +@item "pull_request.head.sha" +@item "pull_request.head.repo.name" +@item "pull_request.head.repo.clone_url" +@end table + +The resulting jobset, named as +@code{forgejo-pull-requests-@var{pull_request}.@var{base}.@var{pull_request}.@var{name}-@var{pull_request}.@var{head}.@var{ref}-@var{pull_request}.@var{number}}, +is set to build the channel corresponding to the source branch in the +merge request data with a priority of 1. + @c ********************************************************************* @node Database @chapter Database schema diff --git a/src/cuirass/http.scm b/src/cuirass/http.scm index 8ea929f..0a6bfae 100644 --- a/src/cuirass/http.scm +++ b/src/cuirass/http.scm @@ -5,7 +5,7 @@ ;;; Copyright © 2018 Clément Lassieur ;;; Copyright © 2018 Tatiana Sholokhova ;;; Copyright © 2019, 2020 Ricardo Wurmus -;;; Copyright © 2024 Romain Garbage +;;; Copyright © 2024 Romain Garbage ;;; ;;; This file is part of Cuirass. ;;; @@ -28,6 +28,7 @@ #:use-module (cuirass database) #:use-module ((cuirass base) #:select (evaluation-log-file)) #:use-module (cuirass forges gitlab) + #:use-module (cuirass forges forgejo) #:use-module (cuirass metrics) #:use-module (cuirass utils) #:use-module (cuirass logging) @@ -779,6 +780,67 @@ return DEFAULT." (event-type (respond-json-with-error 400 (format #f "Event type \"~a\" not supported." event-type)))) (respond-json-with-error 400 "This API only supports JSON.")))) + ;; Define an API for Forgejo events + (('POST "admin" "forgejo" "event") + (let* ((params (utf8->string body)) + (event-type (assoc-ref (request-headers request) 'x-forgejo-event)) + (content-type (assoc-ref (request-headers request) 'content-type)) + (json? (equal? (car content-type) + 'application/json))) + (if json? + (match event-type + ("pull_request" + (let* ((event (json->forgejo-pull-request-event params)) + (pull-request (forgejo-pull-request-event-pull-request event)) + (spec (forgejo-pull-request->specification pull-request)) + (spec-name (specification-name spec))) + (match (forgejo-pull-request-event-action event) + ;; New pull request. + ((or 'opened 'reopened) + (if (not (db-get-specification spec-name)) + (begin + (db-add-or-update-specification spec) + + (unless (call-bridge `(register-jobset ,spec-name) + bridge) + (log-warning + "cannot notify bridge of the addition of jobset '~a'" + spec-name)) + (respond + (build-response #:code 200 + #:headers + `((location . ,(string->uri-reference "/")))) + #:body "")) + (begin + (log-warning "jobset '~a' already exists" spec-name) + (respond-json-with-error 400 "Jobset already exists.")))) + ;; Closed or merged pull request. + ('closed + (if (db-get-specification spec-name) + (begin + (call-bridge `(remove-jobset ,spec-name) bridge) + (log-info "Removed jobset '~a'" spec-name) + (respond + (build-response #:code 200 + #:headers + `((location . ,(string->uri-reference "/")))) + #:body "")) + (begin + (log-warning "cannot find jobset '~a'" spec-name) + (respond-json-with-error 404 "Jobset not found.")))) + ;; Pull request is updated. + ('synchronized + (if (db-get-specification spec-name) + (if (call-bridge `(trigger-jobset ,(specification-name spec)) + bridge) + (respond-json (scm->json-string `((jobset . ,spec-name)))) + (begin + (log-warning "evaluation hook disabled") + (respond-json-with-error 400 "Evaluation hook disabled."))) + (respond-json-with-error 404 "Jobset not found.")))))) + (_ (respond-json-with-error 400 (format #f "Event type \"~a\" not supported." event-type)))) + (respond-json-with-error 400 "This API only supports JSON.")))) + (('POST "admin" "specification" "add") (let* ((spec (body->specification body)) (name (specification-name spec))) diff --git a/tests/http.scm b/tests/http.scm index aa58e78..862e06b 100644 --- a/tests/http.scm +++ b/tests/http.scm @@ -3,6 +3,7 @@ ;;; Copyright © 2017-2020, 2023-2024 Ludovic Courtès ;;; Copyright © 2017, 2020, 2021 Mathieu Othacehe ;;; Copyright © 2018 Clément Lassieur +;;; Copyright © 2024 Romain Garbage ;;; ;;; This file is part of Cuirass. ;;; @@ -22,6 +23,7 @@ (use-modules ((cuirass base) #:select (%bridge-socket-file-name)) (cuirass http) (cuirass database) + (cuirass forges forgejo) (cuirass forges gitlab) (cuirass specification) (cuirass utils) @@ -43,8 +45,9 @@ (call-with-values (lambda () (http-get uri)) (lambda (response body) body))) -(define (http-post-json uri body) - (http-post uri #:body body #:headers '((content-type application/json)))) +(define* (http-post-json uri body #:optional (extra-headers '())) + (http-post uri #:body body #:headers (append '((content-type application/json)) + extra-headers))) (define (wait-until-ready port) ;; Wait until the server is accepting connections. @@ -132,6 +135,65 @@ (gitlab-event-value event) (gitlab-event-project event)))) +(define forgejo-pull-request-json-open + "{ + \"action\": \"opened\", + \"pull_request\": { + \"number\": 1, + \"state\": \"open\", + \"base\": { + \"label\": \"base-label\", + \"ref\": \"base-branch\", + \"sha\": \"666af40e8a059fa05c7048a7ac4f2eccbbd0183b\", + \"repo\": { + \"name\": \"project-name\", + \"clone_url\": \"https://forgejo.instance.test/base-repo/project-name.git\" + } + }, + \"head\": { + \"label\": \"test-label\", + \"ref\": \"test-branch\", + \"sha\": \"582af40e8a059fa05c7048a7ac4f2eccbbd0183b\", + \"repo\": { + \"name\": \"fork-name\", + \"clone_url\": \"https://forgejo.instance.test/source-repo/fork-name.git\" + } + } + } + }") + +(define forgejo-pull-request-json-close + "{ + \"action\": \"closed\", + \"pull_request\": { + \"number\": 1, + \"state\": \"closed\", + \"base\": { + \"label\": \"base-label\", + \"ref\": \"base-branch\", + \"sha\": \"666af40e8a059fa05c7048a7ac4f2eccbbd0183b\", + \"repo\": { + \"name\": \"project-name\", + \"clone_url\": \"https://forgejo.instance.test/base-repo/project-name.git\" + } + }, + \"head\": { + \"label\": \"test-label\", + \"ref\": \"test-branch\", + \"sha\": \"582af40e8a059fa05c7048a7ac4f2eccbbd0183b\", + \"repo\": { + \"name\": \"fork-name\", + \"clone_url\": \"https://forgejo.instance.test/source-repo/fork-name.git\" + } + } + } + }") + +(define forgejo-pull-request-specification + (forgejo-pull-request->specification + (forgejo-pull-request-event-pull-request + (json->forgejo-pull-request-event forgejo-pull-request-json-open)))) + (define-syntax-rule (with-cuirass-register exp ...) (with-guix-daemon (let ((pid #f)) @@ -457,6 +519,33 @@ (response-code (http-post-json (test-cuirass-uri "/admin/gitlab/event") gitlab-merge-request-json-close))) + (test-equal "/admin/forgejo/event creates a spec from a new pull request" + (specification-name forgejo-pull-request-specification) + (begin + (http-post-json (test-cuirass-uri "/admin/forgejo/event") + forgejo-pull-request-json-open + '((x-forgejo-event . "pull_request"))) + (specification-name (db-get-specification (specification-name forgejo-pull-request-specification))))) + + (test-equal "/admin/forgejo/event error when a pull request has already been created" + 400 + (response-code (http-post-json (test-cuirass-uri "/admin/forgejo/event") + forgejo-pull-request-json-open + '((x-forgejo-event . "pull_request"))))) + + (test-assert "/admin/forgejo/event removes a spec from a closed pull request" + (begin + (http-post-json (test-cuirass-uri "/admin/forgejo/event") + forgejo-pull-request-json-close + '((x-forgejo-event . "pull_request"))) + (not (db-get-specification (specification-name forgejo-pull-request-specification))))) + + (test-equal "/admin/forgejo/event error when a pull request has already been closed" + 404 + (response-code (http-post-json (test-cuirass-uri "/admin/forgejo/event") + forgejo-pull-request-json-close + '((x-forgejo-event . "pull_request"))))) + (test-assert "db-close" (begin (db-close (%db)) -- 2.46.0