diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | buildbot-build.el | 4 | ||||
-rw-r--r-- | buildbot-client.el | 78 | ||||
-rw-r--r-- | buildbot-revision.el | 70 | ||||
-rw-r--r-- | buildbot-utils.el | 50 | ||||
-rw-r--r-- | buildbot.el | 10 |
6 files changed, 213 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4e5f6c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~
\ No newline at end of file diff --git a/buildbot-build.el b/buildbot-build.el new file mode 100644 index 0000000..ca1a8bc --- /dev/null +++ b/buildbot-build.el @@ -0,0 +1,4 @@ +(define-derived-mode buildbot-build-mode special-mode "Buildbot build" + "Buildbot view for a build") + +(provide 'buildbot-build) diff --git a/buildbot-client.el b/buildbot-client.el new file mode 100644 index 0000000..1bb8310 --- /dev/null +++ b/buildbot-client.el @@ -0,0 +1,78 @@ +;; -*- lexical-binding: t; -*- +(require 'buildbot-utils) + +(defun buildbot-api-change (attr) + (buildbot-url-fetch-json + (format + "%s/api/v2/changes?%s" + buildbot-host (buildbot-format-attr attr)))) + +(defun buildbot-api-logs (stepid) + (buildbot-url-fetch-json + (format + "%s/api/v2/steps/%s/logs" + buildbot-host stepid))) + +(defun buildbot-api-builders () + (buildbot-url-fetch-json + (format + "%s/api/v2/builders" + buildbot-host))) + +(defun buildbot-api-build (attr) + (buildbot-url-fetch-json + (format + "%s/api/v2/builds?%s" + buildbot-host (buildbot-format-attr attr)))) + +(defun buildbot-api-step (buildid) + (buildbot-url-fetch-json + (format + "%s/api/v2/builds/%s/steps" + buildbot-host buildid))) + +(defun buildbot-format-log-url (logid) + (format "%s/api/v2/logs/%s/raw" buildbot-host logid)) + +(defun buildbot-api-recent-changes (limit) + (buildbot-api-change (list (cons 'order "-changeid") (cons 'limit limit)))) + +(defun buildbot-get-all-builders () + (alist-get 'builders (buildbot-api-builders))) + +(defun buildbot-builder-by-id (builderid) + (cl-find-if + (lambda (builder) + (= (alist-get 'builderid builder) builderid)) + buildbot-builders)) + +(defun buildbot-get-builder-name-by-id (id) + (alist-get 'name (buildbot-builder-by-id id))) + +(defun buildbot-get-change-by-revision (revision) + (elt + (alist-get 'changes + (buildbot-api-change (list (cons 'revision revision)))) + 0)) + +(defun buildbot-get-builds-by-revision (revision) + (alist-get 'builds (buildbot-get-change-by-revision revision))) + +(defun buildbot-get-failed-builds-by-revision (revision) + (seq-filter + (lambda (build) + (not (equal (alist-get 'state_string build) "build successful"))) + (buildbot-get-builds-by-revision revision))) + +(defun buildbot-format-builds-by-revision (revision) + (mapcar + 'buildbot-format-build + (buildbot-get-builds-by-revision revision))) + +(defun buildbot-get-steps-by-buildid (buildid) + (alist-get 'steps (buildbot-api-step buildid))) + +(defun buildbot-get-logs-by-stepid (stepid) + (alist-get 'logs (buildbot-api-logs stepid))) + +(provide 'buildbot-client) diff --git a/buildbot-revision.el b/buildbot-revision.el new file mode 100644 index 0000000..4f8ad57 --- /dev/null +++ b/buildbot-revision.el @@ -0,0 +1,70 @@ +;; -*- lexical-binding: t; -*- +(require 'buildbot-client) + +(defvar-local buildbot-revision-revision-id nil) +(defvar buildbot-revision-header-regex "^\\[.*\\]$") + +(define-derived-mode buildbot-revision-mode special-mode "Buildbot revision" + "Buildbot view for a revision") + +(define-key buildbot-revision-mode-map "g" 'buildbot-revision-reload) + +(defun buildbot-revision-buffer-name (revision) + (concat "*buildbot revision " revision "*")) + +(defun buildbot-revision-load (revision) + (let ((buffer-name (buildbot-revision-buffer-name revision))) + (with-current-buffer (get-buffer-create buffer-name) + (buildbot-revision-mode) + (setq buildbot-revision-revision-id revision) + (buildbot-revision-update)) + (switch-to-buffer buffer-name))) + +(defun buildbot-revision-update () + (unless (derived-mode-p 'buildbot-revision-mode) + (error "Not in buildbot revision mode")) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (buildbot-revision-format buildbot-revision-revision-id)))) + +(defun buildbot-revision-open (revision) + (interactive "sRevision (commit hash): ") + (buildbot-revision-load revision)) + +(defun buildbot-revision-reload () + (interactive) + (buildbot-revision-update)) + +(defun buildbot-revision-format (revision) + (string-join + (mapcar 'buildbot-revision-format-build + (buildbot-get-builds-by-revision revision)) + "\n")) + +(defun buildbot-revision-next-header (n) + (interactive "p") + (dotimes (_ n) + (end-of-line 1) + (re-search-forward buildbot-revision-header-regex) + (beginning-of-line 1))) +(define-key buildbot-revision-mode-map "n" 'buildbot-revision-next-header) + +(defun buildbot-revision-previous-header (n) + (interactive "p") + (beginning-of-line 1) + (unless (looking-at buildbot-revision-header-regex) + (re-search-backward buildbot-revision-header-regex)) + (dotimes (_ n) + (re-search-backward buildbot-revision-header-regex))) +(define-key buildbot-revision-mode-map "p" 'buildbot-revision-previous-header) + +(defun buildbot-revision-format-build (build) + (format "[%s %s]\n%s\n" + (buildbot-get-builder-name-by-id (alist-get 'builderid build)) + (alist-get 'state_string build) + (string-join + (mapcar (lambda (test) (alist-get 'test_name test)) + (alist-get 'failed_tests build)) + "\n"))) + +(provide 'buildbot-revision) diff --git a/buildbot-utils.el b/buildbot-utils.el new file mode 100644 index 0000000..ebac706 --- /dev/null +++ b/buildbot-utils.el @@ -0,0 +1,50 @@ +(defun buildbot-parse-http-header (text) + (let ((status) (fields)) + (with-temp-buffer + (insert text) + (goto-char (point-min)) + (re-search-forward "^HTTP.*\\([0-9]\\{3\\}\\).*$") + (setq status (match-string 1)) + (while (re-search-forward "^\\(.*?\\): \\(.*\\)$" nil t) + (push (cons (intern (match-string 1)) (match-string 2)) fields))) + (list (cons 'status status) (cons 'fields fields)))) + +(defun buildbot-delete-http-header () + (save-excursion + (goto-char (point-min)) + (kill-region (point) (progn (re-search-forward "\r?\n\r?\n") + (point))))) + +(defun buildbot-url-fetch-json (url &optional decompression with-header) + (with-current-buffer (get-buffer-create buildbot-client-buffer-name) + (goto-char (point-max)) + (insert "[" (current-time-string) "] Request: " url "\n")) + (with-current-buffer (url-retrieve-synchronously url t) + (let ((header) (status) (fields)) + (buildbot-delete-http-header) + (goto-char (point-min)) + (setq header (buildbot-parse-http-header (car kill-ring)) + status (alist-get 'status header) + fields (alist-get 'fields header)) + (with-current-buffer buildbot-client-buffer-name + (insert "[" (current-time-string) "] Response: " status "\n")) + (when decompression + (call-process-region (point) (point-max) "gunzip" t t t) + (goto-char (point-min))) + (call-interactively 'delete-trailing-whitespace) + (if (string= status "200") + (unless (= (point) (point-max)) + (if with-header + (list + (cons 'header fields) + (cons 'json (json-read))) + (json-read))) + (error "HTTP error: %s" (buffer-substring (point) (point-max))))))) + +(defun buildbot-format-attr (attr) + (string-join (mapcar (lambda (pair) + (format "%s=%s" (car pair) (cdr pair))) + attr) + "&")) + +(provide 'buildbot-utils) diff --git a/buildbot.el b/buildbot.el new file mode 100644 index 0000000..7c62017 --- /dev/null +++ b/buildbot.el @@ -0,0 +1,10 @@ +;; -*- lexical-binding: t; -*- +(defvar buildbot-builders (buildbot-get-all-builders)) +(defvar buildbot-client-buffer-name "*buildbot api*") +(defvar buildbot-host nil) + +(require 'buildbot-revision) +(require 'buildbot-build) +(require 'buildbot-client) + +(provide 'buildbot) |