freepod 0.1.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.
- freepod-0.1.1/.gitignore +53 -0
- freepod-0.1.1/LICENSE +21 -0
- freepod-0.1.1/PKG-INFO +118 -0
- freepod-0.1.1/README.md +90 -0
- freepod-0.1.1/pyproject.toml +64 -0
- freepod-0.1.1/src/freepod/__init__.py +86 -0
- freepod-0.1.1/src/freepod/__main__.py +10 -0
- freepod-0.1.1/src/freepod/api.py +306 -0
- freepod-0.1.1/src/freepod/archive.py +480 -0
- freepod-0.1.1/src/freepod/auth.py +589 -0
- freepod-0.1.1/src/freepod/build.py +358 -0
- freepod-0.1.1/src/freepod/cli.py +349 -0
- freepod-0.1.1/src/freepod/config.py +158 -0
- freepod-0.1.1/src/freepod/deploy.py +867 -0
- freepod-0.1.1/src/freepod/project.py +203 -0
- freepod-0.1.1/src/freepod/tos.py +228 -0
- freepod-0.1.1/src/freepod/values.py +273 -0
- freepod-0.1.1/tests/conftest.py +166 -0
- freepod-0.1.1/tests/test_api.py +429 -0
- freepod-0.1.1/tests/test_archive.py +659 -0
- freepod-0.1.1/tests/test_auth.py +550 -0
- freepod-0.1.1/tests/test_build.py +679 -0
- freepod-0.1.1/tests/test_cli.py +282 -0
- freepod-0.1.1/tests/test_config.py +110 -0
- freepod-0.1.1/tests/test_deploy.py +1134 -0
- freepod-0.1.1/tests/test_init.py +319 -0
- freepod-0.1.1/tests/test_package.py +126 -0
- freepod-0.1.1/tests/test_project.py +254 -0
- freepod-0.1.1/tests/test_surface.py +368 -0
- freepod-0.1.1/tests/test_tos.py +371 -0
- freepod-0.1.1/tests/test_values.py +331 -0
freepod-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/**
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyc
|
|
5
|
+
*.pyo
|
|
6
|
+
*.pyd
|
|
7
|
+
*.so
|
|
8
|
+
*.egg-info/
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
.eggs/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
coverage.xml
|
|
18
|
+
pytest.xml
|
|
19
|
+
|
|
20
|
+
# Virtualenv
|
|
21
|
+
.venv/
|
|
22
|
+
venv/
|
|
23
|
+
|
|
24
|
+
# Local env overrides (may contain secrets)
|
|
25
|
+
.env
|
|
26
|
+
.env.local
|
|
27
|
+
|
|
28
|
+
# SQLite
|
|
29
|
+
*.db
|
|
30
|
+
*.sqlite3
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# IDE
|
|
34
|
+
.idea/
|
|
35
|
+
.vscode/
|
|
36
|
+
|
|
37
|
+
# OS
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
var/
|
|
42
|
+
|
|
43
|
+
# Playwright MCP artifacts
|
|
44
|
+
.playwright-mcp/
|
|
45
|
+
|
|
46
|
+
.claude/worktrees
|
|
47
|
+
|
|
48
|
+
# OpenCode tooling artifacts
|
|
49
|
+
.opencode/node_modules/
|
|
50
|
+
.opencode/package-lock.json
|
|
51
|
+
.opencode/bun.lock
|
|
52
|
+
|
|
53
|
+
api/static/
|
freepod-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2026 Erik van Zijst <erik.van.zijst@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
freepod-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: freepod
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Command-line client for Freepod — take a local project directory to a running deployment on freepod.eu.
|
|
5
|
+
Project-URL: Homepage, https://freepod.eu
|
|
6
|
+
Project-URL: Repository, https://github.com/erikvanzijst/freepod
|
|
7
|
+
Project-URL: Issues, https://github.com/erikvanzijst/freepod/issues
|
|
8
|
+
Author-email: Freepod <erik@freepod.eu>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,deployment,freepod,paas
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: click>=8.0
|
|
25
|
+
Requires-Dist: httpx>=0.24
|
|
26
|
+
Requires-Dist: pathspec>=1.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# freepod
|
|
30
|
+
|
|
31
|
+
Take a local project directory to a running deployment on
|
|
32
|
+
[freepod.eu](https://freepod.eu).
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install freepod
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
cd myapp
|
|
40
|
+
freepod login # sign in through the browser
|
|
41
|
+
freepod init # choose a hostname for this project
|
|
42
|
+
freepod deploy # https://myapp.freepod.eu
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`deploy` packs the working tree, uploads it, builds a container image on the
|
|
46
|
+
platform and releases it. By the time the command returns, the app is live on
|
|
47
|
+
its own hostname over HTTPS. Ship a new version by running it again.
|
|
48
|
+
|
|
49
|
+
There is no Dockerfile to write and nothing to configure:
|
|
50
|
+
[Railpack](https://railpack.com) detects the stack — Node, Python, Go, Java,
|
|
51
|
+
PHP, Ruby, Rust and more — and builds an image from your source as it is.
|
|
52
|
+
|
|
53
|
+
## What you need
|
|
54
|
+
|
|
55
|
+
- **A Freepod account.** `freepod login` opens the sign-in page, which is also
|
|
56
|
+
where you can register.
|
|
57
|
+
- **Python 3.9 or newer**, for the CLI itself. The project you deploy can be in
|
|
58
|
+
any language Railpack supports.
|
|
59
|
+
- **An app that listens on `$PORT`.** The platform assigns the port and passes
|
|
60
|
+
it in the environment, so bind `0.0.0.0:$PORT` (`process.env.PORT`,
|
|
61
|
+
`os.environ["PORT"]`, …) rather than a fixed number. An app that picks its own
|
|
62
|
+
port receives no traffic.
|
|
63
|
+
|
|
64
|
+
Deployments have no persistent disk. Whatever the app writes to its own
|
|
65
|
+
filesystem is gone when it restarts, and at every release.
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
| Command | What it does |
|
|
70
|
+
|------------------|------------------------------------------------------------------------------------------|
|
|
71
|
+
| `freepod login` | Sign in, and remember the credential. |
|
|
72
|
+
| `freepod init` | Set the current directory up as a project: choose a hostname, write `.freepod.json`. |
|
|
73
|
+
| `freepod deploy` | Pack, build and release the current project. |
|
|
74
|
+
| `freepod whoami` | Report who you are signed in as. |
|
|
75
|
+
| `freepod logout` | Forget the stored credential. |
|
|
76
|
+
|
|
77
|
+
`freepod --help` and `freepod <command> --help` cover the flags.
|
|
78
|
+
|
|
79
|
+
## Hostnames
|
|
80
|
+
|
|
81
|
+
`init` asks for one. A bare name becomes a subdomain of the platform: `myapp` is
|
|
82
|
+
served at `myapp.freepod.eu`. To use a domain of your own, point a CNAME at
|
|
83
|
+
`freepod.eu` first, then give `init` the full name. Certificates are issued and
|
|
84
|
+
renewed for you either way.
|
|
85
|
+
|
|
86
|
+
## .freepod.json
|
|
87
|
+
|
|
88
|
+
`init` writes it, and it is meant to be committed. It records the hostname you
|
|
89
|
+
chose and which deployment this directory belongs to, so the next `deploy` —
|
|
90
|
+
yours, a coworker's, or CI's — updates that same app instead of creating a
|
|
91
|
+
second one. To change a value, edit the file; the next deploy applies it.
|
|
92
|
+
|
|
93
|
+
## What gets uploaded
|
|
94
|
+
|
|
95
|
+
The working tree, minus `.git/`, anything your `.gitignore` excludes, and the
|
|
96
|
+
usual build output (`node_modules/`, `.venv/`, `__pycache__/`, `dist/`, …). A
|
|
97
|
+
`.freepodignore` file — same syntax as `.gitignore`, and applied last — keeps
|
|
98
|
+
out anything else the build does not need.
|
|
99
|
+
|
|
100
|
+
Re-including a file follows git's rules, with one wrinkle worth knowing: the
|
|
101
|
+
negation has to name the path (`!node_modules/patched.js`; a bare
|
|
102
|
+
`!patched.js` does nothing), and nothing beneath a directory you excluded
|
|
103
|
+
yourself can come back. Exclude `build/*` rather than `build/` if you need an
|
|
104
|
+
exception.
|
|
105
|
+
|
|
106
|
+
## Output
|
|
107
|
+
|
|
108
|
+
The address is the only thing `deploy` writes to stdout. The build log, the
|
|
109
|
+
progress bar and every status line go to stderr:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
URL=$(freepod deploy)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
Deployments are listed and managed at [freepod.eu](https://freepod.eu). Source
|
|
118
|
+
and issues: [github.com/erikvanzijst/freepod](https://github.com/erikvanzijst/freepod).
|
freepod-0.1.1/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# freepod
|
|
2
|
+
|
|
3
|
+
Take a local project directory to a running deployment on
|
|
4
|
+
[freepod.eu](https://freepod.eu).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install freepod
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
cd myapp
|
|
12
|
+
freepod login # sign in through the browser
|
|
13
|
+
freepod init # choose a hostname for this project
|
|
14
|
+
freepod deploy # https://myapp.freepod.eu
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`deploy` packs the working tree, uploads it, builds a container image on the
|
|
18
|
+
platform and releases it. By the time the command returns, the app is live on
|
|
19
|
+
its own hostname over HTTPS. Ship a new version by running it again.
|
|
20
|
+
|
|
21
|
+
There is no Dockerfile to write and nothing to configure:
|
|
22
|
+
[Railpack](https://railpack.com) detects the stack — Node, Python, Go, Java,
|
|
23
|
+
PHP, Ruby, Rust and more — and builds an image from your source as it is.
|
|
24
|
+
|
|
25
|
+
## What you need
|
|
26
|
+
|
|
27
|
+
- **A Freepod account.** `freepod login` opens the sign-in page, which is also
|
|
28
|
+
where you can register.
|
|
29
|
+
- **Python 3.9 or newer**, for the CLI itself. The project you deploy can be in
|
|
30
|
+
any language Railpack supports.
|
|
31
|
+
- **An app that listens on `$PORT`.** The platform assigns the port and passes
|
|
32
|
+
it in the environment, so bind `0.0.0.0:$PORT` (`process.env.PORT`,
|
|
33
|
+
`os.environ["PORT"]`, …) rather than a fixed number. An app that picks its own
|
|
34
|
+
port receives no traffic.
|
|
35
|
+
|
|
36
|
+
Deployments have no persistent disk. Whatever the app writes to its own
|
|
37
|
+
filesystem is gone when it restarts, and at every release.
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
| Command | What it does |
|
|
42
|
+
|------------------|------------------------------------------------------------------------------------------|
|
|
43
|
+
| `freepod login` | Sign in, and remember the credential. |
|
|
44
|
+
| `freepod init` | Set the current directory up as a project: choose a hostname, write `.freepod.json`. |
|
|
45
|
+
| `freepod deploy` | Pack, build and release the current project. |
|
|
46
|
+
| `freepod whoami` | Report who you are signed in as. |
|
|
47
|
+
| `freepod logout` | Forget the stored credential. |
|
|
48
|
+
|
|
49
|
+
`freepod --help` and `freepod <command> --help` cover the flags.
|
|
50
|
+
|
|
51
|
+
## Hostnames
|
|
52
|
+
|
|
53
|
+
`init` asks for one. A bare name becomes a subdomain of the platform: `myapp` is
|
|
54
|
+
served at `myapp.freepod.eu`. To use a domain of your own, point a CNAME at
|
|
55
|
+
`freepod.eu` first, then give `init` the full name. Certificates are issued and
|
|
56
|
+
renewed for you either way.
|
|
57
|
+
|
|
58
|
+
## .freepod.json
|
|
59
|
+
|
|
60
|
+
`init` writes it, and it is meant to be committed. It records the hostname you
|
|
61
|
+
chose and which deployment this directory belongs to, so the next `deploy` —
|
|
62
|
+
yours, a coworker's, or CI's — updates that same app instead of creating a
|
|
63
|
+
second one. To change a value, edit the file; the next deploy applies it.
|
|
64
|
+
|
|
65
|
+
## What gets uploaded
|
|
66
|
+
|
|
67
|
+
The working tree, minus `.git/`, anything your `.gitignore` excludes, and the
|
|
68
|
+
usual build output (`node_modules/`, `.venv/`, `__pycache__/`, `dist/`, …). A
|
|
69
|
+
`.freepodignore` file — same syntax as `.gitignore`, and applied last — keeps
|
|
70
|
+
out anything else the build does not need.
|
|
71
|
+
|
|
72
|
+
Re-including a file follows git's rules, with one wrinkle worth knowing: the
|
|
73
|
+
negation has to name the path (`!node_modules/patched.js`; a bare
|
|
74
|
+
`!patched.js` does nothing), and nothing beneath a directory you excluded
|
|
75
|
+
yourself can come back. Exclude `build/*` rather than `build/` if you need an
|
|
76
|
+
exception.
|
|
77
|
+
|
|
78
|
+
## Output
|
|
79
|
+
|
|
80
|
+
The address is the only thing `deploy` writes to stdout. The build log, the
|
|
81
|
+
progress bar and every status line go to stderr:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
URL=$(freepod deploy)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
Deployments are listed and managed at [freepod.eu](https://freepod.eu). Source
|
|
90
|
+
and issues: [github.com/erikvanzijst/freepod](https://github.com/erikvanzijst/freepod).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
# A floor rather than an open range: 1.27 is the first release that understands
|
|
3
|
+
# PEP 639, which the `license`/`license-files` fields below depend on.
|
|
4
|
+
requires = ["hatchling>=1.27"]
|
|
5
|
+
build-backend = "hatchling.build"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "freepod"
|
|
9
|
+
dynamic = ["version"]
|
|
10
|
+
description = "Command-line client for Freepod — take a local project directory to a running deployment on freepod.eu."
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE"]
|
|
15
|
+
authors = [{ name = "Freepod", email = "erik@freepod.eu" }]
|
|
16
|
+
keywords = ["freepod", "deployment", "paas", "cli"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Topic :: Software Development :: Build Tools",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
dependencies = [
|
|
32
|
+
"click>=8.0",
|
|
33
|
+
"httpx>=0.24",
|
|
34
|
+
"pathspec>=1.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://freepod.eu"
|
|
39
|
+
Repository = "https://github.com/erikvanzijst/freepod"
|
|
40
|
+
Issues = "https://github.com/erikvanzijst/freepod/issues"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
freepod = "freepod.cli:main"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.version]
|
|
46
|
+
path = "src/freepod/__init__.py"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/freepod"]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.sdist]
|
|
52
|
+
include = ["src/freepod", "tests", "README.md", "LICENSE", "pyproject.toml"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
line-length = 100
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
testpaths = ["tests"]
|
|
59
|
+
addopts = "-q"
|
|
60
|
+
|
|
61
|
+
[dependency-groups]
|
|
62
|
+
dev = [
|
|
63
|
+
"pytest>=8.0",
|
|
64
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""freepod — the Freepod command-line client.
|
|
2
|
+
|
|
3
|
+
The package is a pure client of the public Freepod REST API. It shares no code
|
|
4
|
+
with the API server and imports nothing from it.
|
|
5
|
+
|
|
6
|
+
This module holds only the error hierarchy, because every other module raises
|
|
7
|
+
from it and a leaf module keeps the imports acyclic. Each error carries the
|
|
8
|
+
exit code its class maps onto; see `cli-distribution` § *Exit codes
|
|
9
|
+
distinguish failure classes*.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"FreepodError",
|
|
16
|
+
"UsageError",
|
|
17
|
+
"AuthenticationError",
|
|
18
|
+
"PermissionError_",
|
|
19
|
+
"BuildFailed",
|
|
20
|
+
"RolloutFailed",
|
|
21
|
+
"EXIT_OK",
|
|
22
|
+
"EXIT_ERROR",
|
|
23
|
+
"EXIT_USAGE",
|
|
24
|
+
"EXIT_NOT_AUTHENTICATED",
|
|
25
|
+
"EXIT_BUILD_FAILED",
|
|
26
|
+
"EXIT_ROLLOUT_FAILED",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
__version__ = "0.1.1"
|
|
30
|
+
|
|
31
|
+
EXIT_OK = 0
|
|
32
|
+
EXIT_ERROR = 1
|
|
33
|
+
EXIT_USAGE = 2
|
|
34
|
+
EXIT_NOT_AUTHENTICATED = 3
|
|
35
|
+
EXIT_BUILD_FAILED = 4
|
|
36
|
+
EXIT_ROLLOUT_FAILED = 5
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FreepodError(Exception):
|
|
40
|
+
"""Anything that should end the run with a readable message.
|
|
41
|
+
|
|
42
|
+
Subclasses override `exit_code` to select their row of the exit-code
|
|
43
|
+
table. The base class is the "unexpected error" row.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
exit_code = EXIT_ERROR
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class UsageError(FreepodError):
|
|
50
|
+
"""The command was invoked wrongly. Nothing was attempted."""
|
|
51
|
+
|
|
52
|
+
exit_code = EXIT_USAGE
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AuthenticationError(FreepodError):
|
|
56
|
+
"""No usable credential, or one the platform will not accept.
|
|
57
|
+
|
|
58
|
+
Raised for the 401 row of the status-code contract, where re-authenticating
|
|
59
|
+
would succeed and change nothing, as well as for the ordinary "you are not
|
|
60
|
+
logged in" case.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
exit_code = EXIT_NOT_AUTHENTICATED
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PermissionError_(FreepodError):
|
|
67
|
+
"""Authenticated, but not permitted to act on the resource.
|
|
68
|
+
|
|
69
|
+
Named with a trailing underscore so it cannot shadow the builtin. This is
|
|
70
|
+
the API's own 403 — a credential problem no refresh can fix — and it is
|
|
71
|
+
deliberately *not* an `AuthenticationError`, so it never triggers a login.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
exit_code = EXIT_ERROR
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class BuildFailed(FreepodError):
|
|
78
|
+
"""The build reached a non-successful terminal status."""
|
|
79
|
+
|
|
80
|
+
exit_code = EXIT_BUILD_FAILED
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class RolloutFailed(FreepodError):
|
|
84
|
+
"""The rollout failed, or waiting for it timed out."""
|
|
85
|
+
|
|
86
|
+
exit_code = EXIT_ROLLOUT_FAILED
|