Setting sourcehut repository readme from org-mode files

March 11, 2021 (modified March 22, 2021)

I am moving my code to sourcehut, but I don’t write the projects readmes in plain text or markdown format. I like to write them as org-mode files – given the superiority of the format.

Sourcehut provides an API to set a repositories readme text, as is explained here. Authorization is done with an OAuth2 bearer token. I put this token in my encrypted $HOME/.netrc.gpg, so that I can use emacs netrc handling code to securly get to the token without leaving it accessible.

The repository is identified by a unique numerical id. The following function uses a synchronous request.el call to fetch and return the id.

(defun tom/srht-repo-id (name)
    "Return the unique numerical id associated with every sourehut repository."
    (interactive "sRepo name: ")
    (let* ((srht (netrc-machine (netrc-parse "~/.netrc.gpg") "repo.git.sr.ht"))
           (srht-token (netrc-get srht "password"))
           (oauth2-token (concat "Bearer " srht-token))
           (id (assoc-default
            'id
            (assoc-default
             'repositoryByName
             (assoc-default
              'data
              (request-response-data
               (request
                 "https://git.sr.ht/query"
                 :params `(("query" . ,(concat "query { repositoryByName (name:\"" name "\") { id } }")))
                 :type "GET"
                 :headers `(("Authorization" . ,oauth2-token))
                 :parser 'json-read
                 :sync t
                 :timeout 2
                 :error (cl-function (lambda (&key error-thrown &allow-other-keys) (message "Error %S" error-thrown)))
                 )))))))
      (if (called-interactively-p)
          (message "Id: %S" id)
        id)))

Once we have the id we can use the following function to export the current buffer and set the resulting html as readme for the project.

(defun tom/srht-set-readme (id)
    "Export the current file to html and set the result as readme for the
  sourcehut repo identified by ID."
    (interactive "sRepo id: ")
    (let* ((srht (netrc-machine (netrc-parse "~/.netrc.gpg") "git.sr.ht"))
           (srht-token (netrc-get srht "password"))
           (oauth2-token (concat "Bearer " srht-token))
           (readme.html (org-export-as (org-export-get-backend 'html) nil nil t))
           (json-object-type 'hash-table)
           (json-array-type 'list)
           (json-key-type 'string)
           (query (make-hash-table))
           (variables (make-hash-table)))
      (puthash "id" id variables) ; the sourcehut repo id
      (puthash "readme" readme.html variables)
      (puthash
       "query"
       "mutation UpdateRepo($id: Int!, $readme: String!) {
      updateRepository(id: $id, input: { readme: $readme }) { id }
    }"
       query)
      (puthash
       "variables" variables query)
      
      (request
        "https://git.sr.ht/query"
        :type "POST"
        :data (json-serialize query)
        :headers `(("Content-Type" . "application/json") ("Authorization" . ,oauth2-token))
        :parser 'json-read
        :complete (cl-function (lambda (&key symbol-status &allow-other-keys) (message "Set: %S" symbol-status))))))

The json handling in both functions is not very sophisticated, but the fastest way for me to get it done as of now – maybe I extract the common pattern and make it a package one of these days.