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 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
+
@@ -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()