devnomads-cli 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/PKG-INFO +12 -3
  2. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/README.md +10 -1
  3. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/PKG-INFO +12 -3
  4. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/requires.txt +1 -1
  5. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/dncli.py +26 -6
  6. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/pyproject.toml +2 -2
  7. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_cert.py +50 -0
  8. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/LICENSE +0 -0
  9. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/SOURCES.txt +0 -0
  10. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/dependency_links.txt +0 -0
  11. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/entry_points.txt +0 -0
  12. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/devnomads_cli.egg-info/top_level.txt +0 -0
  13. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/setup.cfg +0 -0
  14. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_cli.py +0 -0
  15. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_config.py +0 -0
  16. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_generate.py +0 -0
  17. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_generated_cli.py +0 -0
  18. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_helpers.py +0 -0
  19. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_hook.py +0 -0
  20. {devnomads_cli-0.4.0 → devnomads_cli-0.4.1}/tests/test_transfer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devnomads-cli
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Manage your DevNomads services from the command line
5
5
  Author-email: DevNomads <support@devnomads.nl>
6
6
  License: MIT
@@ -11,7 +11,7 @@ Requires-Dist: typer>=0.12
11
11
  Requires-Dist: httpx>=0.27
12
12
  Requires-Dist: rich>=13
13
13
  Requires-Dist: cryptography>=42
14
- Requires-Dist: devnomads[acme]>=0.2.1
14
+ Requires-Dist: devnomads[acme]>=0.2.3
15
15
  Dynamic: license-file
16
16
 
17
17
  # dncli
@@ -129,9 +129,18 @@ dncli services list | jq -r '.[].entity'
129
129
  `dncli` issues Let's Encrypt certificates over DNS-01 and HTTP-01:
130
130
 
131
131
  ```sh
132
- dncli cert issue example.com -d "*.example.com"
132
+ dncli cert issue example.com -d www.example.com -d "*.example.com"
133
133
  ```
134
134
 
135
+ The first argument is the primary domain (the certificate CN); add
136
+ extra names (SANs) by repeating `--san`/`-d`. The certificate is
137
+ written to `~/.config/dncli/certs/<domain>/` as `cert.pem`,
138
+ `fullchain.pem`, `chain.pem`, and `privkey.pem` (0600); override the
139
+ location with `--out`.
140
+
141
+ Keys are ECDSA P-384 by default. Pick another with `--key-type`
142
+ (`ecdsa256`, `ecdsa384`, `ecdsa521`, `rsa2048`, `rsa4096`).
143
+
135
144
  A dehydrated-compatible DNS-01 hook ships as `dncli-dns-hook`:
136
145
 
137
146
  ```sh
@@ -113,9 +113,18 @@ dncli services list | jq -r '.[].entity'
113
113
  `dncli` issues Let's Encrypt certificates over DNS-01 and HTTP-01:
114
114
 
115
115
  ```sh
116
- dncli cert issue example.com -d "*.example.com"
116
+ dncli cert issue example.com -d www.example.com -d "*.example.com"
117
117
  ```
118
118
 
119
+ The first argument is the primary domain (the certificate CN); add
120
+ extra names (SANs) by repeating `--san`/`-d`. The certificate is
121
+ written to `~/.config/dncli/certs/<domain>/` as `cert.pem`,
122
+ `fullchain.pem`, `chain.pem`, and `privkey.pem` (0600); override the
123
+ location with `--out`.
124
+
125
+ Keys are ECDSA P-384 by default. Pick another with `--key-type`
126
+ (`ecdsa256`, `ecdsa384`, `ecdsa521`, `rsa2048`, `rsa4096`).
127
+
119
128
  A dehydrated-compatible DNS-01 hook ships as `dncli-dns-hook`:
120
129
 
121
130
  ```sh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devnomads-cli
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Manage your DevNomads services from the command line
5
5
  Author-email: DevNomads <support@devnomads.nl>
6
6
  License: MIT
