Next: Compare Replace, Up: Build Tools [Index]
############################################################################### ### USER-DEPENDENT VARIABLES ### USE ENVIRONMENT VARIABLES WHENEVER POSSIBLE # NOTE: All environment variables need to be exported PRIOR to starting the # Emacs server as EDITOR in your shell startup files; otherwise, they will not # be available to Emacs. # When I moved from using Bash to Zsh, I inadvertently changed the order of # import, and started the Emacs server before importing, and caused a horrible # bug which caused the program to work on one computer but fail on another. # The absolute path to this Template file TEMPLATE := $(SYNC_ORG_TEMPLATE) # Use emacsclient as $EDITOR; make sure it is set in a shell startup file and # the server has been started. EMACS := $(EMACS) EDITOR := $(EDITOR) # User’s personal GitHub token for authentication to GitHub # DO NOT HARD-CODE THIS VALUE GITHUB_TOKEN := $(GITHUB_TOKEN) # The AWS Command Line Interface (AWS CLI) is an open source tool # that enables you to interact with AWS services using commands in # your command-line shell. It must be present on your system. Run the 'make' # command 'install-aws-cli' to install it if you do not have it. Be sure to # run 'aws configure' after installing it. This will place your AWS # credentials into ~/.aws/credentials. AWS := aws S3 := $(AWS) s3 CFD := $(AWS) cloudfront ### END OF USER-DEPENDENT VARIABLES ############################################################################### ### MAKE-GENERATED VARIABLES ### TOOLS & RESOURCES # resources is a directory holding static resources for the project; # resources is created as a subdirectory of every new project. # resource/tools is a directory holding tangled scripts, such as cmprpl # resources/images is a directory holding jpg and png image files RESOURCES := resources TOOLS := $(RESOURCES)/tools IMAGES := $(RESOURCES)/images CMPRPL := $(TOOLS)/cmprpl ### PROJ AND ORG # ORG is the name of this Org file with extension .org # PROJ is the project name---the Org file name without extension. ### NOTE: there can be only one Org file in the project directory; # so far this has not been a problem, but it might be. PWD := $(shell pwd) ORG := $(shell ls *.org) PROJ := $(basename $(ORG)) ### NOTE: S is needed only for the Template file because of the way it is nested # one level deep in the Templates GitHub repo, which uses the plural form # of Templates, whereas this file uses the singular form, Template. So when # the homepage link is updated, the curl command must be told to use the plural # form. This is obviously a hack only for my own use and can be removed once # I clean up this anomaly. ifeq ($(PROJ),$(basename $(notdir $(TEMPLATE)))) S := s endif # The AWS S3 bucket to use to store the html source file; it is found at the # key #+bucket towards the beginning of the file and should include the appropriate # suffix (.com, .net, .org, etc) BUCKET := $(shell $(EDITOR) --eval \ '(with-current-buffer (find-file-noselect "$(ORG)") \ (save-excursion \ (goto-char (point-min)) \ (re-search-forward "^\#[+]bucket:\\(.*\\)$$" nil t) \ (match-string-no-properties 1)))') S3_BUCKET := s3://$(BUCKET) # Buckets set up to serve static web sites from S3 can use either http # or https protocols; some http protocols will automatically redirect # to https; however, some only use http. I would like to accomodate # both, and so this code finds the url's that are in my Cloudfront # account, which presumably will serve https. If the url is not here, # then this must be set up to serve http instead. HTTP_S := $(shell $(CFD) list-distributions | perl -MJSON::PP -e \ '$$/=""; \ my @urls = (); \ my $$json=JSON::PP->new->decode(<STDIN>); \ for my $$item ( @{$$json->{"DistributionList"}{"Items"}} ) { \ push @urls, @{$$item->{"Aliases"}{"Items"}}; \ } \ my $$found = grep { /'$(BUCKET)'/ } @urls; \ print "http", ($$found ? "s" : "");') HTTPS_BUCKET := https://$(BUCKET) ### DIR, SRC # DIR is the .info name found at '#+texinfo_filename:<DIR>.info' (at # the bottom of this file in the export configuration settings) # without its extension, used as the INFO filename and the name of the # HTML export directory; this code uses the lowercased PROJ name if # there is no '#+texinfo_filename'. # SRC is HTML directory based upon the DIR name #DIR := $(shell $(EDITOR) --eval \ # '(with-current-buffer (find-file-noselect "$(ORG)") \ # (save-excursion \ # (goto-char (point-min)) \ # (re-search-forward "^\#[+]\\(?:texinfo_filename\\|TEXINFO_FILENAME\\):\\(.*\\).info$$" nil t) \ # (match-string-no-properties 1)))') DIR := $(shell sed -E -n "/^\#\+texinfo_filename/s/^.*:(.*)\.info$$/\1/p" $(ORG)) ifeq ($(DIR),$(EMPTY)) DIR := $(shell echo $(PROJ) | tr "[:upper:]" "[:lower:]") endif SRC := $(DIR)/ ### VERS: v1.2.34/ # VERS is the version number of this Org document. # When sync is run after the version number has been updated, then VERS # picks up the newly-changed value. VERS used to be staticly imbedded # when the Makefile was tangled, but it needs to be dynamic for # development. # QUERY: should this number be formatted like this, or should it be just the numbers? # The reason it includes them is the S3PROJ obtains the name from the S3 bucket, and # it includes them. But it only includes them because I have made it so. Not a good # reason just by itself. The ending slash is not actually a part of the version, but # comes from the way the 'aws2 ls' command returns its values. So VERS should probably # not include the trailing slash, although it doesn’t hurt anything. VERS := v$(shell $(EDITOR) --eval \ '(with-current-buffer (find-file-noselect "$(ORG)") \ (save-excursion \ (goto-char (point-min)) \ (re-search-forward "^\#[+]\\(?:macro\\|MACRO\\):version Version \\(\\(?:[[:digit:]]+[.]?\\)\\{3\\}\\)") \ (match-string-no-properties 1)))')/ ### AWS # PROJ_LIST contains the list of projects currently uploaded to # the S3 bucket; each item contains the name of the project and its # current version. # Created function using elisp instead of the shell. # This variable contains an elisp list of strings of the form '("proj1-v1.2.3/" "proj2-v4.5.6/" ...)' # However, when it prints to the shell, the quotes are lost. # Need to make sure elisp's variable 'exec-path contains the proper $PATH instead of adding to 'exec-path. PROJ_LIST := $(shell $(EDITOR) --eval \ "(progn \ (require (quote seq)) (add-to-list (quote exec-path) (quote \"/usr/local/bin\")) \ (seq-map (lambda (s) (replace-regexp-in-string \"^\s+PRE \" \"\" s)) \ (seq-filter (lambda (s) (string-match-p (regexp-quote \" PRE \") s)) \ (process-lines \"$(AWS)\" \"s3\" \"ls\" \"$(S3_BUCKET)\"))))") ### S3PROJ # The name of the current project as obtained from S3: 'proj-v1.2.34/' # If there is no current project in the S3 bucket, then assign a value equal to # the Org project and version instead. It is set to the project if found, and # NO if not found, then updated in the ifeq block below. S3PROJ := $(shell $(EDITOR) --eval \ '(let ((proj (seq-find (lambda (s) (string-match-p "$(DIR)" s)) (quote $(PROJ_LIST))))) \ (or proj (quote NO)))') ### PROJINS3 # is used by make sync; this allows the index.html file to be generated the first # time the project is synced. It is set to NO if this project is not currently in an # S3 bucket, and it is set to YES if it is. PROJINS3 := ### S3VERS # The version of this project currently installed in the S3 bucket: 'v1.2.34/' # If there is no current version in the S3 bucket, then assign the version from # this Org file instead. S3VERS := # Update S3PROJ, S3VERS, and PROJINS3 ifeq ($(S3PROJ), NO) S3PROJ := $(DIR)-$(VERS) S3VERS := $(VERS) PROJINS3 := NO else S3VERS := $(subst $(DIR)-,,$(S3PROJ)) PROJINS3 := YES endif ### GITHUB # USER is the current user's GitHub login name. # The user name used to be statically embedded into the Makefile # during tangle, but in an effort to make the Makefile dynamically # indepedent, dynamic code has replaced the static code. The code # that placed the static name in the Makefile was a 'node' script that # ran in a separate Org process during tangle. An unfortunate fact of # 'make' is that 'make' strips the quote marks from the string # obtained from the 'curl' command when the 'make shell' command # returns the string. This makes the string malformed JSON and # unparsable by most JSON parsers, including 'node’. However, # 'perl'’s core module JSON::PP (but not JSON::XS) has facilities to # parse very malformed JSON strings. Therefore, this dynamic code # uses 'perl' and the core module JSON::PP to parse the 'curl' string # into a 'perl' JSON object which can return the login name. This # code should work with any version of 'perl' without having to # install any modules. USER := $(shell \ curl -sH "Authorization: token $(GITHUB_TOKEN)" https://api.github.com/user \ | \ perl -MJSON::PP -e \ '$$/ = ""; \ my $$json = JSON::PP->new->loose->allow_barekey->decode(<STDIN>); \ print $$json->{login};' \ ) SAVE := resources ### TEXINFO TEXI := $(PROJ).texi INFO := $(DIR).info INFOTN := $(shell $(EDITOR) --eval "(file-truename \"$(INFO)\")") PDF := $(PROJ).pdf INDEX := index.html HTML := $(DIR)/$(INDEX) DIR_OLD := $(DIR)-old ### AWS S3 DST_OLD := $(S3_BUCKET)/$(S3PROJ) DST_NEW := $(S3_BUCKET)/$(DIR)-$(VERS) EXCL_INCL := --exclude "*" --include "*.html" INCL_IMAGES := --exclude "*" --include "*.jpg" --include "*.png" GRANTS := --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers S3SYNC := $(S3) sync --delete $(EXCL_INCL) $(SRC) $(DST_OLD) $(GRANTS) S3MOVE := $(S3) mv --recursive $(DST_OLD) $(DST_NEW) $(GRANTS) S3COPY := $(S3) cp $(INDEX) $(S3_BUCKET) $(GRANTS) S3REMOVE := $(S3) rm $(S3_BUCKET)/$(S3PROJ) --recursive S3IMAGESYNC := $(S3) sync $(INCL_IMAGES) $(IMAGES) $(S3_BUCKET)/$(IMAGES) $(GRANTS) ############################################################################### default: check texi info html pdf PHONY: default all check values boot \ texi info html pdf \ open-org open-texi open-html open-pdf \ clean dist-clean wiped-clean \ help sync update delete-proj \ install-aws-cli \ index-html upload-index-html values: check @printf "$${BLUE}Values...$${CLEAR}\n" @echo TEMPLATE: $(TEMPLATE) @echo EDITOR: $(EDITOR) @echo USER: $(USER) @echo PWD: $(PWD) @echo ORG: $(ORG) @echo TEXI: $(TEXI) @echo INFO: $(INFO) @ECHO INFOTN: $(INFOTN) @echo BUCKET: $(BUCKET) @echo PROJ: $(PROJ) $S @echo S3_BUCKET: $(S3_BUCKET) @echo HTTP_S: $(HTTP_S) @echo HTTPS_BUCKET: $(HTTPS_BUCKET) @echo VERS: $(VERS) @echo S3PROJ: $(S3PROJ) @echo S3VERS: $(S3VERS) @echo DIR: $(DIR) @echo DIR_OLD: $(DIR_OLD) @echo SRC: $(SRC) @echo DST_OLD: $(DST_OLD) @echo DST_NEW: $(DST_NEW) @echo PROJ_LIST: "$(PROJ_LIST)" @echo PROJINS3: $(PROJINS3) check: @printf "$${BLUE}Checking dependencies...$${CLEAR}\n" @[[ -z $(BUCKET) ]] && \ { printf "$${RED}$(BUCKET) $${CYAN}must be set.$${CLEAR}\n"; exit 1; } || \ printf "$${CYAN}BUCKET: $${GREEN}$(BUCKET)$${CLEAR}\n"; @[[ -z $${GITHUB_TOKEN} ]] && \ { printf "$${RED}GITHUB_TOKEN $${CYAN}must be set.$${CLEAR}\n"; exit 1; } || \ printf "$${CYAN}GITHUB_TOKEN: $${GREEN}SET$${CLEAR}\n"; @[[ (-d ~/.aws) && (-f ~/.aws/credentials) && (-f ~/.aws/config) ]] && \ printf "$${CYAN}AWS credentials and config: $${GREEN}SET$${CLEAR}\n" || \ { printf "$${RED}~/.aws 'credentials' and 'config' must be set.$${CLEAR}\n"; exit 1; } @[[ "$(shell $(EDITOR) --eval '(member (quote texinfo) org-export-backends)')" = "(texinfo)" ]] && \ printf "$${CYAN}Texinfo backend: $${GREEN}INSTALLED.$${CLEAR}\n" || \ { printf "$${YELLOW}Texinfo backend:$${CLEAR} $${RED}NOT INSTALLED; it must be installed.$${CLEAR}\n"; exit 1; } @[[ $(shell $(EDITOR) --eval '(symbol-value org-confirm-babel-evaluate)') == "t" ]] && \ { printf "$${YELLOW}org-confirm-babel-evaluate:$${CLEAR} $${RED}T; set to NIL.$${CLEAR}\n"; exit 1; } || \ printf "$${CYAN}org-confirm-babel-evaluate: $${GREEN}OFF.$${CLEAR}\n\n" open-org: $(ORG) @$(EDITOR) -n $(ORG) $(ORG): @echo 'THERE IS NO $(ORG) FILE!!!' exit 1 texi: $(TEXI) $(TEXI): $(ORG) @echo Making TEXI... @$(EDITOR) -u --eval \ "(with-current-buffer (find-file-noselect \"$(ORG)\" t) \ (save-excursion \ (org-texinfo-export-to-texinfo)))" @echo Done making TEXI. open-texi: texi @$(EDITOR) -n $(TEXI) info: $(INFO) $(INFO): $(TEXI) @echo Making INFO... @makeinfo -o $(INFO) $(TEXI) @$(EDITOR) -u -eval \ "(when (get-buffer \"$(INFO)\") \ (with-current-buffer (get-buffer \"$(INFO)\") \ (revert-buffer t t t)))" @echo Done making INFO. open-info: info @$(EDITOR) -u -eval \ "(if (get-buffer \"*info*\") \ (with-current-buffer (get-buffer \"*info*\") \ (when (not (string= \"(symbol-value (quote Info-current-file))\" \"$(INFOTN)\")) \ (info \"$(INFOTN)\")) \ (revert-buffer t t t)) \ (info \"$(INFOTN)\"))" html: $(HTML) $(HTML): $(TEXI) @echo Making HTML INFO.. @makeinfo --html -o $(DIR) $(TEXI) @echo Done making HTML. $(CMPRPL) $(DIR) $(DIR_OLD) open-html: html @open $(HTML) # If pdftexi2dvi produces an error, it may still produce a viable PDF; # therefore, use --tidy. If it produces an error, try to link the PDF; # if it does not produce an error, the PDF will be added to the top dir # and there will be no attempt to link. pdf: $(PDF) $(PDF): $(TEXI) @echo Making PDF INFO... @-pdftexi2dvi --quiet --build=tidy $(TEXI) || ln -s $(PROJ).t2d/pdf/build/$(PDF) $(PDF) @echo Done making PDF. open-pdf:pdf @open $(PDF) tangle: $(ORG) @$(EDITOR) -u --eval "(org-babel-tangle)" @echo Done tangling sync: $(HTML) @echo Syncing version $(VERS) onto $(S3VERS)... $(S3SYNC) $(S3IMAGESYNC) @echo Done syncing. [[ $(VERS) != $(S3VERS) ]] && { echo Moving...; $(S3MOVE); echo Done moving.; make homepage; } || : [[ $(PROJINS3) = "NO" ]] && make homepage || : # This is a target-specific variable for updating the “description” # key on the GitHub repo page with the current version number. It # first makes a curl call to the GitHub project repo, finds the # “description” line, pulls out the description only (leaving the old # version) and then prints the value with the current version number. # This value is used by the “homepage:” target in the PATCH call. # This method is arguably harder to code but faster to run than using # Perl with the JSON::PP module. homepage: description = $(shell \ curl -s \ -H "Authorization: token $(GITHUB_TOKEN)" \ https://api.github.com/repos/$(USER)/$(PROJ)$S | \ (perl -ne 'if (/^\s*\"description\":\s*\"(.*): v(?:(?:[[:digit:]]+[.]?){3})/) {print $$1}')) ### NOTE the use of the S variable at the end of PROJ; this is to handle # the singular case of the GitHub repo using the plural form, Templates # whereas the the Template.org file uses the singular form. homepage: $(ORG) upload-index-html @echo Updating homepage... @echo DESCRIPTION: $(description) @echo VERS: $(VERS) @curl -i \ -H "Authorization: token $(GITHUB_TOKEN)" \ -H "Content-Type: application/json" \ -X PATCH \ -d "{\"homepage\":\"$(HTTPS_BUCKET)/$(DIR)-$(VERS)\",\ \"description\":\"$(description): $(VERS)\"}" \ https://api.github.com/repos/$(USER)/$(PROJ)$S @echo Done updating homepage. delete-proj: @echo Deleting project $(PROJ)... @curl -i \ -H "Authorization: token $(GITHUB_TOKEN)" \ -H "Accept: application/vnd.github.v3+json" \ -X DELETE \ https://api.github.com/repos/$(USER)/$(PROJ)$S @$(S3REMOVE) @make dist-clean @make upload-index-html @$(EDITOR) -u --eval "(kill-buffer \"$(ORG)\")" @rm -rf "../$(PROJ)" @echo Done deleting project. index-html: $(INDEX) $(INDEX): $(ORG) @echo making index.html... $(EDITOR) --eval \ "(with-current-buffer (find-file-noselect \"$(ORG)\") \ (save-excursion \ (org-link-search \"#project-index-title\") \ (org-export-to-file (quote html) \"index.html\" nil t)))" @echo Done making index.html. upload-index-html: $(INDEX) @echo Uploading index.html... $(S3COPY) @echo Done uploading index.html install-aws-cli: curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" && \ sudo installer -pkg AWSCLIV2.pkg -target / && \ which aws && aws --version rm -rf AWSCLIV2.pkg clean: @echo Cleaning... -@rm *~ 2>/dev/null -@for file in *.??*; \ do \ ext=$${file#$(PROJ).}; \ [[ ! $${ext} =~ org|texi|info|pdf|html ]] && rm -rv $${file}; \ done dist-clean: clean @echo Dist Cleaning... @${EDITOR} -u --eval \ "(kill-buffer \"$(ORG)\")" -@rm -rf *.{texi*,info*,html*,pdf*} $(DIR) $(TOOLS) -@for dir in *; \ do \ [ -d $$dir -a $$dir != "$(DIR_OLD)" -a $$dir != $(SAVE) ] && \ rm -vr $$dir; \ done wipe-clean: dist-clean @echo Wipe Clean... -@rm -rf Makefile Readme.md $(DIR_OLD) @git checkout Makefile README.md git-ready: dist-clean git checkout Makefile git checkout README.md git status help: @echo '"make boot" tangles all of the files in Template' @echo '"make default" makes the .texi file, the .info file, \ the html files, and the .pdf file.' @echo @echo '"make check" checks for prerequistes' @echo '"make values" runs check and prints variable values' @echo @echo '"make texi" makes the .texi file' @echo '"make info" makes the .info file' @echo '"make html" makes the html distribution in a subdirectory' @echo '"make pdf" makes the .pdf file' @echo @echo '"make open-org" opens the ORG program using emacsclient for editing' @echo '"make open-texi" opens the .texi file using emacsclient for review' @echo '"make open-html" opens the distribution index.html file \ in the default web browser' @echo '"make open-pdf" opens the .pdf file' @echo @echo '"make sync" syncs the html files in the AWS S3 bucket BUCKET; \ you must have your AWS S3 bucket name in the env var AWS_S3_BUCKET; \ You must have your AWS credentials installed in ~/.aws/credentials' @echo @echo '"make install-aws-cli" installs the "aws cli v2" command-line tools' @echo 'You also need to run "aws configure" and supply your Access Key and Secret Access Key' @echo @echo '"make clean" removes the .texi, .info, and backup files ("*~")' @echo '"make dist-clean" cleans, removes the html distribution, \ and removes the build directory' @echo '"make wipe-clean" wipes clean the directory, including old directories' @echo @echo '"make delete-proj" deletes the project from the file system, GitHub and AWS'
Next: Compare Replace, Up: Build Tools [Index]