treestamp 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.
Files changed (49) hide show
  1. treestamp-0.1.0/.gitignore +145 -0
  2. treestamp-0.1.0/.pre-commit-config.yaml +51 -0
  3. treestamp-0.1.0/.pre-commit-hooks.yaml +14 -0
  4. treestamp-0.1.0/.python-version +1 -0
  5. treestamp-0.1.0/CHANGELOG.md +17 -0
  6. treestamp-0.1.0/LICENSE +21 -0
  7. treestamp-0.1.0/MAINTENANCE.md +146 -0
  8. treestamp-0.1.0/Makefile +116 -0
  9. treestamp-0.1.0/PKG-INFO +205 -0
  10. treestamp-0.1.0/PRIVACY.md +3 -0
  11. treestamp-0.1.0/README.md +173 -0
  12. treestamp-0.1.0/pyproject.toml +133 -0
  13. treestamp-0.1.0/scripts/changed_tests.py +76 -0
  14. treestamp-0.1.0/scripts/check_docs.py +143 -0
  15. treestamp-0.1.0/scripts/distribution_check.py +173 -0
  16. treestamp-0.1.0/scripts/hook_check.py +69 -0
  17. treestamp-0.1.0/scripts/installed_editor_check.py +57 -0
  18. treestamp-0.1.0/scripts/release_check.py +151 -0
  19. treestamp-0.1.0/src/treestamp/__init__.py +0 -0
  20. treestamp-0.1.0/src/treestamp/__main__.py +10 -0
  21. treestamp-0.1.0/src/treestamp/cli.py +197 -0
  22. treestamp-0.1.0/src/treestamp/codec.py +515 -0
  23. treestamp-0.1.0/src/treestamp/document.py +476 -0
  24. treestamp-0.1.0/src/treestamp/editor.py +202 -0
  25. treestamp-0.1.0/src/treestamp/errors.py +41 -0
  26. treestamp-0.1.0/src/treestamp/gitio.py +337 -0
  27. treestamp-0.1.0/src/treestamp/maintenance.py +115 -0
  28. treestamp-0.1.0/src/treestamp/model.py +266 -0
  29. treestamp-0.1.0/src/treestamp/py.typed +0 -0
  30. treestamp-0.1.0/src/treestamp/session.py +327 -0
  31. treestamp-0.1.0/src/treestamp/sources.py +103 -0
  32. treestamp-0.1.0/src/treestamp/tui.py +688 -0
  33. treestamp-0.1.0/src/treestamp/tui_dialogs.py +187 -0
  34. treestamp-0.1.0/tests/__init__.py +0 -0
  35. treestamp-0.1.0/tests/conftest.py +107 -0
  36. treestamp-0.1.0/tests/test_changed_tests.py +45 -0
  37. treestamp-0.1.0/tests/test_cli.py +358 -0
  38. treestamp-0.1.0/tests/test_codec.py +576 -0
  39. treestamp-0.1.0/tests/test_document.py +486 -0
  40. treestamp-0.1.0/tests/test_editor.py +323 -0
  41. treestamp-0.1.0/tests/test_gitio.py +250 -0
  42. treestamp-0.1.0/tests/test_maintenance.py +200 -0
  43. treestamp-0.1.0/tests/test_output_bytes.py +205 -0
  44. treestamp-0.1.0/tests/test_paths.py +191 -0
  45. treestamp-0.1.0/tests/test_release_check.py +65 -0
  46. treestamp-0.1.0/tests/test_session.py +300 -0
  47. treestamp-0.1.0/tests/test_sources.py +187 -0
  48. treestamp-0.1.0/tests/test_tui.py +681 -0
  49. treestamp-0.1.0/tests/test_tui_dialogs.py +78 -0
