tuca 0.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.
@@ -0,0 +1,105 @@
1
+ # SPDX-FileCopyrightText: 2026 René de Hesselle <dehesselle@web.de>
2
+ #
3
+ # SPDX-License-Identifier: GPL-2.0-or-later
4
+
5
+ name: Build
6
+ on:
7
+ push:
8
+ jobs:
9
+ #-----------------------------------------------------------------------------
10
+
11
+ Build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Checkout repository
15
+ uses: actions/checkout@v6
16
+ with:
17
+ fetch-depth: 0
18
+
19
+ - name: Run Black
20
+ uses: psf/black@stable
21
+ with:
22
+ options: "--check --verbose"
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v7
26
+
27
+ - name: Build project
28
+ run: uv build
29
+
30
+ - name: Upload artifacts
31
+ uses: actions/upload-artifact@v6
32
+ with:
33
+ name: dist
34
+ path: dist/tuca*.*
35
+
36
+ #-----------------------------------------------------------------------------
37
+
38
+ Prerelease:
39
+ runs-on: ubuntu-latest
40
+ permissions:
41
+ contents: write
42
+ needs: Build
43
+ if: startsWith(github.ref, 'refs/heads/develop')
44
+ steps:
45
+ - name: Download artifacts
46
+ uses: actions/download-artifact@v7
47
+ with:
48
+ name: dist
49
+
50
+ - name: Update prerelease
51
+ uses: ncipollo/release-action@v1
52
+ with:
53
+ name: develop
54
+ artifacts: tuca*.*
55
+ prerelease: true
56
+ allowUpdates: true
57
+ removeArtifacts: true
58
+ tag: latest
59
+ body: |
60
+ This prerelease follows the develop branch.
61
+ For testing purposes only.
62
+
63
+ #-----------------------------------------------------------------------------
64
+
65
+ Release:
66
+ runs-on: ubuntu-latest
67
+ permissions:
68
+ contents: write
69
+ needs: Build
70
+ if: startsWith(github.ref, 'refs/tags/v')
71
+ steps:
72
+ - name: Download artifacts
73
+ uses: actions/download-artifact@v7
74
+ with:
75
+ name: dist
76
+
77
+ - name: Create release
78
+ uses: ncipollo/release-action@v1
79
+ with:
80
+ artifacts: tuca*.*
81
+ draft: true
82
+
83
+ #-----------------------------------------------------------------------------
84
+
85
+ PyPI:
86
+ runs-on: ubuntu-latest
87
+ permissions:
88
+ id-token: write
89
+ needs:
90
+ - Build
91
+ - Release
92
+ if: startsWith(github.ref, 'refs/tags/v')
93
+ environment:
94
+ name: pypi
95
+ url: https://pypi.org/p/tuca
96
+
97
+ steps:
98
+ - name: Download artifacts
99
+ uses: actions/download-artifact@v7
100
+ with:
101
+ name: dist
102
+ path: dist/
103
+
104
+ - name: Publish to PyPI
105
+ uses: pypa/gh-action-pypi-publish@release/v1
tuca-0.1/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+ src/tuca/_version.py
9
+
10
+ # Virtual environments
11
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
tuca-0.1/PKG-INFO ADDED
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: tuca
3
+ Version: 0.1
4
+ Summary: tool using clouding api
5
+ Author-email: René de Hesselle <dehesselle@web.de>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: keyring>=25.7.0
8
+ Requires-Dist: pydantic>=2.12.5
9
+ Requires-Dist: python-slugify>=8.0.4
10
+ Requires-Dist: requests>=2.32.5
11
+ Requires-Dist: urlpath>=2.0.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # tool using Clouding.io API
15
+
16
+ This is an unofficial CLI tool that interacts with the Clouding.io's REST API. Its main goal is to provide a simple interface that I can use to create and destroy servers from shell scripts. Therefore it neither provides access to all available API endpoints nor to all available attributes and/or actions.
17
+
18
+ The project status is best described as "alpha" as things are still very much in motion and specifically tailored towards my usecase.
19
+
20
+ ## Installation
21
+
22
+ `tuca` is on [PyPi](https://pypi.org/project/tuca/), you can use the package manager of your choice to set yourself up. Here is an example using `uv`:
23
+
24
+ ```bash
25
+ uv tool install tuca
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### the basics
31
+
32
+ The CLI interface follows this pattern:
33
+
34
+ ```bash
35
+ tuca <endpoint> <action> [options]
36
+ ```
37
+
38
+ - `endpoint` is the same as in https://api.clouding.io/docs/
39
+ - `action` is one of `create`, `list` and `delete`
40
+ - `options` depend on `endpoint` and `action`, consult `--help` for details
41
+
42
+ `tuca` writes pretty-printed JSON (no colors) to `stdout`. It's both human-readable and intended to be piped into `jq` for non-interactive usage. The following example shows the available SSH keys (redacted values) in a freshly created account:
43
+
44
+ ```json
45
+ {
46
+ "keypairs": [
47
+ {
48
+ "fingerprint": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
49
+ "id": "xxxxxxxxxxxxxxxx",
50
+ "name": "default"
51
+ }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ The output is
57
+
58
+ - always organized as list, even if the result count is 1 or 0
59
+ - usually named after the endpoint
60
+ - contains only a limited number attributes, but always `id` and `name`
61
+ - limited to 100 entries
62
+
63
+ ### authentication
64
+
65
+ Before showing you examples, you need to setup an API token first. You can do that via environment variable:
66
+
67
+ ```
68
+ export CLOUDINGIO_API_TOKEN=my_secret_token
69
+ ```
70
+
71
+ Or, more securely, have tuca write it into your desktop's keyring. The following command will give you an interactive prompt to do that.
72
+
73
+ ```
74
+ tuca auth create
75
+ ```
76
+
77
+ _And before you say anything, I'm aware that `auth` is not an endpoint._
78
+
79
+ If you provide both, the environment variable takes precendence.
80
+
81
+ ### here we go
82
+
83
+ Time to create your first server. First, pick an image.
84
+
85
+ ```bash
86
+ tuca images list
87
+ ```
88
+
89
+ <details>
90
+ <summary>Output</summary>
91
+
92
+ _(modified/shortened for brevity)_
93
+
94
+ ```json
95
+ {
96
+ "sizes/flavors": [
97
+ ...
98
+ {
99
+ "accessMethods": {
100
+ "password": "required",
101
+ "sshKey": "not-supported"
102
+ },
103
+ "id": "jXEm7yK3MJ2VYkQ9",
104
+ "minimumSizeGb": 25,
105
+ "name": "Windows 11 (English 64Bit | Based on Windows Server 2025)"
106
+ },
107
+ ...
108
+ ]
109
+ }
110
+ ```
111
+ </details>
112
+
113
+ Now pick a size.
114
+
115
+ ```bash
116
+ tuca flavors list
117
+ ```
118
+
119
+ <details>
120
+ <summary>Output</summary>
121
+
122
+ _(modified/shortened for brevity)_
123
+
124
+ ```json
125
+ {
126
+ "sizes/flavors": [
127
+ ...
128
+ {
129
+ "id": "8x16",
130
+ "pricePerHour": 0.05472,
131
+ "pricePerMonthApprox": 39.9456,
132
+ "ramGb": 16,
133
+ "vCores": 8.0
134
+ },
135
+ ...
136
+ ]
137
+ }
138
+ ```
139
+ </details>
140
+
141
+ That's all to create a server with minimal configuration.
142
+
143
+ ```bash
144
+ # Windows 11 compatible image
145
+ # 8 cores, 16 GB RAM
146
+ # default firewall
147
+ # default image size
148
+ tuca servers create --image jXEm7yK3MJ2VYkQ9 --name MyWinServer --flavorid 8x16 --password start123
149
+ ```
150
+
151
+ <details>
152
+ <summary>Output</summary>
153
+
154
+ ```json
155
+ {
156
+ "servers": [
157
+ {
158
+ "createdAt": "2026-02-04T23:42:16",
159
+ "id": "mJOZBKqGP702Xjax",
160
+ "name": "MyWinServer",
161
+ "publicIp": null,
162
+ "status": "Pending"
163
+ }
164
+ ]
165
+ }
166
+ ```
167
+ </details>
168
+
169
+ Spooling up the server can take some time and you can check how it's doing.
170
+
171
+ ```bash
172
+ tuca servers list --name MyWinServer
173
+ ```
174
+ <details>
175
+ <summary>Output</summary>
176
+
177
+ ```json
178
+ {
179
+ "servers": [
180
+ {
181
+ "createdAt": "2026-02-04T23:42:16",
182
+ "id": "mJOZBKqGP702Xjax",
183
+ "name": "MyWinServer",
184
+ "publicIp": "103.23.60.115",
185
+ "status": "Creating"
186
+ }
187
+ ]
188
+ }
189
+ ```
190
+ </details>
191
+
192
+ The server will be ready eventually.
193
+
194
+ <details>
195
+ <summary>Output</summary>
196
+
197
+ ```json
198
+ {
199
+ "servers": [
200
+ {
201
+ "createdAt": "2026-02-04T23:42:16",
202
+ "id": "mJOZBKqGP702Xjax",
203
+ "name": "MyWinServer",
204
+ "publicIp": "103.23.60.115",
205
+ "status": "Active"
206
+ }
207
+ ]
208
+ }
209
+ ```
210
+ </details>
211
+
212
+ ## License
213
+
214
+ [GPL-2.0-or-later](https://github.com/dehesselle/tuca/blob/main/LICENSE)
tuca-0.1/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # tool using Clouding.io API
2
+
3
+ This is an unofficial CLI tool that interacts with the Clouding.io's REST API. Its main goal is to provide a simple interface that I can use to create and destroy servers from shell scripts. Therefore it neither provides access to all available API endpoints nor to all available attributes and/or actions.
4
+
5
+ The project status is best described as "alpha" as things are still very much in motion and specifically tailored towards my usecase.
6
+
7
+ ## Installation
8
+
9
+ `tuca` is on [PyPi](https://pypi.org/project/tuca/), you can use the package manager of your choice to set yourself up. Here is an example using `uv`:
10
+
11
+ ```bash
12
+ uv tool install tuca
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### the basics
18
+
19
+ The CLI interface follows this pattern:
20
+
21
+ ```bash
22
+ tuca <endpoint> <action> [options]
23
+ ```
24
+
25
+ - `endpoint` is the same as in https://api.clouding.io/docs/
26
+ - `action` is one of `create`, `list` and `delete`
27
+ - `options` depend on `endpoint` and `action`, consult `--help` for details
28
+
29
+ `tuca` writes pretty-printed JSON (no colors) to `stdout`. It's both human-readable and intended to be piped into `jq` for non-interactive usage. The following example shows the available SSH keys (redacted values) in a freshly created account:
30
+
31
+ ```json
32
+ {
33
+ "keypairs": [
34
+ {
35
+ "fingerprint": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
36
+ "id": "xxxxxxxxxxxxxxxx",
37
+ "name": "default"
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ The output is
44
+
45
+ - always organized as list, even if the result count is 1 or 0
46
+ - usually named after the endpoint
47
+ - contains only a limited number attributes, but always `id` and `name`
48
+ - limited to 100 entries
49
+
50
+ ### authentication
51
+
52
+ Before showing you examples, you need to setup an API token first. You can do that via environment variable:
53
+
54
+ ```
55
+ export CLOUDINGIO_API_TOKEN=my_secret_token
56
+ ```
57
+
58
+ Or, more securely, have tuca write it into your desktop's keyring. The following command will give you an interactive prompt to do that.
59
+
60
+ ```
61
+ tuca auth create
62
+ ```
63
+
64
+ _And before you say anything, I'm aware that `auth` is not an endpoint._
65
+
66
+ If you provide both, the environment variable takes precendence.
67
+
68
+ ### here we go
69
+
70
+ Time to create your first server. First, pick an image.
71
+
72
+ ```bash
73
+ tuca images list
74
+ ```
75
+
76
+ <details>
77
+ <summary>Output</summary>
78
+
79
+ _(modified/shortened for brevity)_
80
+
81
+ ```json
82
+ {
83
+ "sizes/flavors": [
84
+ ...
85
+ {
86
+ "accessMethods": {
87
+ "password": "required",
88
+ "sshKey": "not-supported"
89
+ },
90
+ "id": "jXEm7yK3MJ2VYkQ9",
91
+ "minimumSizeGb": 25,
92
+ "name": "Windows 11 (English 64Bit | Based on Windows Server 2025)"
93
+ },
94
+ ...
95
+ ]
96
+ }
97
+ ```
98
+ </details>
99
+
100
+ Now pick a size.
101
+
102
+ ```bash
103
+ tuca flavors list
104
+ ```
105
+
106
+ <details>
107
+ <summary>Output</summary>
108
+
109
+ _(modified/shortened for brevity)_
110
+
111
+ ```json
112
+ {
113
+ "sizes/flavors": [
114
+ ...
115
+ {
116
+ "id": "8x16",
117
+ "pricePerHour": 0.05472,
118
+ "pricePerMonthApprox": 39.9456,
119
+ "ramGb": 16,
120
+ "vCores": 8.0
121
+ },
122
+ ...
123
+ ]
124
+ }
125
+ ```
126
+ </details>
127
+
128
+ That's all to create a server with minimal configuration.
129
+
130
+ ```bash
131
+ # Windows 11 compatible image
132
+ # 8 cores, 16 GB RAM
133
+ # default firewall
134
+ # default image size
135
+ tuca servers create --image jXEm7yK3MJ2VYkQ9 --name MyWinServer --flavorid 8x16 --password start123
136
+ ```
137
+
138
+ <details>
139
+ <summary>Output</summary>
140
+
141
+ ```json
142
+ {
143
+ "servers": [
144
+ {
145
+ "createdAt": "2026-02-04T23:42:16",
146
+ "id": "mJOZBKqGP702Xjax",
147
+ "name": "MyWinServer",
148
+ "publicIp": null,
149
+ "status": "Pending"
150
+ }
151
+ ]
152
+ }
153
+ ```
154
+ </details>
155
+
156
+ Spooling up the server can take some time and you can check how it's doing.
157
+
158
+ ```bash
159
+ tuca servers list --name MyWinServer
160
+ ```
161
+ <details>
162
+ <summary>Output</summary>
163
+
164
+ ```json
165
+ {
166
+ "servers": [
167
+ {
168
+ "createdAt": "2026-02-04T23:42:16",
169
+ "id": "mJOZBKqGP702Xjax",
170
+ "name": "MyWinServer",
171
+ "publicIp": "103.23.60.115",
172
+ "status": "Creating"
173
+ }
174
+ ]
175
+ }
176
+ ```
177
+ </details>
178
+
179
+ The server will be ready eventually.
180
+
181
+ <details>
182
+ <summary>Output</summary>
183
+
184
+ ```json
185
+ {
186
+ "servers": [
187
+ {
188
+ "createdAt": "2026-02-04T23:42:16",
189
+ "id": "mJOZBKqGP702Xjax",
190
+ "name": "MyWinServer",
191
+ "publicIp": "103.23.60.115",
192
+ "status": "Active"
193
+ }
194
+ ]
195
+ }
196
+ ```
197
+ </details>
198
+
199
+ ## License
200
+
201
+ [GPL-2.0-or-later](https://github.com/dehesselle/tuca/blob/main/LICENSE)
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "tuca"
3
+ dynamic = ["version"]
4
+ description = "tool using clouding api"
5
+ readme = "README.md"
6
+ authors = [{ name = "René de Hesselle", email = "dehesselle@web.de" }]
7
+ requires-python = ">=3.12"
8
+ dependencies = [
9
+ "keyring>=25.7.0",
10
+ "pydantic>=2.12.5",
11
+ "python-slugify>=8.0.4",
12
+ "requests>=2.32.5",
13
+ "urlpath>=2.0.0",
14
+ ]
15
+
16
+ [project.scripts]
17
+ tuca = "tuca:main"
18
+
19
+ [build-system]
20
+ requires = ["hatchling", "hatch-vcs"]
21
+ build-backend = "hatchling.build"
22
+
23
+ [tool.hatch.version]
24
+ source = "vcs"
25
+
26
+ [tool.hatch.build.hooks.vcs]
27
+ version-file = "src/tuca/_version.py"
@@ -0,0 +1,42 @@
1
+ # SPDX-FileCopyrightText: 2026 René de Hesselle <dehesselle@web.de>
2
+ #
3
+ # SPDX-License-Identifier: GPL-2.0-or-later
4
+
5
+ import argparse
6
+
7
+ from tuca.auth import setup_auth_cli
8
+ from tuca.config import config
9
+ from tuca.endpoints import (
10
+ setup_images_endpoint,
11
+ setup_keypairs_endpoint,
12
+ setup_servers_endpoint,
13
+ setup_sizes_endpoint,
14
+ setup_snapshots_endpoint,
15
+ )
16
+ from tuca.version import VERSION
17
+
18
+
19
+ def main() -> None:
20
+ parser = argparse.ArgumentParser(description="unofficial CLI for Clouding.io")
21
+ parser.add_argument(
22
+ "-v",
23
+ "--verbose",
24
+ action="store_true",
25
+ default=False,
26
+ help="make output verbose",
27
+ )
28
+ parser.add_argument("--version", action="version", version=f"tuca {VERSION}")
29
+ endpoints = parser.add_subparsers(help="manageable endpoints", dest="endpoint")
30
+ setup_auth_cli(endpoints)
31
+ setup_images_endpoint(endpoints)
32
+ setup_keypairs_endpoint(endpoints)
33
+ setup_servers_endpoint(endpoints)
34
+ setup_snapshots_endpoint(endpoints)
35
+ setup_sizes_endpoint(endpoints)
36
+
37
+ args = parser.parse_args()
38
+ config.be_verbose = args.verbose
39
+ try:
40
+ args.func(args)
41
+ except AttributeError:
42
+ parser.print_usage()
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.1'
32
+ __version_tuple__ = version_tuple = (0, 1)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,47 @@
1
+ # SPDX-FileCopyrightText: 2026 René de Hesselle <dehesselle@web.de>
2
+ #
3
+ # SPDX-License-Identifier: GPL-2.0-or-later
4
+
5
+ import os
6
+ from argparse import _SubParsersAction
7
+ from enum import StrEnum, auto
8
+ from getpass import getpass
9
+
10
+ import keyring
11
+
12
+ SERVICENAME = "Clouding.io API token"
13
+ USERNAME = "tuca"
14
+
15
+
16
+ class Action(StrEnum):
17
+ CREATE = auto()
18
+ DELETE = auto()
19
+
20
+
21
+ def set_token(_) -> None:
22
+ token = getpass("API token:")
23
+ keyring.set_password(SERVICENAME, USERNAME, token)
24
+
25
+
26
+ def get_token(_) -> str:
27
+ if api_token := os.getenv("CLOUDINGIO_API_TOKEN"):
28
+ return api_token
29
+ else:
30
+ return keyring.get_password(SERVICENAME, USERNAME)
31
+
32
+
33
+ def delete_token(_) -> None:
34
+ keyring.delete_password(SERVICENAME, USERNAME)
35
+
36
+
37
+ def setup_auth_cli(subparser: _SubParsersAction):
38
+ auth = subparser.add_parser("auth", help="manage authentication token")
39
+ auth_actions = auth.add_subparsers()
40
+ auth_action_set = auth_actions.add_parser(
41
+ Action.CREATE, help="set authentication token"
42
+ )
43
+ auth_action_set.set_defaults(func=set_token)
44
+ auth_action_delete = auth_actions.add_parser(
45
+ Action.DELETE, help="delete authentication token"
46
+ )
47
+ auth_action_delete.set_defaults(func=delete_token)