From ca75a8f0f3860f0ac64eaf7fdeaec422912fcd99 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sun, 8 May 2022 18:07:27 +0200 Subject: Parse command line options * Define command line options for the program * Define Make rules to build a binary * Fix system entrypoint definition * Comment out some application code to test command line option parsing --- Makefile | 11 ++++++ src/main.lisp | 28 ++++++++------- src/option.lisp | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wajir.asd | 7 ++-- 4 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 Makefile create mode 100644 src/option.lisp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..03dbb2c --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +LISP ?= sbcl + + +.PHONY: build +build: wajir + +wajir: wajir.asd lib/* src/*.lisp + $(LISP) --load wajir.asd \ + --eval '(ql:quickload :wajir)' \ + --eval '(asdf:make :wajir)' \ + --eval '(quit)' diff --git a/src/main.lisp b/src/main.lisp index 2ac7daf..938a950 100644 --- a/src/main.lisp +++ b/src/main.lisp @@ -7,14 +7,18 @@ ;; Send email to ^maildir^program^ containing message with issue metadata ;; Continue to next page - (let ((config (make-instance 'config - :login "name@example.com" - :token "atlassian-token" - :endpoint "example.atlassian.net" - :email-to "name@example.com" - :jql "project = \"FAKE\" AND watcher != currentUser() AND key > \"FAKE-100\" ORDER BY created DESC"))) - - (run config))) + (let ((config (parse-options))) + (format t "~S" config)) + + ; (let ((config (make-instance 'config + ; :login "name@example.com" + ; :token "atlassian-token" + ; :endpoint "example.atlassian.net" + ; :email-to "name@example.com" + ; :jql "project = \"FAKE\" AND watcher != currentUser() AND key > \"FAKE-100\" ORDER BY created DESC"))) + ; + ; (run config)) + ) (defun run (config) (let ((basic-auth-token (cl-base64:string-to-base64-string @@ -89,10 +93,10 @@ (when (verbose config) (format t "Watching issue ~A~%" (gethash "key" issue))) - (add-watcher - (endpoint config) - issue - :basic-auth-token basic-auth-token) + ; (add-watcher + ; (endpoint config) + ; issue + ; :basic-auth-token basic-auth-token) (if (sendmail config) (deliver-email config issue))) diff --git a/src/option.lisp b/src/option.lisp new file mode 100644 index 0000000..4d1ce19 --- /dev/null +++ b/src/option.lisp @@ -0,0 +1,104 @@ +(in-package :wajir) + +(opts:define-opts + (:name :login + :description "Jira login email address" + :long "login" + :arg-parser #'identity + :meta-var "") + (:name :token + :description "Jira API token" + :long "token" + :arg-parser #'identity + :meta-var "") + (:name :endpoint + :description "Jira site URL host (e.g. example.atlassian.net)" + :long "endpoint" + :meta-var "") + + (:name :sendmail + :description "send email command" + :long "sendmail" + :meta-var "") + (:name :email-to + :description "recipient email address" + :long "email-to" + :meta-var "
") + + (:name :verbose + :description "print verbose output" + :short #\v + :long "verbose") + + (:name :help + :description "print this help menu" + :short #\h + :long "help") + (:name :version + :description "show the program version" + :short #\V + :long "version")) + +(defmacro when-option ((options option) &body body) + "When `option` is present in `options`, run `body`." + `(let ((value (getf ,options ,option))) + (when value + ,@body))) + +(defun exit-with-error (condition exit-code) + "Print the error associated with `condition` on standard error, then exit +with code `exit-code`." + (format *error-output* "error: ~a~%" condition) + + (opts:exit exit-code)) + +(defun handle-option-error (condition) + "Handle errors related to command line options. Prints the error specified by +`condition` and exits with EX_USAGE." + (exit-with-error condition sysexits:+usage+)) + +(defun parse-options () + "Parse command line options." + (multiple-value-bind (options free-args) + (handler-bind + ((opts:unknown-option #'handle-option-error) + (opts:missing-arg #'handle-option-error) + (opts:arg-parser-failed #'handle-option-error) + (opts:missing-required-option #'handle-option-error)) + + (opts:get-opts)) + + ;; Help + (when-option (options :help) + (opts:describe + :usage-of "wajir" + :args "") + + (opts:exit sysexits:+usage+)) + + ;; Version + (when-option (options :version) + (format t "~a~%" (asdf:component-version (asdf:find-system :extreload))) + + (opts:exit sysexits:+ok+)) + + ;; If `sendmail` is given, `email-to` must be defined. + (when (not (null (getf options :sendmail))) + (when (null (getf options :email-to)) + (format *error-output* "error: `--sendmail' requires `--email-to'") + (opts:exit sysexits:+usage+))) + + ;; Error if JQL is empty + (when (null free-args) + (format *error-output* "error: missing JQL") + + (opts:exit sysexits:+usage+)) + + (make-instance 'config + :login (getf options :login) + :token (getf options :token) + :endpoint (getf options :endpoint) + :sendmail (getf options :sendmail) + :email-to (getf options :email-to) + :verbose (getf options :verbose) + :jql (first free-args)))) diff --git a/wajir.asd b/wajir.asd index 1f95cf6..e050c93 100644 --- a/wajir.asd +++ b/wajir.asd @@ -13,17 +13,20 @@ :depends-on (:cl-base64 :cl-smtp :com.inuoe.jzon - :dexador) + :dexador + :sysexits + :unix-opts) :components ((:module "src" :serial t :components ((:file "package") (:file "config") + (:file "option") (:file "email") (:file "main")))) :build-operation "program-op" :build-pathname "wajir" - :entry-point "wajir") + :entry-point "wajir:main") #+sb-core-compression (defmethod asdf:perform ((o asdf:image-op) (c asdf:system)) -- cgit v1.2.3