@@ -11,7 +11,7 @@ Requires-Dist: typer>=0.12
11
11
  Requires-Dist: httpx>=0.27
12
12
  Requires-Dist: rich>=13
13
13
  Requires-Dist: cryptography>=42
14
- Requires-Dist: devnomads[acme]>=0.2.1
14
+ Requires-Dist: devnomads[acme]>=0.2.3
15
15
  Dynamic: license-file
16
16
 
17
17
  # dncli
@@ -129,9 +129,18 @@ dncli services list | jq -r '.[].entity'
129
129
  `dncli` issues Let's Encrypt certificates over DNS-01 and HTTP-01:
130
130
 
131
131
  ```sh
132
- dncli cert issue example.com -d "*.example.com"
132
+ dncli cert issue example.com -d www.example.com -d "*.example.com"
133
133
  ```
134
134
 
135
+ The first argument is the primary domain (the certificate CN); add
136
+ extra names (SANs) by repeating `--san`/`-d`. The certificate is
137
+ written to `~/.config/dncli/certs/<domain>/` as `cert.pem`,
138
+ `fullchain.pem`, `chain.pem`, and `privkey.pem` (0600); override the
139
+ location with `--out`.
140
+
141
+ Keys are ECDSA P-384 by default. Pick another with `--key-type`
142
+ (`ecdsa256`, `ecdsa384`, `ecdsa521`, `rsa2048`, `rsa4096`).
143
+
135
144
  A dehydrated-compatible DNS-01 hook ships as `dncli-dns-hook`:
136
145
 
