;;; vlocitemacs.el --- Vlocity Build Tool :: Emacs integration ;;; Commentary: ;; ;; Essentially the following commands are abstracted into elisp and made dynamic: ;; ;; vlocity packDeploy -propertyfile build.properties -job job.yaml -key VlocityUITemplate/Test-test ;; vlocity packExport -propertyfile build.properties -job job.yaml -key VlocityUITemplate/Test-test ;; ;; ;;; Code: (require 'transient) (require 'json) (defvar vlo/project-file-name ".vemacs.txt" "The name of the VlocitEmacs configuration file. This file exists for the purpose of locating the project folder as there is no good way of knowing you are in the root vlocity project. It also contains the list of environments to be used for deployments/exports.") (defvar vlo/project-jobfile-template "projectPath: ." "Bare-bones template to use for =job.yaml=.") ;; DONE (defun vlo/generate-project-file () "Create the project file and append it to .gitignore if file exists." (interactive) (let* ((dir (read-directory-name "Choose Project Root: ")) (project-file (concat dir vlo/project-file-name)) (job-file (concat dir "job.yaml")) (gitignore (concat dir ".gitignore"))) ;; Create job.yaml if it doesn't exist (when (not (file-exists-p job-file)) (with-temp-file job-file (insert vlo/project-jobfile-template))) ;; Create .vemacs.txt (with-temp-file project-file (insert "")) ;; Insert username into .vemacs.txt (let ((current-user (vlo/prompt-org-list))) (with-temp-file project-file (insert current-user)) ) ;; Add .vemacs.txt to .gitignore (when (file-exists-p gitignore) (append-to-file vlo/project-file-name nil gitignore)))) ;; DONE (defun vlo/prompt-org-list () "Get a list of authenticated orgs via SFDX cli and store the selected user in the project file." (interactive) (let* ((project-file (concat (vlo/project-path) vlo/project-file-name)) (temp-json-file (make-temp-file "sfdx-org-list" nil ".json" (shell-command-to-string "sfdx force:org:list --json"))) (json-object-type 'hash-table) (json-array-type 'list) (json-key-type 'string) (json (json-read-file temp-json-file)) (result (gethash "result" json)) (orgs (gethash "nonScratchOrgs" result)) (usernames '()) ) (dolist (org orgs) (add-to-list 'usernames (gethash "username" org))) (let ((current-user (completing-read "SFDX user: " usernames))) (with-temp-file project-file current-user) current-user))) ;; DONE (defun vlo/in-vlocity-project () "Check if you are currently inside a vlocity project." (if (vlo/project-path) t nil)) ;; DONE (defun vlo/project-path () "Return path to the project file, or nil. If project file exists in the current working directory, or a parent directory recursively, return its path. Otherwise, return nil." (locate-dominating-file default-directory vlo/project-file-name)) ;; DONE (defun vlo/get-project-user () "Return the user stored in the project file." (if (vlo/in-vlocity-project) (let ((project-dir (concat (vlo/project-path) vlo/project-file-name))) (with-temp-buffer (insert-file-contents project-dir) (buffer-string))) nil)) ;; DONE (defun vlo/get-jobfile-name () "The name of the job.yaml file." "job.yaml") ;; DONE (defun vlo/get-deployment-key () "Return the Name of the component dynamically from the =_DataPpack.json= file." (let ((datapack-file (expand-file-name (concat (file-name-base) "_DataPack.json")))) (if (file-exists-p datapack-file) (let* ((json-object-type 'hash-table) (json-array-type 'list) (json-key-type 'string) (json (json-read-file datapack-file)) (component-name (gethash "Name" json))) (concat "VlocityUITemplate/" component-name)) nil))) ;; DONE (defun vlo/exec-process (cmd name &optional comint) "Execute a process running CMD and use NAME to generate a unique buffer name and optionally pass COMINT as t to put buffer in `comint-mode'." (let ((compilation-buffer-name-function (lambda (mode) (format "*%s*" name)))) (message (concat "Running " cmd)) (compile cmd comint))) (defun vlo/packExport (username job &optional key) "Run the packExport command with sfdx USERNAME or alias using the JOB file. Optionally specifying KEY to export. If KEY is nil, this command will run packExport using job.yaml provided (i.e. export all)." (if (and (vlo/in-vlocity-project) (file-exists-p (concat (vlo/project-path) job))) (let ((cmd (if key (format "cd %s; vlocity packExport -sfdx.username %s -job %s -key %s" (vlo/project-path) username job key) (format "cd %s; vlocity packExport -sfdx.username %s -job %s" (vlo/project-path) username job) ))) (vlo/exec-process cmd "vlocity:retrieve" t)) (message "ERROR Exporting:: project: %s, user: %s, job: %s, key: %s" (vlo/project-path) username job key))) (defun vlo/packDeploy (username job &optional key) "Run the packDeploy command with sfdx USERNAME or alias using the JOB file. Optionally specifying KEY to export. If KEY is nil, this command will run packDeploy using job.yaml provided (i.e. deploy all)." (if (and (vlo/in-vlocity-project) (file-exists-p (concat (vlo/project-path) job))) (let ((cmd (if key (format "cd %s; vlocity packDeploy -sfdx.username %s -job %s -key %s" (vlo/project-path) username job key) (format "cd %s; vlocity packDeploy -sfdx.username %s -job %s" (vlo/project-path) username job) ))) (vlo/exec-process cmd "vlocity:retrieve" t)) (message "ERROR Deploying:: project: %s, user: %s, job: %s, key: %s" (vlo/project-path) username job key))) (defun vlo/packSearch (username job) "Run the packGetAllAvailableExports command with USERNAME and JOB file." (if (and (vlo/in-vlocity-project) (file-exists-p (concat (vlo/project-path) job))) (vlo/exec-process (format "cd %s; vlocity packGetAllAvailableExports -sfdx.username %s -job %s -type VlocityUITemplate" (vlo/project-path) username job) "vlocity:exports" t) (message "ERROR Retrieving List:: project: %s, user: %s, job: %s" (vlo/project-path) username job))) (defun vlo/createDatapack () "Description." (interactive) (message "TODO :: Creating...")) (defun vlo/search () "Description." (interactive) (message "TODO :: Searching...")) ;; DONE (defun vlo/exportThisAction () "Destructively retrieve this component." (interactive) (let ((key (vlo/get-deployment-key))) (if (yes-or-no-p "Retrieve \"%s\"? (THIS WILL OVERWRITE LOCAL CHANGES!) ") (progn (message "Retrieving \"%s\"..." key) (vlo/packExport (vlo/get-project-user) (vlo/get-jobfile-name) key)) (message "Cancelled Retrieve")))) ;; DONE (defun vlo/exportAllAction () "Description." (interactive) (if (yes-or-no-p "Retrieve ALL DataPacks in Manifest? (THIS WILL OVERWRITE LOCAL CHANGES!) ") (progn (message "Retrieving ALL DataPacks in Manifest file...") (vlo/packExport (vlo/get-project-user) (vlo/get-jobfile-name))) (message "Cancelled Retrieve"))) ;; DONE (defun vlo/deployThisAction () "Description." (interactive) (let ((key (vlo/get-deployment-key))) (if key (progn (vlo/packDeploy (vlo/get-project-user) (vlo/get-jobfile-name) key) (message "Deploying \"%s\"..." key)) (message "No DataPack file found for this component!")))) ;; DONE (defun vlo/deployAllAction () "Description." (interactive) (if (yes-or-no-p "Are you sure you wish to deploy everything? ") (vlo/packDeploy (vlo/get-project-user) (vlo/get-jobfile-name)) (message "Cancelled"))) (defun vlo/transient-action () "Dynamically choose which transient to show based on if currently in a project." (interactive) (if (vlo/in-vlocity-project) (vlo/transient-project-action) (vlo/transient-init-action))) (define-transient-command vlo/transient-init-action () "Vlocity Build Tool CLI Actions" ["Vlocity Project file not created" ("i" "Initialize Vlocity Project" vlo/generate-project-file)]) (define-transient-command vlo/transient-project-action () "Vlocity Build Tool CLI Actions" ["Create" ("c" "Create a new datapack (dynamically)" vlo/createDatapack)] ["Retrieve" ("s" "Search for new datapack" vlo/search) ("r" "refresh this datapack (destructive)" vlo/exportThisAction) ("R" "refresh all local datapacks (destructive)" vlo/exportAllAction) ] ["Deploy" ("d" "deploy this datapack" vlo/deployThisAction) ("D" "deploy all local datapacks" vlo/deployAllAction) ]) (provide 'vlocitemacs) ;;; vlocitemacs.el ends here