ign8-it 5.0.0__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.
- ign8_it-5.0.0/PKG-INFO +80 -0
- ign8_it-5.0.0/README.md +44 -0
- ign8_it-5.0.0/pyproject.toml +68 -0
- ign8_it-5.0.0/src/ign8/__init__.py +1 -0
- ign8_it-5.0.0/src/ign8/cli.py +151 -0
ign8_it-5.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ign8-it
|
|
3
|
+
Version: 5.0.0
|
|
4
|
+
Summary: Bootstrap the IGN8 infrastructure environment — configure cloud, DNS, and Git credentials in one interactive command.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: ign8,infrastructure,hetzner,cloudflare,devops
|
|
7
|
+
Author: Jakob Holst
|
|
8
|
+
Author-email: lyngknuden@gmail.com
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: System :: Systems Administration
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: ign8cloud
|
|
21
|
+
Requires-Dist: ign8git
|
|
22
|
+
Requires-Dist: ign8ingress
|
|
23
|
+
Requires-Dist: ign8inventory
|
|
24
|
+
Requires-Dist: ign8mail
|
|
25
|
+
Requires-Dist: ign8netbox
|
|
26
|
+
Requires-Dist: ign8vault
|
|
27
|
+
Requires-Dist: mypy (>=1.10) ; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest (>=8) ; extra == "dev"
|
|
29
|
+
Requires-Dist: rich (>=13)
|
|
30
|
+
Requires-Dist: ruff (>=0.4) ; extra == "dev"
|
|
31
|
+
Requires-Dist: typer (>=0.12)
|
|
32
|
+
Project-URL: Homepage, https://github.com/team-nine/ign8
|
|
33
|
+
Project-URL: Repository, https://github.com/team-nine/ign8
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# ign8
|
|
37
|
+
|
|
38
|
+
Bootstrap the IGN8 infrastructure environment — configure cloud, DNS, and Git credentials in one interactive command.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install ign8
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
ign8 init
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Prompts for your cloud provider (Hetzner/AWS/GCP/Azure), DNS provider (Cloudflare), and Git provider (GitHub/Bitbucket/Gitea), then saves all credentials to `~/.ign8` (mode 600).
|
|
53
|
+
|
|
54
|
+
Activate in your shell:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
source ~/.ign8
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or add to `~/.zshrc` / `~/.bashrc`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
[ -f ~/.ign8 ] && source ~/.ign8
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Environment variables
|
|
67
|
+
|
|
68
|
+
| Variable | Description |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `IGN8_CLOUD` | Cloud provider: `hetzner` / `aws` / `gcp` / `azure` |
|
|
71
|
+
| `IGN8_HETZNER` | Hetzner Cloud API token |
|
|
72
|
+
| `IGN8_DNS` | DNS provider: `cloudflare` |
|
|
73
|
+
| `IGN8_CLOUDFLARE` | Cloudflare API token |
|
|
74
|
+
| `IGN8_GIT` | Git provider: `github` / `bitbucket` / `gitea` |
|
|
75
|
+
| `IGN8_GITHUB_TOKEN` | GitHub personal access token |
|
|
76
|
+
| `IGN8_BITBUCKET_USER` | Bitbucket username |
|
|
77
|
+
| `IGN8_BITBUCKET_PASSWORD` | Bitbucket app password |
|
|
78
|
+
| `IGN8_GITEA_URL` | Gitea instance URL |
|
|
79
|
+
| `IGN8_GITEA_TOKEN` | Gitea API token |
|
|
80
|
+
|
ign8_it-5.0.0/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ign8
|
|
2
|
+
|
|
3
|
+
Bootstrap the IGN8 infrastructure environment — configure cloud, DNS, and Git credentials in one interactive command.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ign8
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ign8 init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Prompts for your cloud provider (Hetzner/AWS/GCP/Azure), DNS provider (Cloudflare), and Git provider (GitHub/Bitbucket/Gitea), then saves all credentials to `~/.ign8` (mode 600).
|
|
18
|
+
|
|
19
|
+
Activate in your shell:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
source ~/.ign8
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or add to `~/.zshrc` / `~/.bashrc`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
[ -f ~/.ign8 ] && source ~/.ign8
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Environment variables
|
|
32
|
+
|
|
33
|
+
| Variable | Description |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `IGN8_CLOUD` | Cloud provider: `hetzner` / `aws` / `gcp` / `azure` |
|
|
36
|
+
| `IGN8_HETZNER` | Hetzner Cloud API token |
|
|
37
|
+
| `IGN8_DNS` | DNS provider: `cloudflare` |
|
|
38
|
+
| `IGN8_CLOUDFLARE` | Cloudflare API token |
|
|
39
|
+
| `IGN8_GIT` | Git provider: `github` / `bitbucket` / `gitea` |
|
|
40
|
+
| `IGN8_GITHUB_TOKEN` | GitHub personal access token |
|
|
41
|
+
| `IGN8_BITBUCKET_USER` | Bitbucket username |
|
|
42
|
+
| `IGN8_BITBUCKET_PASSWORD` | Bitbucket app password |
|
|
43
|
+
| `IGN8_GITEA_URL` | Gitea instance URL |
|
|
44
|
+
| `IGN8_GITEA_TOKEN` | Gitea API token |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=2.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ign8-it"
|
|
7
|
+
version = "5.0.0"
|
|
8
|
+
description = "Bootstrap the IGN8 infrastructure environment — configure cloud, DNS, and Git credentials in one interactive command."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Jakob Holst", email = "lyngknuden@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ign8", "infrastructure", "hetzner", "cloudflare", "devops"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: System Administrators",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Topic :: System :: Systems Administration",
|
|
25
|
+
"Topic :: Utilities",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"typer>=0.12",
|
|
29
|
+
"rich>=13",
|
|
30
|
+
"ign8mail",
|
|
31
|
+
"ign8inventory",
|
|
32
|
+
"ign8vault",
|
|
33
|
+
"ign8ingress",
|
|
34
|
+
"ign8cloud",
|
|
35
|
+
"ign8git",
|
|
36
|
+
"ign8netbox",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
ign8 = "ign8.cli:app"
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8",
|
|
45
|
+
"ruff>=0.4",
|
|
46
|
+
"mypy>=1.10",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://github.com/team-nine/ign8"
|
|
51
|
+
Repository = "https://github.com/team-nine/ign8"
|
|
52
|
+
|
|
53
|
+
[tool.poetry]
|
|
54
|
+
packages = [{include = "ign8", from = "src"}]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
src = ["src"]
|
|
58
|
+
line-length = 100
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E", "F", "I", "UP"]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
strict = true
|
|
65
|
+
mypy_path = "src"
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "5.0.0"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.prompt import Prompt
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(name="ign8", help="IGN8 infrastructure CLI", add_completion=False)
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
ENV_FILE = Path.home() / ".ign8"
|
|
13
|
+
_KEEP = "••••••••" # sentinel shown when a value is already set
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ── helpers ───────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
def _load() -> dict[str, str]:
|
|
19
|
+
env: dict[str, str] = {}
|
|
20
|
+
try:
|
|
21
|
+
for line in ENV_FILE.read_text().splitlines():
|
|
22
|
+
if line.startswith("export "):
|
|
23
|
+
k, _, v = line[7:].partition("=")
|
|
24
|
+
env[k] = v.strip('"')
|
|
25
|
+
except FileNotFoundError:
|
|
26
|
+
pass
|
|
27
|
+
return env
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _save(env: dict[str, str]) -> None:
|
|
31
|
+
lines = [f'export {k}="{v}"' for k, v in env.items()]
|
|
32
|
+
ENV_FILE.write_text("\n".join(lines) + "\n")
|
|
33
|
+
ENV_FILE.chmod(0o600)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _ask(label: str, key: str, current: str | None = None) -> str | None:
|
|
37
|
+
hint = " [dim]\\[already set — Enter to keep][/dim]" if current else ""
|
|
38
|
+
val = Prompt.ask(
|
|
39
|
+
f" [color(232,92,13)]?[/] {label}{hint}\n [dim]{key}[/]",
|
|
40
|
+
default=_KEEP if current else "",
|
|
41
|
+
console=console,
|
|
42
|
+
)
|
|
43
|
+
if val == _KEEP:
|
|
44
|
+
return current
|
|
45
|
+
return val or None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _choose(label: str, key: str, options: list[str], current: str | None = None) -> str:
|
|
49
|
+
opts = "/".join(options)
|
|
50
|
+
val = Prompt.ask(
|
|
51
|
+
f" [color(232,92,13)]?[/] {label}\n [dim]{key}[/]",
|
|
52
|
+
choices=options,
|
|
53
|
+
default=current or options[0],
|
|
54
|
+
console=console,
|
|
55
|
+
show_choices=False,
|
|
56
|
+
show_default=True,
|
|
57
|
+
)
|
|
58
|
+
console.print(f" [dim]({opts})[/]", highlight=False)
|
|
59
|
+
return val
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ── commands ──────────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
@app.command()
|
|
65
|
+
def init() -> None:
|
|
66
|
+
"""Interactively configure the IGN8 environment and save to ~/.ign8."""
|
|
67
|
+
console.print(f"\n [bold color(232,92,13)]◈ IGN8 / INIT[/]")
|
|
68
|
+
console.print(f" [dim]Bootstrap your infrastructure environment[/]")
|
|
69
|
+
console.print(f" [dim]Saves to {ENV_FILE} (mode 600)[/]\n")
|
|
70
|
+
|
|
71
|
+
existing = _load()
|
|
72
|
+
out: dict[str, str] = dict(existing)
|
|
73
|
+
|
|
74
|
+
# ── Cloud ─────────────────────────────────────────────────────────────────
|
|
75
|
+
cloud = Prompt.ask(
|
|
76
|
+
" [color(232,92,13)]?[/] Cloud provider\n [dim]IGN8_CLOUD[/]",
|
|
77
|
+
choices=["hetzner", "aws", "gcp", "azure"],
|
|
78
|
+
default=existing.get("IGN8_CLOUD", "hetzner"),
|
|
79
|
+
console=console,
|
|
80
|
+
show_choices=True,
|
|
81
|
+
show_default=True,
|
|
82
|
+
)
|
|
83
|
+
out["IGN8_CLOUD"] = cloud
|
|
84
|
+
|
|
85
|
+
if cloud == "hetzner":
|
|
86
|
+
tok = _ask("Hetzner Cloud API token", "IGN8_HETZNER", existing.get("IGN8_HETZNER"))
|
|
87
|
+
if tok:
|
|
88
|
+
out["IGN8_HETZNER"] = tok
|
|
89
|
+
|
|
90
|
+
# ── DNS ───────────────────────────────────────────────────────────────────
|
|
91
|
+
console.print()
|
|
92
|
+
dns = Prompt.ask(
|
|
93
|
+
" [color(232,92,13)]?[/] DNS provider\n [dim]IGN8_DNS[/]",
|
|
94
|
+
choices=["cloudflare"],
|
|
95
|
+
default=existing.get("IGN8_DNS", "cloudflare"),
|
|
96
|
+
console=console,
|
|
97
|
+
show_choices=True,
|
|
98
|
+
show_default=True,
|
|
99
|
+
)
|
|
100
|
+
out["IGN8_DNS"] = dns
|
|
101
|
+
|
|
102
|
+
if dns == "cloudflare":
|
|
103
|
+
tok = _ask("Cloudflare API token", "IGN8_CLOUDFLARE", existing.get("IGN8_CLOUDFLARE"))
|
|
104
|
+
if tok:
|
|
105
|
+
out["IGN8_CLOUDFLARE"] = tok
|
|
106
|
+
|
|
107
|
+
# ── Git ───────────────────────────────────────────────────────────────────
|
|
108
|
+
console.print()
|
|
109
|
+
git = Prompt.ask(
|
|
110
|
+
" [color(232,92,13)]?[/] Git provider\n [dim]IGN8_GIT[/]",
|
|
111
|
+
choices=["github", "bitbucket", "gitea"],
|
|
112
|
+
default=existing.get("IGN8_GIT", "github"),
|
|
113
|
+
console=console,
|
|
114
|
+
show_choices=True,
|
|
115
|
+
show_default=True,
|
|
116
|
+
)
|
|
117
|
+
out["IGN8_GIT"] = git
|
|
118
|
+
|
|
119
|
+
if git == "bitbucket":
|
|
120
|
+
user = _ask("Bitbucket username", "IGN8_BITBUCKET_USER", existing.get("IGN8_BITBUCKET_USER"))
|
|
121
|
+
if user:
|
|
122
|
+
out["IGN8_BITBUCKET_USER"] = user
|
|
123
|
+
pw = _ask("Bitbucket app password", "IGN8_BITBUCKET_PASSWORD", existing.get("IGN8_BITBUCKET_PASSWORD"))
|
|
124
|
+
if pw:
|
|
125
|
+
out["IGN8_BITBUCKET_PASSWORD"] = pw
|
|
126
|
+
elif git == "github":
|
|
127
|
+
tok = _ask("GitHub personal access token", "IGN8_GITHUB_TOKEN", existing.get("IGN8_GITHUB_TOKEN"))
|
|
128
|
+
if tok:
|
|
129
|
+
out["IGN8_GITHUB_TOKEN"] = tok
|
|
130
|
+
elif git == "gitea":
|
|
131
|
+
url = _ask("Gitea instance URL", "IGN8_GITEA_URL", existing.get("IGN8_GITEA_URL"))
|
|
132
|
+
if url:
|
|
133
|
+
out["IGN8_GITEA_URL"] = url
|
|
134
|
+
tok = _ask("Gitea API token", "IGN8_GITEA_TOKEN", existing.get("IGN8_GITEA_TOKEN"))
|
|
135
|
+
if tok:
|
|
136
|
+
out["IGN8_GITEA_TOKEN"] = tok
|
|
137
|
+
|
|
138
|
+
_save(out)
|
|
139
|
+
|
|
140
|
+
hr = "─" * 52
|
|
141
|
+
console.print(f"\n [dim]{hr}[/]")
|
|
142
|
+
console.print(f" [green]Saved → {ENV_FILE}[/]")
|
|
143
|
+
console.print(f"\n Activate in current shell:")
|
|
144
|
+
console.print(f" source ~/.ign8")
|
|
145
|
+
console.print(f"\n Or add to ~/.zshrc / ~/.bashrc:")
|
|
146
|
+
console.print(f" [dim][ -f ~/.ign8 ] && source ~/.ign8[/]")
|
|
147
|
+
console.print(f" [dim]{hr}[/]\n")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def main() -> None:
|
|
151
|
+
app()
|