137
146
  ```sh
@@ -2,4 +2,4 @@ typer>=0.12
2
2
  httpx>=0.27
3
3
  rich>=13
4
4
  cryptography>=42
5
- devnomads[acme]>=0.2.1
5
+ devnomads[acme]>=0.2.3
@@ -1362,6 +1362,16 @@ LE_STAGING_DIRECTORY = "https://acme-staging-v02.api.letsencrypt.org/directory"
1362
1362
  CERT_RENEW_WINDOW_DAYS = 30
1363
1363
 
1364
1364
 
1365
+ class KeyType(str, Enum):
1366
+ """Certificate key types. ECDSA is the default; RSA on request."""
1367
+
1368
+ ecdsa256 = "ecdsa256"
1369
+ ecdsa384 = "ecdsa384"
1370
+ ecdsa521 = "ecdsa521"
1371
+ rsa2048 = "rsa2048"
1372
+ rsa4096 = "rsa4096"
1373
+
1374
+
1365
1375
  def _load_acme() -> Any:
1366
1376
  """Import devnomads.acme lazily, so non-cert commands skip the ACME
1367
1377
  stack at startup."""
@@ -1498,8 +1508,13 @@ def cert_issue(
1498
1508
  str | None, typer.Option("--email", help="ACME account contact email.")
1499
1509
  ] = None,
1500
1510
  key_type: Annotated[
1501
- str, typer.Option("--key-type", help="Certificate key type.")
1502
- ] = "ec256",
1511
+ KeyType,
1512
+ typer.Option(
1513
+ "--key-type",
1514
+ help="Certificate key type "
1515
+ "(ecdsa256/ecdsa384/ecdsa521/rsa2048/rsa4096).",
1516
+ ),
1517
+ ] = KeyType.ecdsa384,
1503
1518
  staging: Annotated[
1504
1519
  bool, typer.Option("--staging", help="Use the Let's Encrypt staging CA.")
1505
1520
  ] = False,
@@ -1528,7 +1543,7 @@ def cert_issue(
1528
1543
  webroot=webroot,
1529
1544
  standalone=standalone,
1530
1545
  email=email,
1531
- key_type=key_type,
1546
+ key_type=key_type.value,
1532
1547
  staging=staging,
1533
1548
  out_dir=_cert_out_dir(out, domain),
1534
1549
  )
@@ -1564,8 +1579,13 @@ def cert_renew(
1564
1579
  str | None, typer.Option("--email", help="ACME account contact email.")
1565
1580
  ] = None,
1566
1581
  key_type: Annotated[
1567
- str, typer.Option("--key-type", help="Certificate key type.")
1568
- ] = "ec256",
1582
+ KeyType,
1583
+ typer.Option(
1584
+ "--key-type",
1585
+ help="Certificate key type "
1586
+ "(ecdsa256/ecdsa384/ecdsa521/rsa2048/rsa4096).",
1587
+ ),
1588
+ ] = KeyType.ecdsa384,
1569
1589
  staging: Annotated[
1570
1590
  bool, typer.Option("--staging", help="Use the Let's Encrypt staging CA.")
1571
1591
  ] = False,
@@ -1597,7 +1617,7 @@ def cert_renew(
1597
1617
  webroot=None,
1598
1618
  standalone=False,
1599
1619
  email=email,
1600
- key_type=key_type,
1620
+ key_type=key_type.value,
1601
1621
  staging=staging,
1602
1622
  out_dir=out_dir,
1603
1623
  )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devnomads-cli"
3
- version = "0.4.0"
3
+ version = "0.4.1"
4
4
  description = "Manage your DevNomads services from the command line"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -11,7 +11,7 @@ dependencies = [
11
11
  "httpx>=0.27",
12
12
  "rich>=13",
13
13
  "cryptography>=42",
14
- "devnomads[acme]>=0.2.1",
14
+ "devnomads[acme]>=0.2.3",
15
15
  ]
16
16
 
17
17
  [project.scripts]
@@ -57,6 +57,56 @@ def test_issue_dns01_default_uses_dns_provider(configured, captured, isolated_co
57
57
  assert stat.S_IMODE(privkey.stat().st_mode) == 0o600
58
58
 
59
59
 
60
+ def _spy_generate_key(monkeypatch, seen):
61
+ import devnomads.acme as acme
62
+
63
+ def spy(algo):
64
+ seen["algo"] = algo
65
+ return object()
66
+
67
+ monkeypatch.setattr(acme, "generate_key", spy)
68
+
69
+
70
+ def test_issue_default_key_type_is_ecdsa384(
71
+ configured, captured, isolated_config, monkeypatch
72
+ ):
73
+ seen = {}
74
+ _spy_generate_key(monkeypatch, seen)
75
+ result = runner.invoke(app, ["cert", "issue", "example.com"])
76
+ assert result.exit_code == 0, result.output
77
+ assert seen["algo"] == "ecdsa384"
78
+
79
+
80
+ def test_issue_key_type_ecdsa521(configured, captured, isolated_config, monkeypatch):
81
+ seen = {}
82
+ _spy_generate_key(monkeypatch, seen)
83
+ result = runner.invoke(
84
+ app, ["cert", "issue", "example.com", "--key-type", "ecdsa521"]
85
+ )
86
+ assert result.exit_code == 0, result.output
87
+ assert seen["algo"] == "ecdsa521"
88
+
89
+
90
+ def test_issue_key_type_is_selectable(
91
+ configured, captured, isolated_config, monkeypatch
92
+ ):
93
+ seen = {}
94
+ _spy_generate_key(monkeypatch, seen)
95
+ result = runner.invoke(
96
+ app, ["cert", "issue", "example.com", "--key-type", "rsa4096"]
97
+ )
98
+ assert result.exit_code == 0, result.output
99
+ assert seen["algo"] == "rsa4096"
100
+
101
+
102
+ def test_issue_rejects_unknown_key_type():
103
+ result = runner.invoke(
104
+ app, ["cert", "issue", "example.com", "--key-type", "ed25519"]
105
+ )
106
+ assert result.exit_code != 0
107
+ assert "ed25519" in result.output
108
+
109
+
60
110
  def test_issue_http01_webroot_uses_webroot_solver(
61
111
  configured, captured, isolated_config, tmp_path
62
112
  ):
File without changes
File without changes