Skip to content

Commit

Permalink
Merge pull request #48 from jp-berg/cli-rework
Browse files Browse the repository at this point in the history
CLI-rework
  • Loading branch information
mietzen committed Apr 28, 2024
2 parents 2d0e57e + 7aacabb commit 7360ba0
Show file tree
Hide file tree
Showing 15 changed files with 531 additions and 282 deletions.
6 changes: 5 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/Docker"
directory: "/Docker"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/workflows"
schedule:
interval: "weekly"
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
2 changes: 1 addition & 1 deletion .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]
fail-fast: false

steps:
Expand Down
4 changes: 2 additions & 2 deletions Docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ services:
# PUBLIC_IPS: "1.2.3.4,2001:043e::1" # Set if you got static IP's
# FRITZBOX: "192.168.178.1" # Use Fritz!BOX to obtain Public IP's
# SLEEP: "300" # Seconds to sleep between DynDNS runs
# IPV4_ONLY: "FALSE" # Only set IPv4 address
# IPV6_ONLY: "FALSE" # Only set IPv6 address
# IPV4: "TRUE" # Set IPv4 address
# IPV6: "FALSE" # Set IPv6 address
# DEBUG: "FALSE" # DEBUG LOGGING
restart: unless-stopped
6 changes: 2 additions & 4 deletions Docker/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from time import sleep
from porkbun_ddns import PorkbunDDNS
from porkbun_ddns.config import Config, DEFAULT_ENDPOINT

logger = logging.getLogger('porkbun_ddns')
if os.getenv('DEBUG', 'False').lower() in ('true', '1', 't'):
Expand All @@ -23,10 +24,7 @@
public_ips = [x.strip() for x in os.getenv('PUBLIC_IPS', None).split(',')]
fritzbox = os.getenv('FRITZBOX', None)

config = {
'secretapikey': os.getenv('SECRETAPIKEY'),
'apikey': os.getenv('APIKEY')
}
config = Config(DEFAULT_ENDPOINT, os.getenv('APIKEY'), os.getenv('SECRETAPIKEY'))

ipv4 = ipv6 = False
if os.getenv('IPV4', 'True').lower() in ('true', '1', 't'):
Expand Down
72 changes: 49 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ If this is not enabled, you'll see an error about your API keys being invalid, d

# CLI

**Minimum required python version: 3.10**

## Install via pip

```shell
Expand All @@ -34,44 +36,65 @@ pip install porkbun-ddns
## Usage

```Shell
$ porkbun-ddns -h
usage: porkbun-ddns [-h] [-i [PUBLIC_IPS ...]] [-f FRITZBOX] [-4 | -6] config domain [subdomain]
usage: porkbun-ddns [-h] [-c CONFIG] [-e ENDPOINT] [-pk APIKEY] [-sk SECRETAPIKEY] [-i [PUBLIC_IPS ...]] [-f FRITZBOX] [-4 | -6] [-v] [--env_only] domain [subdomains ...]

positional arguments:
config Path to config file
domain Domain to be updated
subdomain Subdomain
subdomains Subdomain(s)

options:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Path to config file (default: ~/.config/porkbun-ddns-config.json)
-e ENDPOINT, --endpoint ENDPOINT
The endpoint
-pk APIKEY, --apikey APIKEY
The Porkbun-API-key
-sk SECRETAPIKEY, --secretapikey SECRETAPIKEY
The secret API-key
-i [PUBLIC_IPS ...], --public-ips [PUBLIC_IPS ...]
Public IPs (v4 and or v6)
-f FRITZBOX, --fritzbox FRITZBOX
IP or Domain of your Fritz!Box
-4, --ipv4-only Only set/update IPv4 A Records
-6, --ipv6-only Only set/update IPv6 AAAA Records
-v, --verbose Show Debug Output
--env_only Don't use any config, get all variables from the environment
```
Examples:
### The parameter *endpoint*, *apikey*, *secretapikey*
```shell
$ porkbun-ddns "./config.json" domain.com my_subdomain
These parameter are required for each run of the program. The program will take the values for these (in this order) from:
# Multiple subdomains:
$ porkbun-ddns "./config.json" domain.com my_subdomain_1 my_subdomain_2 my_subdomain_3
1. The command-line-arguments (`-pk pk1_xxx`)
2. The environment-variables (`export PORKBUN_APIKEY='pk1_xxx'`)
3. The config-file (`apikey="pk_xxx"`)
# Set root and subdomains:
$ porkbun-ddns "./config.json" domain.com @ my_subdomain_1 my_subdomain_2 my_subdomain_3
So if a value is set through the CLI and in the file, the CLI-value will be used. This allows for a default-configuration in the config-file, whose settings can be selectively overridden through enviromnment-variables or CLI-arguments.
### Examples
```shell
# using the default config-file in ~/.config/porkbun-ddns-config.json:
$ porkbun-ddns domain.com my_subdomain
# Get config from environment variable:
# Using only environment variables:
# PORKBUN_APIKEY
# PORKBUN_SECRETAPIKEY
# PORKBUN_DDNS_ENDPOINT (Optional)
$ porkbun-ddns - domain.com my_subdomain
# PORKBUN_ENDPOINT (Optional)
$ porkbun-ddns domain.com my_subdomain --env_only
# Specific config-file:
$ porkbun-ddns domain.com my_subdomain -c "./config.json"
# Multiple subdomains:
$ porkbun-ddns domain.com my_subdomain_1 my_subdomain_2 my_subdomain_3
# Set root and subdomains:
$ porkbun-ddns domain.com @ my_subdomain_1 my_subdomain_2 my_subdomain_3
# Set IP's explicit
$ porkbun-ddns "./config.json" domain.com my_subdomain -i '1.2.3.4' '1234:abcd:0:4567::8900'
$ porkbun-ddns domain.com my_subdomain -i '1.2.3.4' '1234:abcd:0:4567::8900'

# Use Fritz!Box to obtain IP's and set IPv4 A Record only
$ porkbun-ddns "./config.json" domain.com my_subdomain -f fritz.box -4
Expand All @@ -86,7 +109,8 @@ You can set up a cron job get the full path to porkbun-ddns with `which porkbun-
`config.json` example:
```
{ "endpoint":"https://porkbun.com/api/json/v3",
{
"endpoint":"https://porkbun.com/api/json/v3",
"apikey": "pk1_xxx",
"secretapikey": "sk1_xxx"
}
Expand Down Expand Up @@ -132,18 +156,20 @@ docker run -d \
# Python
**Minimum required python version: 3.10**
```python
from pathlib import Path
from porkbun_ddns import PorkbunDDNS
from porkbun_ddns.config import Config, DEFAULT_ENDPOINT, extract_config
config = {
'secretapikey': 'YOUR-SECRETAPIKEY',
'apikey': 'YOUR-APIKEY'
}
config = Config(DEFAULT_ENDPOINT, "YOUR-APIKEY", "YOUR-SECRETAPIKEY")
porkbun_ddns = PorkbunDDNS(config, 'domain.com')
# porkbun_ddns = PorkbunDDNS('./config.json', 'domain.com')
# porkbun_ddns_ip = PorkbunDDNS('./config.json', 'domain.com', public_ips=['1.2.3.4','1234:abcd:0:4567::8900'])
# porkbun_ddns_fritz = PorkbunDDNS('./config.json', 'domain.com', fritzbox='fritz.box', ipv6=False)
# config = extract_config(Path("./config.json"))
# porkbun_ddns = PorkbunDDNS(config, 'domain.com')
# porkbun_ddns_ip = PorkbunDDNS(config, 'domain.com', public_ips=['1.2.3.4','1234:abcd:0:4567::8900'])
# porkbun_ddns_fritz = PorkbunDDNS(config, 'domain.com', fritzbox='fritz.box', ipv6=False)
porkbun_ddns.set_subdomain('my_subdomain')
porkbun_ddns.update_records()
Expand Down
1 change: 0 additions & 1 deletion porkbun_ddns/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .porkbun_ddns import PorkbunDDNS
from .porkbun_ddns import PorkbunDDNS_Error
88 changes: 45 additions & 43 deletions porkbun_ddns/cli.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,81 @@
import argparse
import logging
import sys
import os
import traceback
import logging
from porkbun_ddns import PorkbunDDNS, PorkbunDDNS_Error

