feat: POSIX compatibility

This commit is contained in:
ge 2022-08-28 11:29:13 +03:00
parent 36b2964e7f
commit ca762cf21c

338
piglet
View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/sh
# piglet - Porkbun DNS API client. # piglet - Porkbun DNS API client.
# #
@ -27,123 +27,123 @@
# #
# For more information, please refer to <http://unlicense.org/> # For more information, please refer to <http://unlicense.org/>
piglet_version='0.1' piglet_version='0.1.1'
piglet_conf="${HOME}/.config/piglet.conf" piglet_conf="${HOME}/.config/piglet.conf"
print_help() { print_help() {
cat <<- EOF cat <<- 'EOF'
Porkbun DNS API client. Porkbun DNS API client.
/\\ ____ /\\ /\ ____ /\
\\/ \\/ <OINK!> \/ \/ <OINK!>
| *(00)* | / | *(00)* | /
\\ /&" \ /&"
|_|--|_| |_|--|_|
Usage: piglet [-cdjhv] command [arg=value ...] Usage: piglet [-cdjhv] command [arg=value ...]
Options: Options:
-c, --config path to configuration file [default: ~/.config/piglet.conf] -c, --config path to configuration file [default: ~/.config/piglet.conf]
-d, --domain domain name on which operations will be performed. -d, --domain domain name on which operations will be performed.
-j, --json raw JSON output. -j, --json raw JSON output.
-h, --help print this help message and exit. -h, --help print this help message and exit.
-v, --version print version and exit. -v, --version print version and exit.
Commands: Commands:
create Create a DNS record. create Create a DNS record.
edit Edit a DNS record by Domain and ID. edit Edit a DNS record by Domain and ID.
delete Delete a specific DNS record by ID. delete Delete a specific DNS record by ID.
retrieve Retrieve all DNS records or a single record by ID. retrieve Retrieve all DNS records or a single record by ID.
config Setup configuration file. config Setup configuration file.
Use 'piglet command --help' to see detailed help. Use 'piglet command --help' to see detailed help.
For more info, please refference to original API Documentation: For more info, please refference to original API Documentation:
<https://porkbun.com/api/json/v3/documentation> <https://porkbun.com/api/json/v3/documentation>
This software is not affiliated with Porkbun LLC <https://porkbun.com/> This software is not affiliated with Porkbun LLC <https://porkbun.com/>
License: The Unlicense <http://unlicense.org/> License: The Unlicense <http://unlicense.org/>
EOF EOF
exit 0 exit 0
} }
print_help::create() { print_help_create() {
cat <<- EOF cat <<- EOF
Create a DNS record. Create a DNS record.
Usage: piglet [options...] create [arg=value ...] Usage: piglet [options...] create [arg=value ...]
Arguments: Arguments:
name (optional) name (optional)
The subdomain for the record being created, not including The subdomain for the record being created, not including
the domain itself. Leave blank to create a record on the root domain. the domain itself. Leave blank to create a record on the root domain.
Use '*' to create a wildcard record. Use '*' to create a wildcard record.
type type
The type of record being created. Valid types are: A, MX, CNAME, ALIAS, The type of record being created. Valid types are: A, MX, CNAME, ALIAS,
TXT, NS, AAAA, SRV, TLSA, CAA. TXT, NS, AAAA, SRV, TLSA, CAA.
content content
The answer content for the record. The answer content for the record.
ttl (optional) [default: 600 seconds] ttl (optional) [default: 600 seconds]
The time to live in seconds for the record The time to live in seconds for the record
prio (optional) [default: 0] prio (optional) [default: 0]
The priority of the record for those that support it. The priority of the record for those that support it.
EOF EOF
exit 0 exit 0
} }
print_help::edit() { print_help_edit() {
cat <<- EOF cat <<- EOF
Edit a DNS record by Domain and ID. Edit a DNS record by Domain and ID.
Usage: piglet [options...] edit [arg=value ...] Usage: piglet [options...] edit [arg=value ...]
Arguments: Arguments:
id id
ID of DNS record to edit. ID of DNS record to edit.
name (optional) name (optional)
The subdomain for the record being created, not including The subdomain for the record being created, not including
the domain itself. Leave blank to create a record on the root domain. the domain itself. Leave blank to create a record on the root domain.
Use '*' to create a wildcard record. Use '*' to create a wildcard record.
type type
The type of record being created. Valid types are: A, MX, CNAME, ALIAS, The type of record being created. Valid types are: A, MX, CNAME, ALIAS,
TXT, NS, AAAA, SRV, TLSA, CAA. TXT, NS, AAAA, SRV, TLSA, CAA.
content content
The answer content for the record. The answer content for the record.
ttl (optional) [default: 600 seconds] ttl (optional) [default: 600 seconds]
The time to live in seconds for the record The time to live in seconds for the record
prio (optional) [default: 0] prio (optional) [default: 0]
The priority of the record for those that support it. The priority of the record for those that support it.
EOF EOF
exit 0 exit 0
} }
print_help::delete() { print_help_delete() {
cat <<- EOF cat <<- EOF
Delete a specific DNS record by Doman and ID.. Delete a specific DNS record by Doman and ID..
Usage: piglet [options...] delete [arg=value ...] Usage: piglet [options...] delete [arg=value ...]
Arguments: Arguments:
id id
DNS record ID. DNS record ID.
EOF EOF
exit 0 exit 0
} }
print_help::retrieve() { print_help_retrieve() {
cat <<- EOF cat <<- EOF
Retrieve all editable DNS records associated with a domain or a single record Retrieve all editable DNS records associated with a domain or a single record
for a particular record ID. for a particular record ID.
Usage: piglet [options...] retrieve [arg=value ...] Usage: piglet [options...] retrieve [arg=value ...]
Retrieve options: Retrieve options:
-h, --help print this help message and exit. -h, --help print this help message and exit.
Arguments: Arguments:
id (optional) id (optional)
DNS record ID. DNS record ID.
EOF EOF
exit 0 exit 0
} }
@ -151,69 +151,51 @@ print_help::retrieve() {
# API methods # # API methods #
# ----------------------------------------- # # ----------------------------------------- #
api::create() { api_create() {
# Create DNS record. # Create DNS record.
# See: https://porkbun.com/api/json/v3/documentation#DNS%20Create%20Record # See: https://porkbun.com/api/json/v3/documentation#DNS%20Create%20Record
# Usage: api::create domain name type content ttl prio # Usage: api_create domain name type content ttl prio
local Domain="$1" curl -sS -X POST https://porkbun.com/api/json/v3/dns/create/"$1" \
local Name="$2"
local Type="$3"
local Content="$4"
local TTL="$5"
local Prio="$6"
curl -sS -X POST https://porkbun.com/api/json/v3/dns/create/$Domain \
--data '{ --data '{
"secretapikey": "'"$Secret_API_Key"'", "secretapikey": "'"$Secret_API_Key"'",
"apikey": "'"$API_Key"'", "apikey": "'"$API_Key"'",
"name": "'"$Name"'", "name": "'"$2"'",
"type": "'"$Type"'", "type": "'"$3"'",
"content": "'"$Content"'", "content": "'"$4"'",
"ttl": "'"$TTL"'", "ttl": "'"$5"'",
"prio": "'"$Prio"'" "prio": "'"$6"'"
}' }'
echo # just print new line echo # just print new line
} }
api::edit() { api_edit() {
# Edit DNS record by Domain and ID. # Edit DNS record by Domain and ID.
# See: https://porkbun.com/api/json/v3/documentation#DNS%20Edit%20Record%20by%20Domain%20and%20ID # See: https://porkbun.com/api/json/v3/documentation#DNS%20Edit%20Record%20by%20Domain%20and%20ID
# Usage: api::edit doamin id name type content ttl prio # Usage: api_edit doamin id name type content ttl prio
local Domain="$1" curl -sS -X POST https://porkbun.com/api/json/v3/dns/edit/"$1"/"$2" \
local ID="$2"
local Name="$3"
local Type="$4"
local Content="$5"
local TTL="$6"
local Prio="$7"
curl -sS -X POST https://porkbun.com/api/json/v3/dns/edit/$Domain/$ID \
--data '{ --data '{
"secretapikey": "'"$Secret_API_Key"'", "secretapikey": "'"$Secret_API_Key"'",
"apikey": "'"$API_Key"'", "apikey": "'"$API_Key"'",
"name": "'"$Name"'", "name": "'"$3"'",
"type": "'"$Type"'", "type": "'"$4"'",
"content": "'"$Content"'", "content": "'"$5"'",
"ttl": "'"$TTL"'", "ttl": "'"$6"'",
"prio": "'"$Prio"'" "prio": "'"$7"'"
}' }'
echo # just print new line echo # just print new line
} }
api::delete() { api_delete() {
# Delete DNS record. # Delete DNS record.
# See: https://porkbun.com/api/json/v3/documentation#DNS%20Delete%20Record%20by%20Domain%20and%20ID # See: https://porkbun.com/api/json/v3/documentation#DNS%20Delete%20Record%20by%20Domain%20and%20ID
# Usage: api::delete domain id # Usage: api_delete domain id
local Domain="$1" curl -sS -X POST https://porkbun.com/api/json/v3/dns/delete/"$1"/"$2" \
local ID="$2"
curl -sS -X POST https://porkbun.com/api/json/v3/dns/delete/$Domain/$ID \
--data '{ --data '{
"secretapikey": "'"$Secret_API_Key"'", "secretapikey": "'"$Secret_API_Key"'",
"apikey": "'"$API_Key"'" "apikey": "'"$API_Key"'"
@ -221,23 +203,19 @@ api::delete() {
echo # just print new line echo # just print new line
} }
api::retrieve() { api_retrieve() {
# Retrieve DNS records. # Retrieve DNS records.
# See: https://porkbun.com/api/json/v3/documentation#DNS%20Retrieve%20Records%20by%20Domain%20or%20ID # See: https://porkbun.com/api/json/v3/documentation#DNS%20Retrieve%20Records%20by%20Domain%20or%20ID
# Usage: api::retrieve domain id # Usage: api_retrieve domain id
# Function arguments:
# $1 domain name
# $2 DNS record ID
local target= _target="$1"
target="$1" if [ "$#" -eq 2 ]; then
if [[ "$1" && "$2" ]]; then target="$1/$2"
target="${1}/${2}"
fi fi
curl -sS -X POST https://porkbun.com/api/json/v3/dns/retrieve/$target \ curl -sS -X POST https://porkbun.com/api/json/v3/dns/retrieve/"$target" \
--data '{ --data '{
"secretapikey": "'"$Secret_API_Key"'", "secretapikey": "'"$Secret_API_Key"'",
"apikey": "'"$API_Key"'" "apikey": "'"$API_Key"'"
@ -251,37 +229,44 @@ api::retrieve() {
_init_piglet() { _init_piglet() {
# Source configuration file # Source configuration file
# shellcheck disable=SC1090
. "$piglet_conf" . "$piglet_conf"
[[ "$API_Key" && "$Secret_API_Key" ]] || { if [ -z "$API_Key" ] || [ -z "$Secret_API_Key" ]; then
echo "Bad API credentials. Please check the ${piglet_conf}. " \ echo "Bad API credentials. Please check the ${piglet_conf}. " \
"Run 'piglet config' to create new config file." >&2; exit 1 "Run 'piglet config' to create new config file." >&2; exit 1
} fi
[ "$domain" ] || { echo 'Domain name is not set' >&2; exit 1; }
if [ -z "$domain" ]; then
echo 'Domain name is not set' >&2; exit 1;
fi
} }
piglet_config() { piglet_config() {
echo -e "Enter Porkbun DNS API keys. Press ^C to cancel.\n" \ mkdir -p "$HOME"/.config
"\bHelp: <https://kb.porkbun.com/article/190-getting-started-with-the-porkbun-dns-api>\n"
read -p 'API_Key: ' api_key printf '%s\n%s\n' \
read -p 'Secret_API_Key: ' secret_api_key "Enter Porkbun DNS API keys. Press ^C to cancel." \
"Help: https://kb.porkbun.com/article/190-getting-started-with-the-porkbun-dns-api"
printf '%s ' 'API_Key:'; read -r api_key
printf '%s ' 'Secret_API_Key:'; read -r secret_api_key
cat > "$piglet_conf" <<- EOF cat > "$piglet_conf" <<- EOF
# Porkbun DNS API credentials # Porkbun DNS API credentials
API_Key=$api_key API_Key=$api_key
Secret_API_Key=$secret_api_key Secret_API_Key=$secret_api_key
EOF EOF
echo -e "Config saved as $piglet_conf" printf 'Config saved as %s\n' "$piglet_conf"
} }
piglet_create() { piglet_create() {
local name= type= content= ttl= prio= while [ "$#" -ne 0 ]; do
while (( "$#" )); do
case "$1" in case "$1" in
-c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";; -c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";;
-d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";; -d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";;
-j|--json) raw_json=1; shift;; -j|--json) raw_json=1; shift;;
-h|--help) print_help::create;; -h|--help) print_help_create;;
name=*) name="${1##*=}"; shift;; name=*) name="${1##*=}"; shift;;
type=*) type="${1##*=}"; shift;; type=*) type="${1##*=}"; shift;;
content=*) content="${1##*=}"; shift;; content=*) content="${1##*=}"; shift;;
@ -294,18 +279,16 @@ piglet_create() {
_init_piglet _init_piglet
api::create "$domain" "$name" "$type" "$content" "$ttl" "$prio" api_create "$domain" "$name" "$type" "$content" "$ttl" "$prio"
} }
piglet_edit() { piglet_edit() {
local record_id= name= type= content= ttl= prio= while [ "$#" -ne 0 ]; do
while (( "$#" )); do
case "$1" in case "$1" in
-c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";; -c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";;
-d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";; -d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";;
-j|--json) raw_json=1; shift;; -j|--json) raw_json=1; shift;;
-h|--help) print_help::edit;; -h|--help) print_help_edit;;
id=*) record_id="${1##*=}"; shift;; id=*) record_id="${1##*=}"; shift;;
name=*) name="${1##*=}"; shift;; name=*) name="${1##*=}"; shift;;
type=*) type="${1##*=}"; shift;; type=*) type="${1##*=}"; shift;;
@ -319,18 +302,16 @@ piglet_edit() {
_init_piglet _init_piglet
api::edit "$domain" "$record_id" "$name" "$type" "$content" "$ttl" "$prio" api_edit "$domain" "$record_id" "$name" "$type" "$content" "$ttl" "$prio"
} }
piglet_delete() { piglet_delete() {
local record_id= while [ "$#" -ne 0 ]; do
while (( "$#" )); do
case "$1" in case "$1" in
-c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";; -c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";;
-d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";; -d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";;
-j|--json) raw_json=1; shift;; -j|--json) raw_json=1; shift;;
-h|--help) print_help::delete;; -h|--help) print_help_delete;;
id=*) record_id="${1##*=}"; shift;; id=*) record_id="${1##*=}"; shift;;
-*) echo "Unknown option: $1" >&2; exit 1;; -*) echo "Unknown option: $1" >&2; exit 1;;
*) echo "Unknown key: $1" >&2; exit 1;; *) echo "Unknown key: $1" >&2; exit 1;;
@ -339,18 +320,16 @@ piglet_delete() {
_init_piglet _init_piglet
api::delete "$domain" "$record_id" api_delete "$domain" "$record_id"
} }
piglet_retrieve() { piglet_retrieve() {
local json_out= record_id= while [ "$#" -ne 0 ]; do
while (( "$#" )); do
case "$1" in case "$1" in
-c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";; -c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";;
-d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";; -d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";;
-j|--json) raw_json=1; shift;; -j|--json) raw_json=1; shift;;
-h|--help) print_help::retrieve;; -h|--help) print_help_retrieve;;
id=*) record_id="${1##*=}"; shift;; id=*) record_id="${1##*=}"; shift;;
-*) echo "Unknown option: $1" >&2; exit 1;; -*) echo "Unknown option: $1" >&2; exit 1;;
*) echo "Unknown key: $1" >&2; exit 1;; *) echo "Unknown key: $1" >&2; exit 1;;
@ -359,11 +338,11 @@ piglet_retrieve() {
_init_piglet _init_piglet
json_out="$(api::retrieve "$domain" "$record_id")" json_out="$(api_retrieve "$domain" "$record_id")"
if ! hash jq 2>/dev/null; then if ! hash jq 2>/dev/null; then
raw_json=1 raw_json=1
echo -e "Install jq to enable table view." echo "Install jq to enable table view." >&2
fi fi
if [ "$raw_json" ]; then if [ "$raw_json" ]; then
@ -390,34 +369,39 @@ opts() {
# $val - option value. # $val - option value.
# $sft - value for shift. # $sft - value for shift.
if [[ "$1" =~ .+=.+ ]]; then opt="${1%%=*}"; val="${1##*=}"; sft=1
opt="${1%%=*}"; val="${1#*=}"; sft=1
elif [[ ! "$1" =~ .+=$ ]] && \ if [ "$opt" = "$val" ]; then
[ "$2" ] && [ "${2:0:1}" != "-" ] if [ -n "$2" ] && [ "$(echo "$2" | cut -c1-1)" != "-" ]; then
then val="$2"; sft=2
opt="$1"; val="$2"; sft=2 else
else unset val
opt="$1" fi
if [[ "$1" =~ .+=$ ]]; then opt="${1:0: -1}"; fi fi
echo "Missing argument for $opt" >& /dev/null; exit 1
if [ -z "$val" ]; then
printf 'Missing argument for option %s\n' "$opt" >&2; exit 1
fi fi
} }
[[ "$@" ]] || print_help if [ "$#" -eq 0 ]; then
print_help
fi
# Split combined short options, e.g. '-abc' to '-a' '-b' '-c' # Split combined short options, e.g. '-abc' to '-a' '-b' '-c'
for args in "$@"; do for args in "$@"; do
shift shift
case "$args" in case "$args" in
--*) set -- "$@" "$args";; # save long options --*) set -- "$@" "$args";; # save long options
-*) args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')" -*) args="$(echo "${args#-}" | grep -o . | xargs -I {} echo -n '-{} ')"
# shellcheck disable=SC2086
set -- "$@" $args;; set -- "$@" $args;;
*) set -- "$@" "$args";; # save positional arguments *) set -- "$@" "$args";; # save positional arguments
esac esac
done done
# Final arguments parser # Final arguments parser
while (( "$#" )); do while [ "$#" -ne 0 ]; do
case "$1" in case "$1" in
-c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";; -c|--config|--config=*) opts "$1" "$2"; piglet_conf="$val"; shift "$sft";;
-d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";; -d|--domain|--domain=*) opts "$1" "$2"; domain="$val"; shift "$sft";;