#!/usr/bin/env bash # jmp2 — publish markdown to jmp2.io # # jmp2 push [dir-or-file] publish a folder (or one file) to # --secret do not list it on your public index # --password require a password (implies --secret) # --public list it publicly (the default) # jmp2 secret [password] make an existing site secret # jmp2 public list it publicly and drop any password # jmp2 ls list your sites # jmp2 info show versions for one site # jmp2 rollback go back to an earlier version # jmp2 rm delete a site # jmp2 whoami check the current token and quota usage # jmp2 tokens list your tokens # jmp2 token-new [name] mint another token # jmp2 token-rm revoke a token # # Reads JMP2_TOKEN from the environment or ~/.jmp2/token. set -euo pipefail API="${JMP2_API:-https://jmp2.io}" EXCLUDES=() for pattern in node_modules .git .wrangler .venv __pycache__ target dist .next .DS_Store; do EXCLUDES+=(--exclude "$pattern") done token() { if [[ -n "${JMP2_TOKEN:-}" ]]; then printf '%s' "$JMP2_TOKEN"; return; fi if [[ -f "$HOME/.jmp2/token" ]]; then tr -d '[:space:]' < "$HOME/.jmp2/token"; return; fi echo "no token: set JMP2_TOKEN or write one to ~/.jmp2/token" >&2 exit 1 } req() { # req [curl args...] local method="$1" path="$2"; shift 2 curl -sS --max-time 300 -X "$method" "$API/_api$path" \ -H "Authorization: Bearer $(token)" "$@" } set_visibility() { # set_visibility [password] local slug="$1" visibility="$2" password="${3:-}" # `null` clears an existing password; omitting the key would leave it in place. local pw='null' [[ -n "$password" ]] && pw="\"$password\"" req POST "/sites/$slug/visibility" -H 'content-type: application/json' \ -d "{\"visibility\":\"$visibility\",\"password\":$pw}" } cmd="${1:-}"; shift || true case "$cmd" in push) slug="${1:?usage: jmp2 push [dir-or-file]}"; shift || true src="."; visibility=""; password="" while [[ $# -gt 0 ]]; do case "$1" in --secret) visibility="secret"; shift ;; --public) visibility="public"; shift ;; --password) password="${2:?--password needs a value}"; visibility="secret"; shift 2 ;; # Never treat an unrecognized flag as a path. An older client that # silently ignored --secret would publish publicly while the caller # believed the site was unlisted; failing loudly is the only safe # behaviour for a flag that controls who can see something. --*) echo "unknown option: $1 (try: jmp2 help)" >&2; exit 2 ;; *) src="$1"; shift ;; esac done query=""; [[ -n "$visibility" ]] && query="?visibility=$visibility" # bash 3.2 (still the default on macOS) treats "${arr[@]}" on an empty array # as an unbound variable under `set -u`, so guard the expansion. pwhdr=(); [[ -n "$password" ]] && pwhdr=(-H "X-Site-Password: $password") pw_args=(); [[ ${#pwhdr[@]} -gt 0 ]] && pw_args=("${pwhdr[@]}") if [[ -f "$src" ]]; then # A lone file becomes the site's index so the slug itself is the URL. dest="$(basename "$src")" [[ "$dest" == *.md || "$dest" == *.markdown ]] && dest="index.md" req PUT "/sites/$slug/files/$dest" --data-binary "@$src" > /dev/null [[ -n "$visibility$password" ]] && set_visibility "$slug" "$visibility" "$password" > /dev/null req POST "/sites/$slug/publish" else [[ -d "$src" ]] || { echo "no such file or directory: $src" >&2; exit 1; } # A docs folder often sits inside a project, and `push ` defaults to # the current directory, so keep the obvious build junk out of the upload. tar czf - -C "$src" "${EXCLUDES[@]}" . \ | req PUT "/sites/$slug/tarball$query" -T - ${pw_args[@]+"${pw_args[@]}"} fi ;; secret) slug="${1:?usage: jmp2 secret [password]}" set_visibility "$slug" secret "${2:-}" ;; public) slug="${1:?usage: jmp2 public }" set_visibility "$slug" public "" ;; ls) req GET "/sites" ;; info) req GET "/sites/${1:?usage: jmp2 info }" ;; rm) req DELETE "/sites/${1:?usage: jmp2 rm }" ;; whoami) req GET "/whoami" ;; tokens) req GET "/tokens" ;; token-new) req POST "/tokens" -H 'content-type: application/json' -d "{\"name\":\"${1:-cli}\"}" ;; token-rm) req DELETE "/tokens/${1:?usage: jmp2 token-rm }" ;; rollback) slug="${1:?usage: jmp2 rollback }"; version="${2:?usage: jmp2 rollback }" req POST "/sites/$slug/rollback" -H 'content-type: application/json' -d "{\"version\":$version}" ;; ""|-h|--help|help) sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//' ;; *) echo "unknown command: $cmd (try: jmp2 help)" >&2; exit 1 ;; esac