from porkbun_ddns import PorkbunDDNS
from porkbun_ddns.config import extract_config, get_config_file_default
from porkbun_ddns.errors import PorkbunDDNS_Error

logger = logging.getLogger('porkbun_ddns')
logger = logging.getLogger("porkbun_ddns")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(message)s')
formatter = logging.Formatter("%(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)


def main(argv=sys.argv[1:]):
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("config", help="Path to config file")
parser.add_argument("domain", help="Domain to be updated")

parser.add_argument("-c", "--config", help=f"Path to config file "
f"(default: {get_config_file_default()})",
default=get_config_file_default())
parser.add_argument("-e", "--endpoint", help="The endpoint")
parser.add_argument("-pk", "--apikey", help="The Porkbun-API-key")
parser.add_argument("-sk", "--secretapikey", help="The secret API-key")

subdomains = parser.add_mutually_exclusive_group()
subdomains.add_argument('subdomains', nargs='*',
subdomains.add_argument("subdomains", nargs="*",
default=None, help="Subdomain(s)")

public_ips = parser.add_mutually_exclusive_group()
public_ips.add_argument('-i', '--public-ips', nargs='*',
public_ips.add_argument("-i", "--public-ips", nargs="*",
default=None, help="Public IPs (v4 and or v6)")

fritzbox = parser.add_mutually_exclusive_group()
fritzbox.add_argument('-f', '--fritzbox', default=None,
fritzbox.add_argument("-f", "--fritzbox", default=None,
help="IP or Domain of your Fritz!Box")

ip = parser.add_mutually_exclusive_group()
ip.add_argument('-4', '--ipv4-only', action='store_true',
ip.add_argument("-4", "--ipv4-only", action="store_true",
help="Only set/update IPv4 A Records")
ip.add_argument('-6', '--ipv6-only', action='store_true',
ip.add_argument("-6", "--ipv6-only", action="store_true",
help="Only set/update IPv6 AAAA Records")

verbose = parser.add_mutually_exclusive_group()
verbose.add_argument('-v', '--verbose', action='store_true',
verbose.add_argument("-v", "--verbose", action="store_true",
help="Show Debug Output")

if argv and len(argv) == 1:
parser.print_help()
exit(1)
env_only = parser.add_mutually_exclusive_group()
env_only.add_argument("--env_only", action="store_true",
help="Don't use any config, "
"get all variables from the environment")

if not argv:
parser.print_help()
exit()

args = parser.parse_args(argv)

if args.config == "-":
try:
config = {
"apikey": os.environ["PORKBUN_APIKEY"],
"secretapikey": os.environ["PORKBUN_SECRETAPIKEY"],
}
config["endpoint"] = os.environ.get("PORKBUN_DDNS_ENDPOINT", "https://porkbun.com/api/json/v3")
except KeyError:
logger.error("Invalid config environment variables.")
else:
config = args.config

ipv4 = args.ipv4_only
ipv6 = args.ipv6_only
if not any([ipv4, ipv6]):
ipv4 = ipv6 = True

if args.verbose:
logger.setLevel(logging.DEBUG)
for handler in logger.handlers:
handler.setLevel(logging.DEBUG)

exit(1)
try:
args = parser.parse_args(argv)

if args.env_only:
args.config = None

if args.verbose:
logger.setLevel(logging.DEBUG)
for handler in logger.handlers:
handler.setLevel(logging.DEBUG)

config = extract_config(args)
ipv4 = args.ipv4_only
ipv6 = args.ipv6_only
if not any([ipv4, ipv6]):
ipv4 = ipv6 = True

porkbun_ddns = PorkbunDDNS(config=config, domain=args.domain,
public_ips=args.public_ips, fritzbox_ip=args.fritzbox,
ipv4=ipv4, ipv6=ipv6)
Expand All @@ -87,11 +87,13 @@ def main(argv=sys.argv[1:]):
porkbun_ddns.update_records()
except PorkbunDDNS_Error as e:
logger.error("Error: " + str(e))
exit(1)
except Exception as e:
logger.error("This shouldn't have happened!")
logger.error("Error: " + str(e))
logger.error(traceback.format_exc())
exit(1)


if __name__ == '__main__':
if __name__ == "__main__":
main()
Loading

0 comments on commit 7360ba0

Please sign in to comment.