clouddeck 0.1.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.
- clouddeck-0.1.0/.env.example +56 -0
- clouddeck-0.1.0/.gitleaks.toml +27 -0
- clouddeck-0.1.0/CHANGELOG.md +90 -0
- clouddeck-0.1.0/CODE_OF_CONDUCT.md +131 -0
- clouddeck-0.1.0/CONTRIBUTING.md +181 -0
- clouddeck-0.1.0/LICENSE +21 -0
- clouddeck-0.1.0/MANIFEST.in +14 -0
- clouddeck-0.1.0/PKG-INFO +322 -0
- clouddeck-0.1.0/README.md +285 -0
- clouddeck-0.1.0/SECURITY.md +205 -0
- clouddeck-0.1.0/THIRD_PARTY_NOTICES.md +68 -0
- clouddeck-0.1.0/bin/clouddeck +4 -0
- clouddeck-0.1.0/clouddeck/__init__.py +3 -0
- clouddeck-0.1.0/clouddeck/__main__.py +5 -0
- clouddeck-0.1.0/clouddeck/cli.py +250 -0
- clouddeck-0.1.0/clouddeck/controller.py +3417 -0
- clouddeck-0.1.0/clouddeck/engine.py +37 -0
- clouddeck-0.1.0/clouddeck/fmt.py +43 -0
- clouddeck-0.1.0/clouddeck/hardware.py +598 -0
- clouddeck-0.1.0/clouddeck/job/__init__.py +1 -0
- clouddeck-0.1.0/clouddeck/job/__main__.py +3 -0
- clouddeck-0.1.0/clouddeck/job/cli.py +153 -0
- clouddeck-0.1.0/clouddeck/job/clock.py +20 -0
- clouddeck-0.1.0/clouddeck/job/market.py +194 -0
- clouddeck-0.1.0/clouddeck/job/preflight.py +196 -0
- clouddeck-0.1.0/clouddeck/job/remote.py +190 -0
- clouddeck-0.1.0/clouddeck/job/runner.py +731 -0
- clouddeck-0.1.0/clouddeck/job/spec.py +400 -0
- clouddeck-0.1.0/clouddeck/job/telemetry.py +100 -0
- clouddeck-0.1.0/clouddeck/providers/__init__.py +44 -0
- clouddeck-0.1.0/clouddeck/providers/akash.py +620 -0
- clouddeck-0.1.0/clouddeck/providers/base.py +494 -0
- clouddeck-0.1.0/clouddeck/providers/demo.py +329 -0
- clouddeck-0.1.0/clouddeck/remote.py +447 -0
- clouddeck-0.1.0/clouddeck/remote_client.py +90 -0
- clouddeck-0.1.0/clouddeck/sdlgen.py +275 -0
- clouddeck-0.1.0/clouddeck/service.py +434 -0
- clouddeck-0.1.0/clouddeck/tui/__init__.py +0 -0
- clouddeck-0.1.0/clouddeck/tui/app.py +495 -0
- clouddeck-0.1.0/clouddeck/tui/clouddeck.tcss +109 -0
- clouddeck-0.1.0/clouddeck/tui/compute.py +627 -0
- clouddeck-0.1.0/clouddeck/tui/monitor.py +188 -0
- clouddeck-0.1.0/clouddeck/tui/picker.py +622 -0
- clouddeck-0.1.0/clouddeck/tui/projects.py +265 -0
- clouddeck-0.1.0/clouddeck/tui/recovery.py +217 -0
- clouddeck-0.1.0/clouddeck/tui/safety.py +120 -0
- clouddeck-0.1.0/clouddeck/tui/widgets.py +483 -0
- clouddeck-0.1.0/clouddeck/tui/workers.py +421 -0
- clouddeck-0.1.0/clouddeck.egg-info/PKG-INFO +322 -0
- clouddeck-0.1.0/clouddeck.egg-info/SOURCES.txt +155 -0
- clouddeck-0.1.0/clouddeck.egg-info/dependency_links.txt +1 -0
- clouddeck-0.1.0/clouddeck.egg-info/entry_points.txt +4 -0
- clouddeck-0.1.0/clouddeck.egg-info/requires.txt +13 -0
- clouddeck-0.1.0/clouddeck.egg-info/top_level.txt +1 -0
- clouddeck-0.1.0/config.example.toml +94 -0
- clouddeck-0.1.0/conftest.py +21 -0
- clouddeck-0.1.0/ddc.py +10 -0
- clouddeck-0.1.0/docker/Dockerfile +19 -0
- clouddeck-0.1.0/docs/ARCHITECTURE.md +263 -0
- clouddeck-0.1.0/docs/CHECKPOINTS.md +154 -0
- clouddeck-0.1.0/docs/CLOUDDECK.md +176 -0
- clouddeck-0.1.0/docs/COSTS-AND-SAFETY.md +227 -0
- clouddeck-0.1.0/docs/HARDWARE.md +156 -0
- clouddeck-0.1.0/docs/JOBS.md +178 -0
- clouddeck-0.1.0/docs/PLATFORMS.md +108 -0
- clouddeck-0.1.0/docs/QUICKSTART.md +269 -0
- clouddeck-0.1.0/docs/prebuilt-image.md +54 -0
- clouddeck-0.1.0/examples/cpu-smoke.toml +19 -0
- clouddeck-0.1.0/examples/gpu-batch.toml +62 -0
- clouddeck-0.1.0/jobremote/boot.sh +23 -0
- clouddeck-0.1.0/jobremote/fetch.sh +26 -0
- clouddeck-0.1.0/jobremote/step.sh +27 -0
- clouddeck-0.1.0/lease/README.md +58 -0
- clouddeck-0.1.0/lease/agent.sh +91 -0
- clouddeck-0.1.0/lease/bench.sh +43 -0
- clouddeck-0.1.0/lease/checkpoint.sh +169 -0
- clouddeck-0.1.0/lease/ddc-lib.sh +73 -0
- clouddeck-0.1.0/lease/dist.sh +45 -0
- clouddeck-0.1.0/lease/restore.sh +134 -0
- clouddeck-0.1.0/lease/setup.sh +95 -0
- clouddeck-0.1.0/lease/tmux.conf +25 -0
- clouddeck-0.1.0/lease/workers.sh +23 -0
- clouddeck-0.1.0/pyproject.toml +80 -0
- clouddeck-0.1.0/requirements.txt +3 -0
- clouddeck-0.1.0/scripts/check_dist.py +87 -0
- clouddeck-0.1.0/scripts/ci_local.py +76 -0
- clouddeck-0.1.0/scripts/release_allowlist.toml +77 -0
- clouddeck-0.1.0/scripts/release_check.py +347 -0
- clouddeck-0.1.0/sdl.yaml.tmpl +63 -0
- clouddeck-0.1.0/setup.cfg +4 -0
- clouddeck-0.1.0/setup.py +23 -0
- clouddeck-0.1.0/test_ddc.py +107 -0
- clouddeck-0.1.0/tests/conftest.py +62 -0
- clouddeck-0.1.0/tests/e2e_helpers.py +551 -0
- clouddeck-0.1.0/tests/fake_akash.py +256 -0
- clouddeck-0.1.0/tests/fixtures/config.toml +58 -0
- clouddeck-0.1.0/tests/fixtures/hardware/bid_screening_400_invalid_attr_key.json +25 -0
- clouddeck-0.1.0/tests/fixtures/hardware/bid_screening_samples.json +134 -0
- clouddeck-0.1.0/tests/fixtures/hardware/bids_synthetic.json +68 -0
- clouddeck-0.1.0/tests/fixtures/hardware/gpu_models_sample.json +156 -0
- clouddeck-0.1.0/tests/fixtures/hardware/gpu_prices_sample.json +670 -0
- clouddeck-0.1.0/tests/fixtures/hardware/placement_options_sample.json +643 -0
- clouddeck-0.1.0/tests/fixtures/hardware/providers_sample.json +5888 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/box64.yaml +61 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/gpu_a100x4.yaml +67 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/gpu_bare.yaml +66 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/gpu_model.yaml +67 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/plain16.yaml +61 -0
- clouddeck-0.1.0/tests/fixtures/hardware/sdl_golden/worker32.yaml +61 -0
- clouddeck-0.1.0/tests/fixtures/lease/legacy-checkpoint.sh +102 -0
- clouddeck-0.1.0/tests/fixtures/lease/legacy-restore.sh +59 -0
- clouddeck-0.1.0/tests/hw_helpers.py +81 -0
- clouddeck-0.1.0/tests/job_fakes.py +322 -0
- clouddeck-0.1.0/tests/job_helpers.py +70 -0
- clouddeck-0.1.0/tests/job_sim.py +188 -0
- clouddeck-0.1.0/tests/pty_smoke.py +157 -0
- clouddeck-0.1.0/tests/test_controller.py +406 -0
- clouddeck-0.1.0/tests/test_controller_followups.py +254 -0
- clouddeck-0.1.0/tests/test_e2e_checkpoint.py +318 -0
- clouddeck-0.1.0/tests/test_e2e_failure.py +262 -0
- clouddeck-0.1.0/tests/test_e2e_provision.py +165 -0
- clouddeck-0.1.0/tests/test_e2e_scaling.py +221 -0
- clouddeck-0.1.0/tests/test_e2e_shutdown.py +257 -0
- clouddeck-0.1.0/tests/test_e2e_tui.py +427 -0
- clouddeck-0.1.0/tests/test_hardware_catalog.py +365 -0
- clouddeck-0.1.0/tests/test_hardware_sdl.py +327 -0
- clouddeck-0.1.0/tests/test_hardware_spec.py +289 -0
- clouddeck-0.1.0/tests/test_hardware_tui.py +535 -0
- clouddeck-0.1.0/tests/test_isolation_guard.py +24 -0
- clouddeck-0.1.0/tests/test_job_hardening.py +497 -0
- clouddeck-0.1.0/tests/test_job_runner.py +327 -0
- clouddeck-0.1.0/tests/test_job_safety_access.py +211 -0
- clouddeck-0.1.0/tests/test_job_safety_orphans.py +281 -0
- clouddeck-0.1.0/tests/test_job_safety_spend.py +294 -0
- clouddeck-0.1.0/tests/test_job_scripts.py +304 -0
- clouddeck-0.1.0/tests/test_job_spec.py +217 -0
- clouddeck-0.1.0/tests/test_lease_scripts.py +640 -0
- clouddeck-0.1.0/tests/test_packaging.py +50 -0
- clouddeck-0.1.0/tests/test_providers.py +443 -0
- clouddeck-0.1.0/tests/test_release_check.py +264 -0
- clouddeck-0.1.0/tests/test_remote_security.py +571 -0
- clouddeck-0.1.0/tests/test_safety_followups.py +431 -0
- clouddeck-0.1.0/tests/test_safety_lease_disabled.py +72 -0
- clouddeck-0.1.0/tests/test_safety_lease_followups.py +132 -0
- clouddeck-0.1.0/tests/test_safety_limits.py +236 -0
- clouddeck-0.1.0/tests/test_safety_ownership.py +233 -0
- clouddeck-0.1.0/tests/test_safety_provision.py +418 -0
- clouddeck-0.1.0/tests/test_safety_state.py +300 -0
- clouddeck-0.1.0/tests/test_safety_watchdog.py +420 -0
- clouddeck-0.1.0/tests/test_service.py +521 -0
- clouddeck-0.1.0/tests/test_tui.py +985 -0
- clouddeck-0.1.0/windows/bench.cmd +3 -0
- clouddeck-0.1.0/windows/claude-cloud.cmd +2 -0
- clouddeck-0.1.0/windows/clouddeck.cmd +3 -0
- clouddeck-0.1.0/windows/start.cmd +3 -0
- clouddeck-0.1.0/windows/status.cmd +3 -0
- clouddeck-0.1.0/windows/stop.cmd +3 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# CloudDeck / ddc: the name of every setting that comes from a file or from the environment. Placeholders only; never commit real values.
|
|
2
|
+
#
|
|
3
|
+
# CloudDeck does NOT read a ".env" file. This file only documents the names:
|
|
4
|
+
# * Part 1 lists the keys of secrets.env (same KEY=value syntax). `ddc init` creates the real file for you, owner-only
|
|
5
|
+
# (mode 0600 on Linux/macOS), in the secrets directory (default ~/.config/clouddeck/, see Part 2).
|
|
6
|
+
# * Part 2 lists process environment variables (all optional, all commented out here) that move files or change behaviour.
|
|
7
|
+
# Everything else (limits, repos, profiles) lives in config.toml: see config.example.toml.
|
|
8
|
+
|
|
9
|
+
# ---------------------------------------------------------------------------------------------------------------------------------
|
|
10
|
+
# Part 1: keys of secrets.env (read from that FILE only, never from the process environment)
|
|
11
|
+
# ---------------------------------------------------------------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
# Akash Console API key (https://console.akash.network, API Keys). Required. It can spend your Akash balance: use a dedicated,
|
|
14
|
+
# low-balance account. It stays on this PC and is never sent to any rented box.
|
|
15
|
+
AKASH_API_KEY=
|
|
16
|
+
|
|
17
|
+
# Passphrase that encrypts checkpoints (gpg AES256). `ddc install` generates it if missing. It IS copied to the dev box so that the
|
|
18
|
+
# box can encrypt checkpoints. Keep a copy in a password manager: if it is lost, every checkpoint is unrecoverable.
|
|
19
|
+
DDC_BACKUP_PASSPHRASE=
|
|
20
|
+
|
|
21
|
+
# Optional. Claude Code login token for the dev box. Only sent when `push_claude_credentials = true` in config.toml (default: false).
|
|
22
|
+
CLAUDE_CODE_OAUTH_TOKEN=
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------------------------------------------------------------
|
|
25
|
+
# Part 2: process environment variables (all optional)
|
|
26
|
+
# ---------------------------------------------------------------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
# Where things live. Defaults: config in %APPDATA%\clouddeck (Windows) or ~/.config/clouddeck (Linux, macOS, WSL; honours
|
|
29
|
+
# XDG_CONFIG_HOME); secrets in ~/.config/clouddeck; state in %LOCALAPPDATA%\clouddeck (Windows) or ~/.local/state/clouddeck
|
|
30
|
+
# (honours XDG_STATE_HOME). DDC_CONFIG is a file path, the other two are directories.
|
|
31
|
+
# DDC_CONFIG=/path/to/config.toml
|
|
32
|
+
# DDC_SECRETS_DIR=/path/to/secrets-dir
|
|
33
|
+
# DDC_STATE_DIR=/path/to/state-dir
|
|
34
|
+
|
|
35
|
+
# Path of the gpg binary (default: gpg on PATH; on Windows also the gpg bundled with Git for Windows).
|
|
36
|
+
# DDC_GPG=/path/to/gpg
|
|
37
|
+
|
|
38
|
+
# Akash Console API base URL (default https://console-api.akash.network). Only change it to point at a test double.
|
|
39
|
+
# DDC_AKASH_API=https://console-api.akash.network
|
|
40
|
+
|
|
41
|
+
# CloudDeck: akash (default), demo (simulated, touches nothing) or a plugin name; the same as `clouddeck -p NAME`.
|
|
42
|
+
# CLOUDDECK_PROVIDER=demo
|
|
43
|
+
# CloudDeck: any non-empty value shows full tracebacks instead of a one-line error.
|
|
44
|
+
# CLOUDDECK_DEBUG=1
|
|
45
|
+
|
|
46
|
+
# `clouddeck-remote`, the client that `clouddeck serve --tunnel` installs on the dev box (set these on the BOX, not on your PC).
|
|
47
|
+
# URL default: http://127.0.0.1:8765 (the end of the SSH reverse tunnel). Token default: the file ~/.config/clouddeck/token
|
|
48
|
+
# that `clouddeck serve --tunnel` pushed.
|
|
49
|
+
# CLOUDDECK_URL=http://127.0.0.1:8765
|
|
50
|
+
# CLOUDDECK_TOKEN=cdk_example
|
|
51
|
+
|
|
52
|
+
# Test seams, used by the test suite and the simulated provider. Do not set these in normal use.
|
|
53
|
+
# DDC_STATE_URL= git URL used to verify checkpoints
|
|
54
|
+
# DDC_STATE_REMOTE= state repo URL on the lease
|
|
55
|
+
# CLOUDDECK_DEMO_LATENCY=0 seconds of fake latency in the demo provider
|
|
56
|
+
# CLOUDDECK_DEMO_SSH_FAIL=1 make the demo provider's interactive ssh fail
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
title = "CloudDeck gitleaks config"
|
|
2
|
+
|
|
3
|
+
# Start from gitleaks' built-in rules (GitHub/AWS/GCP tokens, private keys, generic API keys, ...) and add project-specific ones.
|
|
4
|
+
[extend]
|
|
5
|
+
useDefault = true
|
|
6
|
+
|
|
7
|
+
[[rules]]
|
|
8
|
+
id = "akash-api-key-assignment"
|
|
9
|
+
description = "Akash console API key assigned a real-looking value"
|
|
10
|
+
regex = '''(?i)AKASH_API_KEY["']?\s*[=:]\s*["']?[A-Za-z0-9._\-]{16,}'''
|
|
11
|
+
keywords = ["akash_api_key"]
|
|
12
|
+
|
|
13
|
+
[rules.allowlist]
|
|
14
|
+
description = "placeholders and obviously fake values"
|
|
15
|
+
regexes = ['''(?i)(your|xxx|example|placeholder|changeme|redacted|dummy|fake|sample|test|\$\{)''']
|
|
16
|
+
|
|
17
|
+
[[rules]]
|
|
18
|
+
id = "akash-address"
|
|
19
|
+
description = "Akash account address (identifies the owner's wallet; not a secret, but must not ship in examples)"
|
|
20
|
+
regex = '''\bakash1[0-9a-z]{38}\b'''
|
|
21
|
+
keywords = ["akash1"]
|
|
22
|
+
|
|
23
|
+
# No broad allowlist. The one entry below is a verified false positive: the fixture is a trimmed capture of Akash's public provider
|
|
24
|
+
# listing, whose attribute keys (capabilities/gpu/vendor/nvidia/<model>, ...) look like generic API keys. It holds no credentials.
|
|
25
|
+
[allowlist]
|
|
26
|
+
description = "public Akash provider attribute keys in the hardware test fixture"
|
|
27
|
+
paths = ['''^tests/fixtures/hardware/providers_sample\.json$''']
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
4
|
+
and the project uses [Semantic Versioning](https://semver.org/); while the version is `0.x`, minor releases may change behaviour and say so here.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-10-18
|
|
9
|
+
|
|
10
|
+
First public release (alpha).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
* **`ddc`, the controller** (`ddc init`, `install`, `uninstall`, `start`, `attach`, `scale`, `status`, `bench`, `checkpoint`, `verify`,
|
|
15
|
+
`stop`, `heal`, `reconcile`, `watchdog`, `ssh`, `run`): provisions a dev box and disposable workers on Akash through the Console API
|
|
16
|
+
(create, bids, lease, close), with a price cap and `runtimeLimitHours` in every order.
|
|
17
|
+
* **`clouddeck`, a terminal UI** with five tabs (Compute, Workers, Projects, Monitor, Recovery), a command palette, a provider catalogue
|
|
18
|
+
and GPU market view, live utilisation and spend, typed confirmations for destructive actions, a summary-line warning when the Akash API is
|
|
19
|
+
unreachable (the last known data stays on screen), and a simulated provider (`-p demo`).
|
|
20
|
+
Also `clouddeck status`, `doctor`, `providers`, `install`, `serve` and `remote`.
|
|
21
|
+
* **Hardware picker** (`h` on the Compute tab): choose any CPU architecture or GPU vendor, model, memory variant and interface in any count,
|
|
22
|
+
a region, audited providers only or pinned providers, from lists built from Akash's public data at run time (nothing is hard-coded, offline
|
|
23
|
+
falls back to the last saved list), with advertised, screened and confirmed availability kept apart. Needs no new limit: price cap and
|
|
24
|
+
runtime limit still go into every order, and `max_price_usd_hr` (default 2.0, at most 100) is the explicit ceiling for one price cap.
|
|
25
|
+
* **Encrypted checkpoints** (gpg AES256, split into 45 MiB parts) in a private GitHub repository you own, taken periodically and before
|
|
26
|
+
every stop, verified on the PC (part hashes, decryption, entry counts, git HEADs), restored on the next start.
|
|
27
|
+
* **Spending protection:** budget and reserve guard (on by default: $25 / $5), lease and worker caps, hard ceilings that hold with the guard off
|
|
28
|
+
(8 leases, 7 workers, $2.00/h price cap per profile by default, 24 h, 8 per call), `unsafe_disable_limits` as the only override, worst-case accounting that counts
|
|
29
|
+
creations whose outcome is unknown, and a lifetime ledger.
|
|
30
|
+
* **Watchdog** registered by `ddc install` (Windows scheduled task, Linux and WSL systemd user timer or cron): idle shutdown of the dev box
|
|
31
|
+
and workers, runtime and budget enforcement, cleanup of leases that never became ready, alerts (desktop notification, `alert_command`, log).
|
|
32
|
+
* **Ownership safety:** only leases recorded in `state.json` and named `ddc-*` (and owned by your account) can be closed; other
|
|
33
|
+
deployments are read-only.
|
|
34
|
+
* **Failure handling:** atomic state and ledger writes with a backup, damaged state or ledger blocks new leases but never blocks status or stop,
|
|
35
|
+
create requests are sent once and reconciled when the outcome is unknown, `ddc reconcile` repairs and adopts.
|
|
36
|
+
* **Remote controller API** for a terminal on the dev box: loopback only, SSH reverse tunnel, `read` / `fleet` / `admin` scopes, expiring
|
|
37
|
+
hashed tokens, rate and size limits, audit log.
|
|
38
|
+
* **Lease scripts** for the box (checkpoint, restore, activity agent, benchmark, worker fan-out) that fail safe: restore verifies before it
|
|
39
|
+
renames, checkpoints refuse truncated workspaces, foreign repositories and second writers, and pushes are atomic and lease-checked.
|
|
40
|
+
* **Configuration** with validated, safe defaults (`config.example.toml`), custom hardware profiles, projects, named environments, reusable
|
|
41
|
+
workloads, an optional pinned Claude Code install (`install_claude_code`) and opt-in credential push (`push_claude_credentials`, off by default).
|
|
42
|
+
* **Provider plugin contract** (`clouddeck.providers` entry points) with a mandatory spend-guard seam.
|
|
43
|
+
* **`ddc job`** (also `python -m clouddeck.job`): a generic runner for batch workloads on one short-lived lease (role `job`) with minimal paid
|
|
44
|
+
idle time: preflight, bid early / lease late, parallel local preparation, resumable detached steps, sha256-verified transfers, verified
|
|
45
|
+
close, per-phase telemetry. Docs: [docs/JOBS.md](docs/JOBS.md), a prebuilt-image guide, examples under `examples/` (in the repository and the
|
|
46
|
+
sdist, not in the wheel), a toolchain `docker/Dockerfile`, and an old-vs-new simulation (assumed, synthetic inputs) in the test suite.
|
|
47
|
+
Safety rules: artifact retrieval is capped (`max_artifact_gb`) and bound by the job's own cost and wall-clock limits, ssh host keys are kept
|
|
48
|
+
per lease, `env.secrets` refuses `AKASH_*` / `DDC_*`, downloads need a sha256 (or an explicit `allow_unverified`) and an http(s) URL, the GPU
|
|
49
|
+
vendor must be named, one run per job name, and a signal during the close never aborts it.
|
|
50
|
+
* **One creation path for every lease.** `check_budget` is now a thin wrapper around `check_spend(label, n, hours, cap_usd_hr, role)`, and
|
|
51
|
+
`launch_lease` (write-ahead record, one create request, promotion) is used by both `create_box` and `ddc job`: job leases are subject to
|
|
52
|
+
the lease caps, the hard ceilings (including the per-lease price cap), the budget guard and the unknown-outcome handling like any other lease.
|
|
53
|
+
Behaviour and messages of the existing commands are unchanged.
|
|
54
|
+
* **Orphaned job protection:** the runner records a heartbeat; `ddc watchdog` closes an owned job lease whose runner has been silent for
|
|
55
|
+
`job_orphan_minutes` (new optional config key, default 30) or that is past its own deadline, and alerts. The budget stop closes job leases
|
|
56
|
+
too. `ddc status`, `ddc stop`, `ddc reconcile` and the TUI handle role `job` (not resizable from the TUI). A plain `ddc stop` (and the TUI's
|
|
57
|
+
stop-everything) also closes owned job leases, as the user's kill switch; `--only` / `--workers-only` and the watchdog's idle stop keep their
|
|
58
|
+
narrower meaning. `job_orphan_minutes` has a floor of 10.
|
|
59
|
+
* `ssh_args` takes an optional `known_hosts` path (default unchanged); `close_box` no longer books a lease that another process already
|
|
60
|
+
recorded as closed.
|
|
61
|
+
* Documentation: quickstart, architecture, costs and safety, checkpoints, platforms, security policy, contributing guide.
|
|
62
|
+
* CI (tests on Linux for Python 3.11 and 3.13 and on Windows for 3.12, lint, shellcheck, wheel smoke test on Linux and Windows, secret
|
|
63
|
+
scanning, dependency audit), a tag-triggered release workflow (PyPI Trusted Publishing, GitHub release with SHA256SUMS) and a
|
|
64
|
+
release-hygiene checker (`scripts/release_check.py`).
|
|
65
|
+
* Preflight loads the ssh access key the way ssh does before any lease is created (a key other accounts can read, for example after
|
|
66
|
+
copying `secrets.env` and `keys\` to a new Windows PC, is refused locally with the `icacls` / `chmod` fix instead of a paid lease that
|
|
67
|
+
never answers ssh); on Windows `ddc install` makes the keys it generates owner-only. `clouddeck doctor` counts a scheduled watchdog only
|
|
68
|
+
when it runs this config.
|
|
69
|
+
|
|
70
|
+
### Upgrading from the pre-release `ddc.py`
|
|
71
|
+
|
|
72
|
+
* The command lock is an OS lock on `ddc.flock`; `ddc.lock` is left to the pre-release tool, and `ddc` waits while a pre-release
|
|
73
|
+
`ddc.lock` (holding `<pid> <time>`) younger than 3 h exists. Disable the pre-release watchdog task before the first `ddc` command: the
|
|
74
|
+
pre-release tool does not see `ddc.flock`. `ddc install --migrate` replaces the old watchdog task and also rewrites a
|
|
75
|
+
`%USERPROFILE%\bin\clouddeck.cmd` that points elsewhere (`--dry-run` lists the shims).
|
|
76
|
+
* Scheduled watchdogs (Windows task, systemd, cron) are registered with `--config`, and a watchdog run that cannot load its config or finds
|
|
77
|
+
conflicting state dirs raises an alert instead of failing silently.
|
|
78
|
+
* Every checkpoint first re-sends the lease env, so a changed `state_repo` (for example after the state repository moved to another owner) takes effect without
|
|
79
|
+
restarting the box; restored push repos get `origin` set to their configured `github`; the checkpoint verification clone never prompts
|
|
80
|
+
and fails closed after 600 s.
|
|
81
|
+
|
|
82
|
+
### Known limitations
|
|
83
|
+
|
|
84
|
+
See [SECURITY.md](SECURITY.md#known-limitations) and [docs/COSTS-AND-SAFETY.md](docs/COSTS-AND-SAFETY.md#what-is-not-bounded). In short:
|
|
85
|
+
the platform runtime limit was observed working once (not a guarantee), the watchdog needs your PC, SSH host keys are trusted on first use, deploy keys are
|
|
86
|
+
not per-lease, checkpoint verification is not a content digest, macOS is unsupported (no watchdog installer), and a native Linux host has not been used against real
|
|
87
|
+
leases (WSL2 has). What was checked against real leases: README, "Verified and not verified".
|
|
88
|
+
|
|
89
|
+
[Unreleased]: https://github.com/trexchery/clouddeck/compare/v0.1.0...HEAD
|
|
90
|
+
[0.1.0]: https://github.com/trexchery/clouddeck/releases/tag/v0.1.0
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
+
community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
+
any kind
|
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
* Public or private harassment
|
|
34
|
+
* Publishing others' private information, such as a physical or email address,
|
|
35
|
+
without their explicit permission
|
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
+
decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
+
an individual is officially representing the community in public spaces.
|
|
55
|
+
Examples of representing our community include using an official email address,
|
|
56
|
+
posting via an official social media account, or acting as an appointed
|
|
57
|
+
representative at an online or offline event.
|
|
58
|
+
|
|
59
|
+
## Enforcement
|
|
60
|
+
|
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
+
reported to the community leaders responsible for enforcement through GitHub's
|
|
63
|
+
private reporting form for this repository:
|
|
64
|
+
<https://github.com/trexchery/clouddeck/security/advisories/new> (start the title
|
|
65
|
+
with "Conduct:"). Please do not use public issues for conduct reports.
|
|
66
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
67
|
+
|
|
68
|
+
All community leaders are obligated to respect the privacy and security of the
|
|
69
|
+
reporter of any incident.
|
|
70
|
+
|
|
71
|
+
## Enforcement Guidelines
|
|
72
|
+
|
|
73
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
|
74
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
|
75
|
+
|
|
76
|
+
### 1. Correction
|
|
77
|
+
|
|
78
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
79
|
+
unprofessional or unwelcome in the community.
|
|
80
|
+
|
|
81
|
+
**Consequence**: A private, written warning from community leaders, providing
|
|
82
|
+
clarity around the nature of the violation and an explanation of why the
|
|
83
|
+
behavior was inappropriate. A public apology may be requested.
|
|
84
|
+
|
|
85
|
+
### 2. Warning
|
|
86
|
+
|
|
87
|
+
**Community Impact**: A violation through a single incident or series of
|
|
88
|
+
actions.
|
|
89
|
+
|
|
90
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
|
91
|
+
interaction with the people involved, including unsolicited interaction with
|
|
92
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
93
|
+
includes avoiding interactions in community spaces as well as external channels
|
|
94
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
|
95
|
+
ban.
|
|
96
|
+
|
|
97
|
+
### 3. Temporary Ban
|
|
98
|
+
|
|
99
|
+
**Community Impact**: A serious violation of community standards, including
|
|
100
|
+
sustained inappropriate behavior.
|
|
101
|
+
|
|
102
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
103
|
+
communication with the community for a specified period of time. No public or
|
|
104
|
+
private interaction with the people involved, including unsolicited interaction
|
|
105
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
|
106
|
+
Violating these terms may lead to a permanent ban.
|
|
107
|
+
|
|
108
|
+
### 4. Permanent Ban
|
|
109
|
+
|
|
110
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
111
|
+
standards, including sustained inappropriate behavior, harassment of an
|
|
112
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
113
|
+
|
|
114
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
115
|
+
community.
|
|
116
|
+
|
|
117
|
+
## Attribution
|
|
118
|
+
|
|
119
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
120
|
+
version 2.1, available at
|
|
121
|
+
<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
|
|
122
|
+
|
|
123
|
+
Community Impact Guidelines were inspired by
|
|
124
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
125
|
+
|
|
126
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
127
|
+
<https://www.contributor-covenant.org/faq>. Translations are available at
|
|
128
|
+
<https://www.contributor-covenant.org/translations>.
|
|
129
|
+
|
|
130
|
+
[homepage]: https://www.contributor-covenant.org
|
|
131
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Contributing to CloudDeck
|
|
2
|
+
|
|
3
|
+
Thanks for helping. CloudDeck is alpha software that moves real money, so the bar for changes is "boring, tested and safe". Small,
|
|
4
|
+
focused pull requests are easiest to review. Please read the [Code of Conduct](CODE_OF_CONDUCT.md); report security problems privately
|
|
5
|
+
(see [SECURITY.md](SECURITY.md)), not as issues.
|
|
6
|
+
|
|
7
|
+
By contributing you agree that your contribution is licensed under the [MIT license](LICENSE). Much of this code base was written with
|
|
8
|
+
AI assistance and contributions may be too; you are responsible for understanding, testing and standing behind what you submit.
|
|
9
|
+
|
|
10
|
+
## The one rule
|
|
11
|
+
|
|
12
|
+
> **Tests never spend real money and never touch real state.**
|
|
13
|
+
> No Akash API key, no real lease, no real `secrets.env`, `state.json` or keys directory, no network call to the Akash API (not even a
|
|
14
|
+
> read-only one) from a test.
|
|
15
|
+
|
|
16
|
+
Tests use the simulated provider (`DemoProvider`, `clouddeck -p demo`) and the in-memory Akash and lease fake in `tests/fake_akash.py`
|
|
17
|
+
(the `fake` fixture in `tests/conftest.py`). External responses are saved fixtures. Use neutral names in tests, fixtures and docs
|
|
18
|
+
(`prod-box`, `web-app`, `my-project`, `provider-a.example`), and never real deployment numbers, account addresses, host names or paths.
|
|
19
|
+
|
|
20
|
+
## Development setup
|
|
21
|
+
|
|
22
|
+
You need Python 3.11+ and git. For the lease-script tests you also want bash, GnuPG, `flock` and `tar` (Linux or WSL; they are
|
|
23
|
+
skipped with a reason elsewhere).
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/trexchery/clouddeck.git && cd clouddeck
|
|
27
|
+
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
pip install ruff==0.17.1 # the version CI uses
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Optional: `pip install -e ".[pty-tests]"` for the terminal smoke test (`python tests/pty_smoke.py`), `pip install shellcheck-py` for
|
|
33
|
+
the lease scripts, `pip install pre-commit && pre-commit install` for the same checks as CI before every commit.
|
|
34
|
+
|
|
35
|
+
## Running the tests
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python -m pytest -p no:cacheprovider
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The suite is offline and takes a few minutes. Run plain `pytest` or a single file; mixing `tests/...` paths with the root `test_ddc.py`
|
|
42
|
+
in an unusual order can lose the per-test isolation fixture (there are two `conftest.py` files). The TUI tests are timing-sensitive on a
|
|
43
|
+
heavily loaded machine: rerun a lone failure before investigating.
|
|
44
|
+
|
|
45
|
+
The `ddc job` tests (`tests/test_job_*.py`) run the real runner on virtual time against a stubbed Akash API and a fake box
|
|
46
|
+
(`tests/job_fakes.py`, `tests/job_helpers.py`; the `jw` fixture) inside the same isolated state as every other test. `tests/job_sim.py` is the
|
|
47
|
+
old-vs-new simulation with assumed, synthetic inputs (from a checkout with the dev extra, `python tests/job_sim.py` runs it standalone in a throw-away state dir). `tests/test_job_scripts.py` runs the real
|
|
48
|
+
`jobremote/*.sh` scripts under bash and skips cleanly where bash (or curl, or shellcheck) is missing.
|
|
49
|
+
|
|
50
|
+
Opt-in extras:
|
|
51
|
+
|
|
52
|
+
* `CLOUDDECK_TEST_WHEEL=1 python -m pytest tests/test_packaging.py` builds a wheel in a temp copy and runs it in a clean venv (needs
|
|
53
|
+
network for the build backend).
|
|
54
|
+
* `python tests/pty_smoke.py` drives the real TUI through a pseudo-terminal.
|
|
55
|
+
|
|
56
|
+
### Redirect the environment first (and why)
|
|
57
|
+
|
|
58
|
+
The controller reads its config, state and secrets locations from the environment **when it is imported**. A test run once resolved the
|
|
59
|
+
real directories and overwrote a live `state.json`: the file that records which leases are billing. Two protections exist now:
|
|
60
|
+
|
|
61
|
+
1. the root `conftest.py` points `DDC_STATE_DIR`, `DDC_SECRETS_DIR` and `DDC_CONFIG` at fresh temp directories before anything is
|
|
62
|
+
imported, and the controller **refuses to load under pytest** unless `DDC_STATE_DIR` and `DDC_SECRETS_DIR` are both set;
|
|
63
|
+
2. *you*, every time you run pytest, a script that imports `clouddeck`, or the CLI during development, should also redirect the
|
|
64
|
+
environment, so that a mistake cannot reach your real directories.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Linux / macOS / WSL
|
|
68
|
+
S=$(mktemp -d)
|
|
69
|
+
export HOME=$S/home XDG_CONFIG_HOME=$S/xdg/config XDG_STATE_HOME=$S/xdg/state XDG_DATA_HOME=$S/xdg/data TMPDIR=$S/tmp
|
|
70
|
+
export DDC_STATE_DIR=$S/state DDC_SECRETS_DIR=$S/secrets PYTHONDONTWRITEBYTECODE=1
|
|
71
|
+
mkdir -p "$HOME" "$TMPDIR"
|
|
72
|
+
python -m pytest -p no:cacheprovider
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
# Windows PowerShell
|
|
77
|
+
$S = Join-Path $env:TEMP ("clouddeck-" + [guid]::NewGuid()); New-Item -ItemType Directory $S\home, $S\local, $S\roaming, $S\tmp | Out-Null
|
|
78
|
+
$env:USERPROFILE = "$S\home"; $env:HOME = "$S\home"; $env:LOCALAPPDATA = "$S\local"; $env:APPDATA = "$S\roaming"
|
|
79
|
+
$env:TEMP = "$S\tmp"; $env:TMP = "$S\tmp"
|
|
80
|
+
$env:DDC_STATE_DIR = "$S\state"; $env:DDC_SECRETS_DIR = "$S\secrets"; $env:PYTHONDONTWRITEBYTECODE = "1"
|
|
81
|
+
python -m pytest -p no:cacheprovider
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Do the same in a throwaway shell when you try `ddc` or `clouddeck` by hand, unless you really mean to use your real setup.
|
|
85
|
+
|
|
86
|
+
## Adding or changing things
|
|
87
|
+
|
|
88
|
+
* **Every behaviour change gets an offline test**, and a bug fix gets a test that fails without the fix. Spending, ownership,
|
|
89
|
+
checkpoint and watchdog paths are the most important to cover: look at `tests/test_safety_*.py` for the pattern.
|
|
90
|
+
* **Keep the compatibility promises** ([ARCHITECTURE.md](docs/ARCHITECTURE.md#compatibility-promises)): the `ddc-` lease prefix and `ours`
|
|
91
|
+
semantics, the `state.json` / `ledger.jsonl` / `custom_profiles.json` formats, the checkpoint layout, the lease-side paths, and the
|
|
92
|
+
`DDC_*`, `AKASH_API_KEY` and `CLOUDDECK_*` names. If a change must break one, say so in the PR and in the changelog.
|
|
93
|
+
* **Fail closed.** When something is unknown (an empty API answer, a damaged file, an unreadable balance), do not assume the safe-looking
|
|
94
|
+
answer; refuse to spend and keep close/stop paths working.
|
|
95
|
+
* **No new runtime dependency** without a discussion: the controller and the lease scripts are standard-library only on purpose.
|
|
96
|
+
* **Lease scripts** (`lease/`): every file in that directory is uploaded to every lease, so no subdirectories; LF line endings; bash
|
|
97
|
+
(`set -euo pipefail`); `shellcheck -x -S warning -P lease lease/*.sh` must be clean; document new environment variables in
|
|
98
|
+
`lease/README.md`. Most tests for them need bash, `gpg`, `flock` and `python3` (Linux/WSL).
|
|
99
|
+
* **Job scripts** (`jobremote/`): the same rules as lease scripts (LF, executable bit in git: `git update-index --chmod=+x`, shellcheck clean);
|
|
100
|
+
`boot.sh` is inlined into the SDL, `step.sh` / `fetch.sh` are uploaded. Anything that creates or closes a lease must go through
|
|
101
|
+
`launch_lease` / `close_box`, never a direct API call (a test greps for it).
|
|
102
|
+
* **A new provider**: see [Extension points](docs/ARCHITECTURE.md#extension-points). It must route spending through `Provider.require`.
|
|
103
|
+
* **Docs**: update the relevant file under `docs/`, the README command table and `config.example.toml` when behaviour or configuration
|
|
104
|
+
changes. Do not document a flag you have not run `--help` on.
|
|
105
|
+
|
|
106
|
+
## Style
|
|
107
|
+
|
|
108
|
+
* Python: `ruff check .` must pass (configuration in `pyproject.toml`: rules E, F, W, I, B, UP, target 3.11, long lines are allowed).
|
|
109
|
+
`ruff format` is enforced only for `scripts/` and `tests/test_release_check.py`; elsewhere match the surrounding code (the code base
|
|
110
|
+
is compact), and do not reformat files you are not changing.
|
|
111
|
+
* Prefer the shortest change that works; no abstractions for a single caller, no speculative configuration.
|
|
112
|
+
* Error messages say what happened and what to do next. Never print secrets, tokens or key material, not even in debug output.
|
|
113
|
+
|
|
114
|
+
## Release hygiene checks
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python scripts/release_check.py --no-history # working tree: tracked and untracked-not-ignored files
|
|
118
|
+
python scripts/release_check.py # plus every commit, every blob, every commit message
|
|
119
|
+
python scripts/release_check.py --strict # also fail on placeholders and commit identities (before publishing)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
It fails on real-looking secrets, API keys, account addresses, personal absolute paths, private Claude session links, forbidden file
|
|
123
|
+
names and missing required files. It prints locations and rule names only, never the matched text. With `--strict` it also fails on an
|
|
124
|
+
`OWNER` placeholder left in this project's own GitHub URL and on unfilled release-manager markers (the README's live-verification
|
|
125
|
+
table); placeholders meant for the user, such as `OWNER/your-private-state-repo`, are fine. Private words (names, private project
|
|
126
|
+
names, deployment ids) are never written into the public script: maintainers keep them in `scripts/release_denylist.local.txt`
|
|
127
|
+
(gitignored; one case-insensitive regex per line, `#` comments) or point `CLOUDDECK_RELEASE_DENYLIST` at such a file. The few legacy
|
|
128
|
+
compatibility strings that are allowed live in `scripts/release_allowlist.toml`, and every entry needs a `reason`: keep it small. The
|
|
129
|
+
allowlist is scanned too, except inside its own `contains` values.
|
|
130
|
+
|
|
131
|
+
Secret scanning uses [gitleaks](https://github.com/gitleaks/gitleaks) (CI pins version 8.30.1):
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
gitleaks dir --config .gitleaks.toml --redact --no-banner .
|
|
135
|
+
gitleaks git --config .gitleaks.toml --redact --no-banner --log-opts="--all" .
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`pre-commit` runs gitleaks, ruff, shellcheck, `detect-private-key` and the tree-only release check.
|
|
139
|
+
|
|
140
|
+
Building and checking the artifacts (CI does this too):
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pip install build twine && python -m build && python -m twine check --strict dist/*
|
|
144
|
+
python scripts/check_dist.py dist/*.whl dist/*.tar.gz
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Remove `build/`, `dist/` and `*.egg-info/` afterwards; they are ignored by git but should not linger.
|
|
148
|
+
|
|
149
|
+
With `SOURCE_DATE_EPOCH` set to the commit time (`git log -1 --format=%ct`), the wheel is bit-for-bit reproducible. The sdist is
|
|
150
|
+
content-reproducible only: the same members, order and contents, but tar and gzip timestamps differ between builds. Release artifacts
|
|
151
|
+
are built on Linux (`.github/workflows/release.yml`) so the sdist keeps the executable bits of the shell scripts.
|
|
152
|
+
|
|
153
|
+
## Running the CI gates locally
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
python scripts/ci_local.py # ruff, pytest (redirected env, -n auto with pytest-xdist), release_check --no-history,
|
|
157
|
+
# build + twine check + check_dist, shellcheck if installed
|
|
158
|
+
python scripts/ci_local.py --release # the same with release_check --strict over tree + history, as the release workflow runs it
|
|
159
|
+
python scripts/ci_local.py --no-build # skip the build
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
It needs `pip install -e ".[dev]" ruff==0.17.1 build twine` (and optionally `pytest-xdist`, `shellcheck-py`). It does not run the
|
|
163
|
+
Windows/Linux matrix, the clean-venv wheel smoke test, actionlint, gitleaks or pip-audit; CI does.
|
|
164
|
+
|
|
165
|
+
## Releasing
|
|
166
|
+
|
|
167
|
+
Push a `vX.Y.Z` tag on a commit that is green on `main`, after moving the `[Unreleased]` entries of `CHANGELOG.md` into a dated `[X.Y.Z]`
|
|
168
|
+
section, setting the version in `pyproject.toml` and `clouddeck/__init__.py` and filling in the README's live-verification table.
|
|
169
|
+
`.github/workflows/release.yml` checks that the tag, the version and the changelog agree, runs `release_check.py --strict`, builds and
|
|
170
|
+
checks the artifacts, publishes to PyPI through Trusted Publishing when the repository variable `PYPI_PUBLISH` is `true`, and creates the
|
|
171
|
+
GitHub release with the wheel, the sdist, `SHA256SUMS` and that changelog section as notes.
|
|
172
|
+
|
|
173
|
+
## Pull request checklist
|
|
174
|
+
|
|
175
|
+
- [ ] Offline tests added or updated, and `python -m pytest -p no:cacheprovider` passes (with the redirected environment above)
|
|
176
|
+
- [ ] `ruff check .` is clean (and `shellcheck` for `lease/` changes)
|
|
177
|
+
- [ ] `python scripts/release_check.py --no-history` passes: no secrets, personal paths, account names or real host names
|
|
178
|
+
- [ ] No test, fixture or doc depends on real money, real credentials or real state
|
|
179
|
+
- [ ] Compatibility kept, or the break is called out
|
|
180
|
+
- [ ] Docs, `config.example.toml` and `CHANGELOG.md` updated if behaviour or configuration changed
|
|
181
|
+
- [ ] The change is as small as it can be
|
clouddeck-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CloudDeck contributors
|
|
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.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
include LICENSE README.md SECURITY.md CONTRIBUTING.md CODE_OF_CONDUCT.md CHANGELOG.md THIRD_PARTY_NOTICES.md
|
|
2
|
+
include .env.example .gitleaks.toml
|
|
3
|
+
include config.example.toml sdl.yaml.tmpl ddc.py conftest.py test_ddc.py requirements.txt
|
|
4
|
+
recursive-include lease *
|
|
5
|
+
recursive-include jobremote *
|
|
6
|
+
recursive-include docker *
|
|
7
|
+
recursive-include examples *
|
|
8
|
+
recursive-include tests *
|
|
9
|
+
recursive-include scripts *
|
|
10
|
+
recursive-include windows *
|
|
11
|
+
recursive-include bin *
|
|
12
|
+
recursive-include docs *
|
|
13
|
+
global-exclude __pycache__ *.py[cod]
|
|
14
|
+
exclude scripts/release_denylist.local*
|