@@ -0,0 +1,145 @@
1
+ # Dependencies
2
+ node_modules/
3
+ npm-debug.log*
4
+ yarn-debug.log*
5
+ yarn-error.log*
6
+ pnpm-debug.log*
7
+ .pnpm-store/
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ *$py.class
13
+ *.so
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+ MANIFEST
31
+ .venv/
32
+ venv/
33
+ ENV/
34
+ env/
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # TypeScript
39
+ *.tsbuildinfo
40
+ .tsc-cache/
41
+
42
+ # Testing
43
+ coverage/
44
+ .nyc_output/
45
+ .pytest_cache/
46
+ htmlcov/
47
+ .tox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ .hypothesis/
55
+
56
+ # IDEs and editors
57
+ .vscode/
58
+ .idea/
59
+ *.swp
60
+ *.swo
61
+ *~
62
+ .project
63
+ .classpath
64
+ .c9/
65
+ *.launch
66
+ .settings/
67
+ *.sublime-workspace
68
+ *.sublime-project
69
+
70
+ # OS files
71
+ .DS_Store
72
+ Thumbs.db
73
+ ehthumbs.db
74
+ Desktop.ini
75
+ $RECYCLE.BIN/
76
+ *.cab
77
+ *.msi
78
+ *.msm
79
+ *.msp
80
+
81
+ # Logs
82
+ logs/
83
+ *.log
84
+ npm-debug.log*
85
+ yarn-debug.log*
86
+ yarn-error.log*
87
+ lerna-debug.log*
88
+
89
+ # Runtime data
90
+ pids/
91
+ *.pid
92
+ *.seed
93
+ *.pid.lock
94
+
95
+ # Environment variables
96
+ .env
97
+ .env.local
98
+ .env.development.local
99
+ .env.test.local
100
+ .env.production.local
101
+
102
+ # Build outputs
103
+ dist/
104
+ build/
105
+ out/
106
+ .next/
107
+ .nuxt/
108
+ .cache/
109
+ .parcel-cache/
110
+
111
+ # Temporary files
112
+ .tmp/
113
+ .temp/
114
+ tmp/
115
+ temp/
116
+
117
+ # Package files
118
+ *.tgz
119
+ *.tar.gz
120
+ *.zip
121
+
122
+ # Database
123
+ *.sqlite
124
+ *.sqlite3
125
+ *.db
126
+
127
+ # Miscellaneous
128
+ .eslintcache
129
+ .stylelintcache
130
+ *.bak
131
+ *.orig
132
+
133
+ # PERSONAL
134
+
135
+ INTERNAL
136
+
137
+ sync-from-*
138
+
139
+ # Treestamp tooling and local working material
140
+ .stamps/
141
+ .mypy_cache/
142
+ .ruff_cache/
143
+ .playwright-cli/
144
+ output/
145
+ __INTERNAL__/
@@ -0,0 +1,51 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ exclude: ^assets/TREESTAMP_BANNER\.png$
10
+ - id: check-merge-conflict
11
+
12
+ - repo: https://github.com/astral-sh/ruff-pre-commit
13
+ rev: v0.9.7
14
+ hooks:
15
+ - id: ruff
16
+ args: [--fix]
17
+ - id: ruff-format
18
+
19
+ - repo: local
20
+ hooks:
21
+ - id: mypy
22
+ name: mypy (project environment)
23
+ entry: make typecheck
24
+ language: system
25
+ types: [python]
26
+ pass_filenames: false
27
+ - id: web-typecheck
28
+ name: mypy (hosted preview)
29
+ entry: make web-typecheck
30
+ language: system
31
+ files: ^web/
32
+ types: [python]
33
+ pass_filenames: false
34
+ - id: pytest
35
+ name: pytest (package, changed files)
36
+ entry: venv/bin/python scripts/changed_tests.py package
37
+ language: system
38
+ files: ^(src/|tests/|pyproject\.toml|Makefile)
39
+ pass_filenames: true
40
+ - id: web-test
41
+ name: pytest (hosted preview, changed files)
42
+ entry: venv/bin/python scripts/changed_tests.py web
43
+ language: system
44
+ files: ^(web/src/|web/tests/|web/pyproject\.toml|src/)
45
+ pass_filenames: true
46
+ - id: treestamp-check
47
+ name: treestamp check (this repository's README)
48
+ entry: make integration-check
49
+ language: system
50
+ pass_filenames: false
51
+ always_run: true
@@ -0,0 +1,14 @@
1
+ - id: treestamp
2
+ name: treestamp update
3
+ description: Maintain the README tree from staged Git paths
4
+ entry: treestamp update
5
+ language: python
6
+ pass_filenames: false
7
+ always_run: true
8
+ - id: treestamp-check
9
+ name: treestamp check
10
+ description: Fail if the README tree needs maintenance
11
+ entry: treestamp check
12
+ language: python
13
+ pass_filenames: false
14
+ always_run: true
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-10-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * import treestamp public source tree ([764f4e9](https://github.com/reponomics/treestamp/commit/764f4e9bd9e9630d628c86e90f4d813902a85e6e))
9
+ * prepare release and production deployment automation ([#1](https://github.com/reponomics/treestamp/issues/1)) ([25722b8](https://github.com/reponomics/treestamp/commit/25722b884e29dd8a4ff65e22c782f4e11ea98922))
10
+
11
+ ## 0.1.0 (Unreleased)
12
+
13
+ - Create and curate README directory trees in the terminal or web editor.
14
+ - Preserve expanded directories, hidden paths, and single-line annotations when updating a tree.
15
+ - Check and update documents from the Git index or a locally available commit, with pre-commit hooks for automation.
16
+ - Export Unicode, ASCII, or Markdown list trees with optional child counts.
17
+ - Edit public GitHub repository snapshots in the hosted editor and copy the result into your project.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Reponomics
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,146 @@
1
+ # Maintenance
2
+
3
+ These procedures require a full Git checkout, including the hosted application in `web/`. Use the environment and contributor setup in [CONTRIBUTING](https://github.com/reponomics/treestamp/blob/main/.github/CONTRIBUTING.md). Run `make help` for available commands. Stage source changes before updating the README tree because Treestamp reads the Git index.
4
+
5
+ ## Validate and build
6
+
7
+ ```bash
8
+ make ci web-ci docs-check
9
+ make web-browser-test
10
+ make build metadata-check distribution-check
11
+ make integration-check
12
+ ```
13
+
14
+ Browser checks require the separate browser dependencies described in [the web README](https://github.com/reponomics/treestamp/blob/main/web/README.md). `metadata-check` validates both distributions against the approved file set and reports their hashes. `distribution-check` installs the wheel and source archive into fresh environments and exercises headless commands and the terminal editor. After committing a candidate, `make hook-check` checks the public pre-commit hooks from that exact HEAD.
15
+
16
+ Build release artifacts from a clean, reviewed revision. Inspect the archive contents before publication; internal notes and unrelated products do not belong in either distribution. Remove or archive older files from `dist/` before a release build so checks and uploads cannot select stale artifacts.
17
+
18
+ ## Dependency and installer maintenance
19
+
20
+ The core and hosted projects share `uv.lock`. `make install` installs locked dependencies and build tools into `venv`, then builds the local editable packages without isolated dependency resolution. `make build` synchronizes that locked toolchain before building. Docker selects only the hosted runtime dependencies and operational TLS tools from the same lock, builds both local wheels with the locked build group, and installs those wheels offline. Third-party dependencies must have compatible wheels; the controlled install fails rather than resolving unpinned build dependencies for a source archive.
21
+
22
+ The uv version is pinned in `.tool-versions`, checked by the Makefile, and read by CI. Docker uses the matching version and image digest. Update those together, including when Dependabot proposes an update to the Docker image; Dependabot does not maintain the `.tool-versions` pin. Keep pip at least `26.2.2`, certifi at least `2026.10.19`, and uv at least `0.13.4` for the eMudhra certificate incident. Continue updating the pinned Python base image and checking its system certificate store; the Python dependency lock does not manage operating-system certificates.
23
+
24
+ Dependabot proposes uv dependency updates. Review lock changes with their declarations, run the quality gates and container checks, and commit both together. Release Please also updates the two local package versions in the lock. The clean-distribution checks intentionally use pip to resolve the published dependency ranges outside the workspace; they test consumer compatibility separately from the locked development environment. Revert a dependency change by reverting its declarations and lock together, then synchronizing the environment again.
25
+
26
+ ## Repository and release setup
27
+
28
+ Use `main` as the default branch and require pull requests and signed commits. After the checks have run in the repository, select these required PR check names:
29
+
30
+ - `Regression suite (Linux, Python 3.11)`
31
+ - `Regression suite (Linux, Python 3.12)`
32
+ - `Regression suite (Windows, Python 3.11)`
33
+ - `Regression suite (Windows, Python 3.12)`
34
+ - `Hosted editor (Linux, Python 3.11)`
35
+ - `README tree freshness`
36
+ - `Validate PR title`
37
+
38
+ Production deployment, Release Please, and PyPI publication do not run on ordinary PRs and should not be required PR checks. Enable immutable GitHub Releases before the first release. CodeQL uses GitHub's default setup with the extended query suite for Python, JavaScript/TypeScript, and GitHub Actions, managed in the repository's code scanning settings. Do not enable an advanced CodeQL workflow alongside default setup: GitHub rejects its analysis uploads. Scorecard runs only when the repository is public; OSV scans resolved Python dependencies while private, retaining its report in Actions without uploading it to code scanning.
39
+
40
+ Release Please manages the core and hosted versions together, updates `CHANGELOG.md`, and creates version tags and GitHub Releases after its release PR is merged. The published-release event runs the PyPI workflow, which validates the tag, versions, and commit on `main`, builds and tests fresh distributions, and publishes them with attestations through Trusted Publishing. Website deployment has its own trigger and does not wait for a package release.
41
+
42
+ Configure these GitHub settings before enabling publication or production deployment:
43
+
44
+ | Setting | Location | Purpose |
45
+ | --- | --- | --- |
46
+ | `RELEASE_APP_CLIENT_ID` | Repository variable | GitHub App used by Release Please |
47
+ | `RELEASE_APP_PRIVATE_KEY` | Repository secret | Private key for that App |
48
+ | `PYPI_PUBLISH_ENABLED=true` | Repository variable | Enables PyPI publication once the repository is public |
49
+ | `pypi` | GitHub environment | Publication approvals and PyPI's trusted identity |
50
+ | `FLY_DEPLOY_ENABLED=true` | Repository variable | Enables independent production deployment on pushes to `main` |
51
+ | `FLY_API_TOKEN` | Secret in the `production` environment | Deploy token scoped to the production Fly app |
52
+
53
+ Install the release App on the selected `reponomics/treestamp` repository with write access to contents, issues, and pull requests. Its token lets release PRs trigger CI and GitHub Releases trigger publication. The App does not need organization-wide repository access. The `pypi` environment exists; review its protections and those of `production` against the maintainers' approval policy. Publication runs from release tags, while deployment runs from `main`.
54
+
55
+ In PyPI, configure a [pending Trusted Publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/) for project `treestamp`, GitHub owner `reponomics`, repository `treestamp`, workflow filename `publish.yml`, and environment `pypi`. No long-lived PyPI upload token is needed. A pending publisher does not reserve the package name.
56
+
57
+ ## First release and subsequent releases
58
+
59
+ The first release is configured as `0.1.0`. Complete these steps in order:
60
+
61
+ 1. Merge the automation setup to `main`, configure the release App, enable immutable releases, and configure the `pypi` environment and Trusted Publisher.
62
+ 2. Finish the public-content review, make the repository public, enable private vulnerability reporting, and set `PYPI_PUBLISH_ENABLED=true`.
63
+ 3. Review Release Please's first release PR. Confirm that both project versions and its manifest say `0.1.0`. Consolidate the existing `0.1.0 (Unreleased)` notes into the generated dated release section so the changelog describes the release once.
64
+ 4. After its required checks pass, merge that release PR. Release Please creates the tag and GitHub Release; the publication workflow validates and uploads the package. Verify the PyPI files, installation, and attestations.
65
+
66
+ Do not merge the first release PR before publication is ready: a release created while the repository is private or publication is disabled will not publish automatically when those settings change later. Subsequent releases follow the same release-PR process. Explain changes requiring user action in the changelog, including format changes before `1.0`.
67
+
68
+ Published versions are immutable. Correct a released package through a later release; do not move its tag, delete and recreate its release, or upload changed files under the same version. If a publication run fails, inspect which artifacts reached PyPI before retrying it.
69
+
70
+ ## Production deployment
71
+
72
+ [`web/fly.toml`](https://github.com/reponomics/treestamp/blob/main/web/fly.toml) promotes the existing `treestamp-preview` Fly app to production at `https://treestamp.reponomics.org`. Future previews use the separate `reponomics-treestamp-preview` app. Leave `FLY_DEPLOY_ENABLED` unset until production deployment, session routing, and custom-domain checks succeed.
73
+
74
+ Keep the production app's GitHub API credentials in Fly secrets: either `GITHUB_TOKEN` or `GITHUB_APP_CLIENT_ID`, `GITHUB_APP_INSTALLATION_ID`, and `GITHUB_APP_PRIVATE_KEY`. To install or rotate the App credentials, `make fly-secrets` accepts `FLY_CONFIG=web/fly.toml` and reads the key from `GITHUB_APP_PRIVATE_KEY_FILE`. These service credentials are separate from the App used by Release Please.
75
+
76
+ Deploy a clean, reviewed commit on `main` manually for the initial setup:
77
+
78
+ ```bash
79
+ make fly-production-deploy FLY_DEPLOY_FLAGS=--remote-only
80
+ make fly-status FLY_CONFIG=web/fly.toml
81
+ ```
82
+
83
+ Production keeps two Machines running; the production target enables Fly's high-availability provisioning. Rolling deployment replaces at most one Machine at a time and checks `/healthz` during rollout. A build failure stops before rollout. A failed health check can halt rollout, but it is not a guarantee against application bugs or interrupted editing sessions. With `FLY_SESSION_ROUTING=true`, a browser-session cookie uses Fly's `FLY_MACHINE_ID` to route an active editing session to its cached snapshot. Sessions are temporary. A Machine failure, restart, or deployment can require a fresh URL load; the existing browser draft stays copyable and is replaced only after a successful load. Snapshots and quotas remain process-local, so changing the Machine count changes aggregate capacity and limits.
84
+
85
+ The custom domain is attached to `treestamp-preview` in Fly. These records are saved in Cloudflare for the `reponomics.org` zone, with proxying disabled. Fly reports the certificate as Ready, with active RSA and ECDSA certificates and successful validations:
86
+
87
+ | Type | Name | Value | Cloudflare proxy |
88
+ | --- | --- | --- | --- |
89
+ | CNAME | `treestamp` | `8w9r381.treestamp-preview.fly.dev` | DNS only; enable after the reviewed production deployment |
90
+ | TXT | `_fly-ownership.treestamp` | `app-8w9r381` | Not applicable |
91
+ | CNAME | `_acme-challenge.treestamp` | `treestamp.reponomics.org.8w9r381.flydns.net` | DNS only |
92
+
93
+ Recheck the records with `fly certs setup treestamp.reponomics.org --app treestamp-preview` if the app changes, and check certificate status with `fly certs check treestamp.reponomics.org --app treestamp-preview`. Keep proxying disabled until a reviewed `main` deployment includes Cloudflare peer validation; then enable it with Full (strict) TLS and complete the checks below. See [Fly's custom-domain instructions](https://docs.fly.io/networking/custom-domain) and [Cloudflare guidance](https://docs.fly.io/networking/understanding-cloudflare).
94
+
95
+ Keep HTML, API, and `/healthz` responses out of Cloudflare's edge cache; do not apply a Cache Everything rule to this hostname. The application accepts `CF-Connecting-IP` only when Fly's peer address belongs to the configured Cloudflare ranges. Worker subrequests and invalid visitor headers fall back conservatively to the peer for rate limiting. Review `CLOUDFLARE_PROXY_CIDRS` against [Cloudflare's published ranges](https://www.cloudflare.com/ips/) when maintaining this configuration, and keep the service behind Fly ingress.
96
+
97
+ Check `/healthz` through the custom domain and confirm that `build_commit` matches the deployed commit. Exercise loading, editing, copying, and draft preservation after failed loads in a browser, then run a public-repository smoke test with a matching local checkout:
98
+
99
+ ```bash
100
+ make fly-smoke SMOKE_URL=https://treestamp.reponomics.org \
101
+ SMOKE_REPO=OWNER/REPOSITORY SMOKE_CHECKOUT=/path/to/checkout
102
+ ```
103
+
104
+ Store an app-scoped Fly deploy token as `FLY_API_TOKEN` in the GitHub `production` environment, then enable `FLY_DEPLOY_ENABLED`. The separate [`deploy.yml`](https://github.com/reponomics/treestamp/blob/main/.github/workflows/deploy.yml) workflow deploys pushes to `main`, including merged PRs. Required PR checks are the merge gate; deployment does not wait for post-merge CI, and a failure in the separate CI workflow does not cancel it. Deployment never runs on PR events. No repository connection or auto-deploy setting in Fly is needed; leave Fly-native auto-deployment disabled to avoid duplicate rollouts. Cloudflare supplies DNS and proxying, while GitHub Actions invokes Fly to build and deploy the application.
105
+
106
+ Deployments are serialized without cancelling an active rollout. A queued run skips an obsolete commit if a newer commit is already on `main`. The build receives the pushed commit identifier, and the final health probe verifies that the public domain serves that commit. A failed final probe reports deployment failure; it does not automatically roll back. No separate release command or blue-green deployment is configured.
107
+
108
+ For rollback, prefer a reviewed revert on `main` through the same checks. An urgent manual rollback should use a previously verified image with current certificate stores; verify its build identifier and smoke test afterward, and reconcile `main` before the next automatic deployment.
109
+
110
+ ## Preview deployment
111
+
112
+ The separate `web/fly.preview.toml` targets `reponomics-treestamp-preview`. Here, preview means a test deployment of the web editor. This app has been created but has not been deployed. Install its GitHub API credentials separately before its first deployment, using `make fly-secrets FLY_CONFIG=web/fly.preview.toml` if using a GitHub App. Preview deployment is manual and defaults to one Machine that may stop when idle:
113
+
114
+ ```bash
115
+ make web-image
116
+ make fly-preview-deploy
117
+ make fly-status FLY_CONFIG=web/fly.preview.toml
118
+ ```
119
+
120
+ Deploy a clean, committed candidate so that identifier describes the deployed files. The Docker build checks certificate stores; `make tls-check` performs the corresponding check in the local Python environment. Credentials belong in the host's secret store, not in Git.
121
+
122
+ After deploying, check `https://reponomics-treestamp-preview.fly.dev/healthz`, then run the public-repository smoke test with a matching local checkout:
123
+
124
+ ```bash
125
+ make fly-smoke SMOKE_URL=https://reponomics-treestamp-preview.fly.dev \
126
+ SMOKE_REPO=OWNER/REPOSITORY SMOKE_CHECKOUT=/path/to/checkout
127
+ ```
128
+
129
+ The repository must be public, and the local checkout must contain the commit used by the hosted session. Verify loading, editing, copying, and draft preservation after failed loads in a browser. For rollback, redeploy a previously verified image with up-to-date certificate stores and recheck the build identifier and smoke test.
130
+
131
+ ## Product page
132
+
133
+ The product information page and selected demos belong in the separate `reponomics-web` repository, using its static assets and website deployment. They are not deployed by this repository. Add the product-page link after that page is published.
134
+
135
+ ## External setup checklist
136
+
137
+ - [ ] Configure `main` protections, required checks, signed commits, and immutable releases.
138
+ - [ ] Install the release App on this repository and configure its variable and secret.
139
+ - [ ] Finish the pending Trusted Publisher and review `pypi` environment protections; complete public review and enable publication before merging the first release PR.
140
+ - [ ] Enable private vulnerability reporting when the repository becomes public.
141
+ - [ ] Deploy the production configuration to `treestamp-preview` and verify two-Machine session routing and draft recovery.
142
+ - [x] Save the three Cloudflare DNS records without proxying and validate the Fly certificate.
143
+ - [ ] After the reviewed production deployment, enable Cloudflare proxying with Full (strict) TLS, verify cache behavior, and complete domain and smoke checks.
144
+ - [ ] Configure the `production` environment and app-scoped deploy token, then enable deployment from `main`.
145
+ - [ ] Configure credentials and deploy the new `reponomics-treestamp-preview` app before using it for previews.
146
+ - [ ] Publish the product page and demos from `reponomics-web` when ready.
@@ -0,0 +1,116 @@
1
+ .DEFAULT_GOAL := help
2
+ VENV ?= venv
3
+ PYTHON ?= python3
4
+ UV ?= uv
5
+ UV_VERSION := $(shell awk '$$1 == "uv" {print $$2}' .tool-versions)
6
+ NODE ?= node
7
+ FLY ?= fly
8
+ ACTIONLINT ?= actionlint
9
+ PY := $(VENV)/bin/python
10
+ PIP := $(VENV)/bin/pip
11
+ CLI := $(VENV)/bin/treestamp
12
+
13
+ .PHONY: help venv install format format-check lint typecheck test cov ci build distribution-check integration-check hook-check pre-commit-check tui clean web-install web-lint web-typecheck web-test web-cov web-ci web-fake web-serve web-image web-js-test web-browser-test fly-secrets fly-deploy fly-status fly-logs fly-smoke docs-check metadata-check
14
+ GITHUB_API_URL ?= http://127.0.0.1:8100
15
+ FAKE_PORT ?= 8100
16
+ FAKE_FLAGS ?=
17
+ FLY_CONFIG ?= web/fly.toml
18
+ FLY_PRODUCTION_CONFIG ?= web/fly.toml
19
+ FLY_PREVIEW_CONFIG ?= web/fly.preview.toml
20
+ FLY_DEPLOY_FLAGS ?=
21
+ FLY_HA ?= true
22
+ BUILD_COMMIT ?= $(shell git rev-parse HEAD)
23
+ SMOKE_REPO ?= reponomics/treestamp
24
+ SMOKE_URL ?= https://treestamp.reponomics.org
25
+ SMOKE_CHECKOUT ?= .
26
+ WEB_VENV := $(abspath $(VENV))
27
+ .PHONY: tls-check fly-production-deploy fly-preview-deploy workflow-check lock-check lock-update web-browser-install uv-check
28
+ export UV_PROJECT_ENVIRONMENT := $(abspath $(VENV))
29
+ help: ## Show available targets
30
+ @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "%-22s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
31
+ uv-check: ## Check that uv matches the pinned development tool version
32
+ @test "$$($(UV) --version | awk '{print $$2}')" = "$(UV_VERSION)" || \
33
+ { echo "Install uv $(UV_VERSION), as pinned in .tool-versions."; exit 1; }
34
+ venv: uv-check ## Create the virtual environment
35
+ @test -d "$(VENV)" || $(UV) venv --python $(PYTHON) $(VENV)
36
+ lock-check: uv-check ## Reject dependency declarations that differ from the lock
37
+ $(UV) lock --check
38
+ lock-update: uv-check ## Resolve intentional dependency upgrades into uv.lock
39
+ $(UV) lock --upgrade
40
+ install: uv-check ## Sync locked core, hosted, build, and test dependencies into venv
41
+ $(UV) sync --locked --all-packages --all-extras --no-install-workspace --no-build
42
+ $(UV) sync --locked --all-packages --all-extras --no-build-isolation
43
+ format: ## Format Python code
44
+ $(VENV)/bin/ruff format .
45
+ format-check: ## Check Python formatting
46
+ $(VENV)/bin/ruff format --check .
47
+ lint: ## Lint Python code
48
+ $(VENV)/bin/ruff check .
49
+ typecheck: ## Check strict Python types
50
+ $(VENV)/bin/mypy
51
+ test: ## Run the regression suite, including headless TUI journeys
52
+ $(PY) -m pytest
53
+ cov: ## Run tests with branch coverage
54
+ $(PY) -m pytest --cov --cov-report=term-missing --cov-fail-under=90
55
+ ci: format-check lint typecheck cov ## Run all local quality gates
56
+ docs-check: ## Check retained local documentation links and packaged README URLs
57
+ $(PY) scripts/check_docs.py
58
+ metadata-check: ## Check built metadata, README rendering, package contents, and hashes
59
+ $(PY) -m twine check --strict dist/*.whl dist/*.tar.gz
60
+ $(PY) scripts/release_check.py
61
+ build: install ## Sync locked build tools and build wheel and sdist without isolation
62
+ $(PY) -m build --no-isolation
63
+ distribution-check: ## Verify clean wheel/sdist headless commands and editor workflows
64
+ $(PY) scripts/distribution_check.py
65
+ integration-check: ## Verify the repository's treestamped README
66
+ $(CLI) check README.md
67
+ workflow-check: ## Validate GitHub Actions workflows (requires actionlint)
68
+ $(ACTIONLINT)
69
+ hook-check: ## Exercise installed repository hooks at HEAD
70
+ $(PY) scripts/hook_check.py
71
+ pre-commit-check: ## Run the repository development hooks
72
+ $(VENV)/bin/pre-commit run --all-files
73
+ tui: ## Open the editor
74
+ $(CLI)
75
+ clean: ## Remove generated build and coverage artifacts
76
+ rm -rf dist/ build/ .coverage htmlcov/
77
+ web-install: install ## Sync the locked workspace, including hosted development dependencies
78
+ web-browser-install: install ## Sync the locked browser test dependencies (install Chromium separately)
79
+ web-lint: ## Lint the web editor project
80
+ $(VENV)/bin/ruff check web && $(VENV)/bin/ruff format --check web
81
+ web-typecheck: ## Check strict types for the web editor project
82
+ cd web && $(WEB_VENV)/bin/mypy
83
+ web-test: ## Run the web editor tests
84
+ cd web && $(WEB_VENV)/bin/python -m pytest
85
+ web-cov: ## Run the web editor tests with branch coverage
86
+ cd web && $(WEB_VENV)/bin/python -m pytest --cov --cov-report=term-missing --cov-fail-under=90
87
+ web-ci: web-lint web-typecheck web-cov web-js-test ## Run all web editor quality gates
88
+ web-js-test: ## Run the page's pure-logic tests with Node's test runner
89
+ $(NODE) --test web/tests/js/*.test.mjs
90
+ web-browser-test: ## Drive the page in a browser (needs make install and a browser)
91
+ cd web && $(WEB_VENV)/bin/python -m pytest -q -o addopts="" -m browser tests/browser $(BROWSER_FLAGS)
92
+ web-fake: ## Serve this checkout as a fake GitHub API on FAKE_PORT (FAKE_FLAGS adds failure modes)
93
+ $(PY) web/scripts/fake_github.py --repo . --port $(FAKE_PORT) $(FAKE_FLAGS)
94
+ web-serve: ## Run the web editor locally against GITHUB_API_URL
95
+ GITHUB_API_URL=$(GITHUB_API_URL) GITHUB_RATE_RESERVE=0 $(VENV)/bin/uvicorn --factory treestamp_web.app:create_app --host 127.0.0.1 --port 8000 --reload
96
+ tls-check: ## Check Python TLS package versions and certificate trust stores
97
+ $(PY) web/scripts/check_tls.py
98
+ web-image: ## Build the web editor container image
99
+ docker build -f web/Dockerfile --build-arg BUILD_COMMIT="$(BUILD_COMMIT)" -t treestamp-web:local .
100
+ fly-secrets: ## Set the GitHub App secrets on Fly (GITHUB_APP_CLIENT_ID, GITHUB_APP_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY_FILE)
101
+ @test -n "$(GITHUB_APP_CLIENT_ID)" -a -n "$(GITHUB_APP_INSTALLATION_ID)" -a -n "$(GITHUB_APP_PRIVATE_KEY_FILE)" || \
102
+ { echo "usage: make fly-secrets GITHUB_APP_CLIENT_ID=... GITHUB_APP_INSTALLATION_ID=... GITHUB_APP_PRIVATE_KEY_FILE=path.pem"; exit 2; }
103
+ $(FLY) secrets set --config $(FLY_CONFIG) GITHUB_APP_CLIENT_ID="$(GITHUB_APP_CLIENT_ID)" \
104
+ GITHUB_APP_INSTALLATION_ID="$(GITHUB_APP_INSTALLATION_ID)" GITHUB_APP_PRIVATE_KEY="$$(cat $(GITHUB_APP_PRIVATE_KEY_FILE))"
105
+ fly-deploy: ## Build and deploy the app selected by FLY_CONFIG (defaults to production)
106
+ $(FLY) deploy --config $(FLY_CONFIG) --dockerfile web/Dockerfile --build-arg BUILD_COMMIT="$(BUILD_COMMIT)" --ha=$(FLY_HA) $(FLY_DEPLOY_FLAGS)
107
+ fly-production-deploy: ## Build and deploy the production editor
108
+ $(MAKE) fly-deploy FLY_CONFIG=$(FLY_PRODUCTION_CONFIG) FLY_HA=true
109
+ fly-preview-deploy: ## Build and deploy the separate test editor
110
+ $(MAKE) fly-deploy FLY_CONFIG=$(FLY_PREVIEW_CONFIG) FLY_HA=false
111
+ fly-status: ## Show the app's machines and health
112
+ $(FLY) status --config $(FLY_CONFIG)
113
+ fly-logs: ## Stream the service's log lines from Fly
114
+ $(FLY) logs --config $(FLY_CONFIG)
115
+ fly-smoke: ## Run the smoke script against the deployed app (SMOKE_REPO, SMOKE_CHECKOUT)
116
+ $(PY) web/scripts/smoke.py $(SMOKE_URL) --repo $(SMOKE_REPO) --checkout $(SMOKE_CHECKOUT)