webgate 0.4.2__tar.gz → 2.3.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 (208) hide show
  1. webgate-2.3.0/.github/workflows/ci.yml +106 -0
  2. webgate-2.3.0/.github/workflows/demo.yml +63 -0
  3. webgate-2.3.0/.github/workflows/release.yml +123 -0
  4. {webgate-0.4.2 → webgate-2.3.0}/.gitignore +3 -0
  5. webgate-2.3.0/CHANGELOG.md +847 -0
  6. webgate-2.3.0/DESIGN.md +103 -0
  7. webgate-2.3.0/PKG-INFO +845 -0
  8. webgate-2.3.0/PRODUCT.md +54 -0
  9. webgate-2.3.0/README.md +796 -0
  10. webgate-2.3.0/RELEASING.md +109 -0
  11. webgate-2.3.0/ROADMAP.md +109 -0
  12. webgate-2.3.0/VERSION +1 -0
  13. webgate-2.3.0/compose.ha.yml +62 -0
  14. webgate-2.3.0/compose.playground.yml +152 -0
  15. webgate-2.3.0/compose.yml +32 -0
  16. webgate-2.3.0/deploy/nginx-ha.conf +36 -0
  17. webgate-2.3.0/docs/LOCAL_TESTING.md +239 -0
  18. webgate-2.3.0/docs/architecture.md +293 -0
  19. webgate-2.3.0/docs/changelog.md +847 -0
  20. {webgate-0.4.2 → webgate-2.3.0}/docs/getting-started/quickstart.md +4 -4
  21. webgate-2.3.0/docs/getting-started/upgrade.md +133 -0
  22. webgate-2.3.0/docs/guide/access-control.md +64 -0
  23. webgate-2.3.0/docs/guide/advanced.md +102 -0
  24. webgate-2.3.0/docs/guide/ha.md +59 -0
  25. webgate-2.3.0/docs/guide/integrations.md +110 -0
  26. webgate-2.3.0/docs/guide/settings.md +65 -0
  27. webgate-2.3.0/docs/index.md +86 -0
  28. webgate-2.3.0/docs/screenshots/playground/01-login.png +0 -0
  29. webgate-2.3.0/docs/screenshots/playground/02-alice-ldap-empty.png +0 -0
  30. webgate-2.3.0/docs/screenshots/playground/03-add-server-jump-via.png +0 -0
  31. webgate-2.3.0/docs/screenshots/playground/04-dashboard-3-servers.png +0 -0
  32. webgate-2.3.0/docs/screenshots/playground/05-terminal-via-jump-host.png +0 -0
  33. webgate-2.3.0/docs/screenshots/playground/06-snippet-whoami.png +0 -0
  34. webgate-2.3.0/docs/screenshots/playground/07-shared-joiner.png +0 -0
  35. webgate-2.3.0/docs/screenshots/playground/07-shared-owner.png +0 -0
  36. webgate-2.3.0/docs/screenshots/playground/08-webhook-fired.png +0 -0
  37. webgate-2.3.0/docs/screenshots/playground/09-recordings-list.png +0 -0
  38. webgate-2.3.0/docs/screenshots/playground/10-recording-replay.png +0 -0
  39. webgate-2.3.0/docs/screenshots/playground/11-bob-limited.png +0 -0
  40. webgate-2.3.0/docs/screenshots/site-manager.png +0 -0
  41. webgate-2.3.0/docs/screenshots/v2/agent.png +0 -0
  42. webgate-2.3.0/docs/screenshots/v2/branding.png +0 -0
  43. webgate-2.3.0/docs/screenshots/v2/light.png +0 -0
  44. webgate-2.3.0/docs/screenshots/v2/login.png +0 -0
  45. webgate-2.3.0/docs/screenshots/v2/palette.png +0 -0
  46. webgate-2.3.0/docs/screenshots/v2/settings.png +0 -0
  47. webgate-2.3.0/docs/screenshots/v2/sftp.png +0 -0
  48. webgate-2.3.0/docs/screenshots/v2/site-manager.png +0 -0
  49. webgate-2.3.0/docs/screenshots/v2/terminal.png +0 -0
  50. webgate-2.3.0/docs/screenshots/v2/users.png +0 -0
  51. {webgate-0.4.2 → webgate-2.3.0}/fly.toml +5 -0
  52. {webgate-0.4.2 → webgate-2.3.0}/mkdocs.yml +10 -2
  53. {webgate-0.4.2 → webgate-2.3.0}/pyproject.toml +1 -1
  54. webgate-2.3.0/scripts/release_notes.py +42 -0
  55. webgate-2.3.0/src/webgate/__init__.py +29 -0
  56. webgate-2.3.0/src/webgate/__main__.py +92 -0
  57. webgate-2.3.0/src/webgate/agent/__init__.py +16 -0
  58. webgate-2.3.0/src/webgate/agent/cache.py +76 -0
  59. webgate-2.3.0/src/webgate/agent/conversation.py +189 -0
  60. webgate-2.3.0/src/webgate/agent/memory.py +174 -0
  61. webgate-2.3.0/src/webgate/agent/models.py +56 -0
  62. webgate-2.3.0/src/webgate/agent/provider.py +144 -0
  63. webgate-2.3.0/src/webgate/agent/routes.py +381 -0
  64. webgate-2.3.0/src/webgate/agent/service.py +304 -0
  65. webgate-2.3.0/src/webgate/agent/sftp_tools.py +292 -0
  66. webgate-2.3.0/src/webgate/agent/store.py +157 -0
  67. webgate-2.3.0/src/webgate/agent/tools.py +223 -0
  68. webgate-2.3.0/src/webgate/app.py +150 -0
  69. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/audit/models.py +2 -1
  70. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/auth/ldap.py +18 -15
  71. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/auth/routes.py +71 -32
  72. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/auth/service.py +23 -16
  73. webgate-2.3.0/src/webgate/backup/__init__.py +9 -0
  74. webgate-2.3.0/src/webgate/backup/models.py +37 -0
  75. webgate-2.3.0/src/webgate/backup/routes.py +102 -0
  76. webgate-2.3.0/src/webgate/backup/service.py +364 -0
  77. webgate-2.3.0/src/webgate/branding/__init__.py +10 -0
  78. webgate-2.3.0/src/webgate/branding/routes.py +101 -0
  79. webgate-2.3.0/src/webgate/branding/store.py +243 -0
  80. webgate-2.3.0/src/webgate/config.py +81 -0
  81. webgate-2.3.0/src/webgate/db/engine.py +242 -0
  82. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/demo.py +4 -4
  83. webgate-2.3.0/src/webgate/files/limits.py +71 -0
  84. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/files/pool.py +22 -14
  85. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/files/routes.py +156 -23
  86. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/files/sftp_service.py +123 -27
  87. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/recordings/models.py +7 -4
  88. webgate-2.3.0/src/webgate/recordings/recorder.py +105 -0
  89. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/recordings/routes.py +57 -30
  90. webgate-2.3.0/src/webgate/runtime_config/registry.py +321 -0
  91. webgate-2.3.0/src/webgate/runtime_config/routes.py +153 -0
  92. webgate-2.3.0/src/webgate/runtime_config/store.py +219 -0
  93. webgate-2.3.0/src/webgate/servers/hostkeys.py +127 -0
  94. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/servers/models.py +27 -1
  95. webgate-2.3.0/src/webgate/servers/monitor.py +267 -0
  96. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/servers/routes.py +88 -17
  97. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/servers/service.py +35 -14
  98. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/snippets/models.py +12 -1
  99. webgate-2.3.0/src/webgate/snippets/routes.py +127 -0
  100. webgate-2.3.0/src/webgate/static/index.html +3782 -0
  101. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/terminal/routes.py +60 -25
  102. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/terminal/shared.py +40 -0
  103. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/terminal/ssh_session.py +7 -1
  104. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/terminal/ws_handler.py +25 -12
  105. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/webhooks/dispatcher.py +9 -2
  106. webgate-2.3.0/tests/__init__.py +0 -0
  107. {webgate-0.4.2 → webgate-2.3.0}/tests/conftest.py +40 -2
  108. webgate-2.3.0/tests/test_agent_conversation.py +192 -0
  109. webgate-2.3.0/tests/test_agent_memory.py +124 -0
  110. webgate-2.3.0/tests/test_agent_settings.py +196 -0
  111. webgate-2.3.0/tests/test_agent_sftp_tools.py +223 -0
  112. webgate-2.3.0/tests/test_agent_tools.py +142 -0
  113. {webgate-0.4.2 → webgate-2.3.0}/tests/test_auth.py +4 -1
  114. webgate-2.3.0/tests/test_backup.py +241 -0
  115. webgate-2.3.0/tests/test_branding.py +200 -0
  116. webgate-2.3.0/tests/test_files_listing.py +144 -0
  117. webgate-2.3.0/tests/test_host_keys.py +223 -0
  118. webgate-2.3.0/tests/test_idle_sessions.py +110 -0
  119. webgate-2.3.0/tests/test_limits.py +141 -0
  120. webgate-2.3.0/tests/test_migrations.py +263 -0
  121. webgate-2.3.0/tests/test_monitor.py +92 -0
  122. webgate-2.3.0/tests/test_recordings.py +221 -0
  123. webgate-2.3.0/tests/test_release_notes.py +83 -0
  124. webgate-2.3.0/tests/test_secret_key_gate.py +43 -0
  125. {webgate-0.4.2 → webgate-2.3.0}/tests/test_servers.py +18 -8
  126. webgate-2.3.0/tests/test_settings.py +282 -0
  127. webgate-2.3.0/tests/test_snippets.py +133 -0
  128. {webgate-0.4.2 → webgate-2.3.0}/tests/test_terminal.py +18 -14
  129. webgate-2.3.0/tests/test_webhook_dispatch.py +111 -0
  130. {webgate-0.4.2 → webgate-2.3.0}/uv.lock +1 -1
  131. webgate-0.4.2/CHANGELOG.md +0 -250
  132. webgate-0.4.2/PKG-INFO +0 -649
  133. webgate-0.4.2/README.md +0 -600
  134. webgate-0.4.2/ROADMAP.md +0 -79
  135. webgate-0.4.2/VERSION +0 -1
  136. webgate-0.4.2/after-reload.yaml +0 -64
  137. webgate-0.4.2/compose.yml +0 -29
  138. webgate-0.4.2/docs/changelog.md +0 -27
  139. webgate-0.4.2/docs/index.md +0 -63
  140. webgate-0.4.2/docs/screenshots/site-manager.png +0 -0
  141. webgate-0.4.2/src/webgate/__init__.py +0 -1
  142. webgate-0.4.2/src/webgate/__main__.py +0 -18
  143. webgate-0.4.2/src/webgate/app.py +0 -104
  144. webgate-0.4.2/src/webgate/config.py +0 -49
  145. webgate-0.4.2/src/webgate/db/engine.py +0 -67
  146. webgate-0.4.2/src/webgate/recordings/recorder.py +0 -63
  147. webgate-0.4.2/src/webgate/servers/monitor.py +0 -117
  148. webgate-0.4.2/src/webgate/snippets/routes.py +0 -45
  149. webgate-0.4.2/src/webgate/static/index.html +0 -1647
  150. {webgate-0.4.2 → webgate-2.3.0}/.dockerignore +0 -0
  151. {webgate-0.4.2 → webgate-2.3.0}/.github/workflows/docs.yml +0 -0
  152. {webgate-0.4.2 → webgate-2.3.0}/Dockerfile +0 -0
  153. {webgate-0.4.2 → webgate-2.3.0}/Dockerfile.demo +0 -0
  154. {webgate-0.4.2 → webgate-2.3.0}/Dockerfile.ssh-demo +0 -0
  155. {webgate-0.4.2 → webgate-2.3.0}/LICENSE +0 -0
  156. {webgate-0.4.2 → webgate-2.3.0}/compose.dev.yml +0 -0
  157. {webgate-0.4.2 → webgate-2.3.0}/docs/api/auth.md +0 -0
  158. {webgate-0.4.2 → webgate-2.3.0}/docs/api/files.md +0 -0
  159. {webgate-0.4.2 → webgate-2.3.0}/docs/api/servers.md +0 -0
  160. {webgate-0.4.2 → webgate-2.3.0}/docs/api/terminal.md +0 -0
  161. {webgate-0.4.2 → webgate-2.3.0}/docs/getting-started/installation.md +0 -0
  162. {webgate-0.4.2 → webgate-2.3.0}/docs/guide/files.md +0 -0
  163. {webgate-0.4.2 → webgate-2.3.0}/docs/guide/servers.md +0 -0
  164. {webgate-0.4.2 → webgate-2.3.0}/docs/guide/split.md +0 -0
  165. {webgate-0.4.2 → webgate-2.3.0}/docs/guide/terminal.md +0 -0
  166. {webgate-0.4.2 → webgate-2.3.0}/docs/guide/users.md +0 -0
  167. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/access-control.png +0 -0
  168. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/audit.png +0 -0
  169. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/edit-access-control.png +0 -0
  170. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/editor.png +0 -0
  171. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/light-theme.png +0 -0
  172. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/login.png +0 -0
  173. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/new-server-form.png +0 -0
  174. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/sftp-restricted.png +0 -0
  175. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/sftp.png +0 -0
  176. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/split-view.png +0 -0
  177. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/ssh-disabled.png +0 -0
  178. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/terminal.png +0 -0
  179. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/users.png +0 -0
  180. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/01-login-demo-banner.png +0 -0
  181. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/02-dashboard-jump-host.png +0 -0
  182. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/03-terminal-snippets-jump.png +0 -0
  183. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/04-snippet-executed.png +0 -0
  184. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/05-sftp-via-jump.png +0 -0
  185. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/06-webhooks-modal.png +0 -0
  186. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/07-webhook-test-fired.png +0 -0
  187. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.3/08-add-server-jump-via.png +0 -0
  188. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.4/01-shared-terminal-owner.png +0 -0
  189. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.4/02-shared-terminal-joiner.png +0 -0
  190. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.4/03-recording-replay.png +0 -0
  191. {webgate-0.4.2 → webgate-2.3.0}/docs/screenshots/v0.4/04-recordings-modal.png +0 -0
  192. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/audit/__init__.py +0 -0
  193. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/audit/service.py +0 -0
  194. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/auth/__init__.py +0 -0
  195. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/auth/models.py +0 -0
  196. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/db/__init__.py +0 -0
  197. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/files/__init__.py +0 -0
  198. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/files/models.py +0 -0
  199. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/recordings/__init__.py +0 -0
  200. {webgate-0.4.2/src/webgate/servers → webgate-2.3.0/src/webgate/runtime_config}/__init__.py +0 -0
  201. {webgate-0.4.2/src/webgate/snippets → webgate-2.3.0/src/webgate/servers}/__init__.py +0 -0
  202. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/servers/crypto.py +0 -0
  203. {webgate-0.4.2/src/webgate/terminal → webgate-2.3.0/src/webgate/snippets}/__init__.py +0 -0
  204. {webgate-0.4.2/src/webgate/webhooks → webgate-2.3.0/src/webgate/terminal}/__init__.py +0 -0
  205. {webgate-0.4.2/tests → webgate-2.3.0/src/webgate/webhooks}/__init__.py +0 -0
  206. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/webhooks/models.py +0 -0
  207. {webgate-0.4.2 → webgate-2.3.0}/src/webgate/webhooks/routes.py +0 -0
  208. {webgate-0.4.2 → webgate-2.3.0}/tests/test_files.py +0 -0
@@ -0,0 +1,106 @@
1
+ name: CI
2
+
3
+ # Everything that must be true before a release can be cut. The release workflow
4
+ # runs this same suite again on the tag rather than trusting that main was green.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ workflow_call: # release.yml reuses this instead of duplicating it
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: ci-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ fail-fast: false
24
+ matrix:
25
+ # The floor from requires-python, and what development actually runs on.
26
+ python-version: ["3.11", "3.12", "3.13"]
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v5
32
+ with:
33
+ enable-cache: true
34
+
35
+ - name: Install
36
+ run: uv sync --all-extras --dev --python ${{ matrix.python-version }}
37
+
38
+ - name: Lint
39
+ run: uv run ruff check src/ tests/ scripts/
40
+
41
+ - name: Format check
42
+ run: uv run ruff format --check src/ tests/ scripts/
43
+
44
+ - name: Tests
45
+ run: uv run pytest tests/ -q
46
+
47
+ docker:
48
+ # A Dockerfile only breaks when someone builds it, and nobody was.
49
+ runs-on: ubuntu-latest
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+
53
+ - uses: docker/setup-buildx-action@v3
54
+
55
+ - name: Build the runtime image
56
+ uses: docker/build-push-action@v6
57
+ with:
58
+ context: .
59
+ file: ./Dockerfile
60
+ push: false
61
+ load: true
62
+ tags: webgate:ci
63
+ cache-from: type=gha
64
+ cache-to: type=gha,mode=max
65
+
66
+ - name: It starts, serves, and refuses the default secret key
67
+ run: |
68
+ set -euo pipefail
69
+
70
+ echo "--- refuses to start with the shipped default key ---"
71
+ # Refusing means a non-zero exit, so the run cannot sit inside a pipeline
72
+ # under `pipefail` -- that would report the refusal as this step failing.
73
+ rc=0
74
+ docker run --rm webgate:ci > /tmp/refusal 2>&1 || rc=$?
75
+ cat /tmp/refusal
76
+ if [ "$rc" -eq 0 ]; then
77
+ echo "FAILED: started under the default key"; exit 1
78
+ fi
79
+ grep -q "WEBGATE_SECRET_KEY is still the shipped default" /tmp/refusal \
80
+ || { echo "FAILED: refused, but not for the reason we expect"; exit 1; }
81
+ echo "ok: refused, with the message that says what to do"
82
+
83
+ echo "--- starts and answers with a real key ---"
84
+ docker run -d --name wg -p 8443:8443 \
85
+ -e WEBGATE_SECRET_KEY="$(openssl rand -hex 32)" webgate:ci
86
+ for i in $(seq 1 30); do
87
+ if curl -fsS http://localhost:8443/api/health >/dev/null 2>&1; then
88
+ echo "ok: healthy after ${i}s"
89
+ curl -fsS http://localhost:8443/openapi.json | grep -o '"version":"[^"]*"' | head -1
90
+ docker rm -f wg >/dev/null
91
+ exit 0
92
+ fi
93
+ sleep 1
94
+ done
95
+ echo "FAILED: never became healthy"; docker logs wg; docker rm -f wg; exit 1
96
+
97
+ docs:
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - uses: actions/checkout@v4
101
+ - uses: actions/setup-python@v5
102
+ with:
103
+ python-version: "3.13"
104
+ - run: pip install mkdocs-material mkdocstrings mkdocstrings-python
105
+ - name: Build the site (strict — a broken link fails here, not on deploy)
106
+ run: mkdocs build --strict
@@ -0,0 +1,63 @@
1
+ name: Deploy demo
2
+
3
+ # The public demo at webgate-demo.fly.dev. Deployed by hand until now, which is why
4
+ # it spent months serving a version several releases behind the repository.
5
+
6
+ on:
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: demo
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ deploy:
20
+ runs-on: ubuntu-latest
21
+ environment:
22
+ name: demo
23
+ url: https://webgate-demo.fly.dev/
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - uses: superfly/flyctl-actions/setup-flyctl@master
28
+
29
+ - name: The demo must already hold a real secret key
30
+ # Without it the app refuses to start, and the deploy would replace a working
31
+ # demo with a crash loop. Checking first turns that into a clear failure here.
32
+ env:
33
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
34
+ run: |
35
+ set -euo pipefail
36
+ if flyctl secrets list --app webgate-demo | grep -q WEBGATE_SECRET_KEY; then
37
+ echo "ok: WEBGATE_SECRET_KEY is set"
38
+ else
39
+ echo "WEBGATE_SECRET_KEY is not set on the demo app. Run once:"
40
+ echo " flyctl secrets set WEBGATE_SECRET_KEY=\$(openssl rand -hex 32) --app webgate-demo"
41
+ exit 1
42
+ fi
43
+
44
+ - name: Deploy
45
+ env:
46
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
47
+ run: flyctl deploy --remote-only
48
+
49
+ - name: It is up, and it is the version we just released
50
+ run: |
51
+ set -euo pipefail
52
+ for i in $(seq 1 30); do
53
+ got=$(curl -fsS https://webgate-demo.fly.dev/openapi.json 2>/dev/null \
54
+ | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4) || got=""
55
+ if [ -n "$got" ]; then
56
+ echo "demo reports $got"
57
+ want="$(cat VERSION)"
58
+ [ "$got" = "$want" ] || { echo "expected $want"; exit 1; }
59
+ exit 0
60
+ fi
61
+ sleep 5
62
+ done
63
+ echo "the demo never answered"; exit 1
@@ -0,0 +1,123 @@
1
+ name: Release
2
+
3
+ # Cutting a release is `git tag vX.Y.Z && git push origin vX.Y.Z`. Everything after
4
+ # that happens here, because doing it by hand is how PyPI and Docker Hub ended up
5
+ # three minor versions behind the repository.
6
+
7
+ on:
8
+ push:
9
+ tags: ["v*"]
10
+ workflow_dispatch:
11
+ inputs:
12
+ tag:
13
+ description: "Existing tag to (re)publish, e.g. v2.2.0"
14
+ required: true
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ # Nothing is published before the version agrees with itself and the tests pass.
21
+ verify:
22
+ runs-on: ubuntu-latest
23
+ outputs:
24
+ version: ${{ steps.v.outputs.version }}
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ with:
28
+ ref: ${{ inputs.tag || github.ref }}
29
+
30
+ - id: v
31
+ name: The tag, VERSION and pyproject must agree
32
+ run: |
33
+ set -euo pipefail
34
+ tag="${{ inputs.tag || github.ref_name }}"
35
+ version="${tag#v}"
36
+ file="$(cat VERSION)"
37
+ proj="$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)"
38
+ echo "tag=$version VERSION=$file pyproject=$proj"
39
+ [ "$version" = "$file" ] || { echo "tag does not match VERSION"; exit 1; }
40
+ [ "$version" = "$proj" ] || { echo "tag does not match pyproject.toml"; exit 1; }
41
+ echo "version=$version" >> "$GITHUB_OUTPUT"
42
+
43
+ - uses: astral-sh/setup-uv@v5
44
+ - name: The changelog has an entry for it
45
+ run: uv run python scripts/release_notes.py "${{ steps.v.outputs.version }}" > /dev/null
46
+
47
+ test:
48
+ needs: verify
49
+ uses: ./.github/workflows/ci.yml
50
+
51
+ pypi:
52
+ needs: [verify, test]
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/project/webgate/${{ needs.verify.outputs.version }}
57
+ permissions:
58
+ id-token: write # trusted publishing: no API token is stored anywhere
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+ with:
62
+ ref: ${{ inputs.tag || github.ref }}
63
+ - uses: astral-sh/setup-uv@v5
64
+ - run: uv build
65
+ - uses: pypa/gh-action-pypi-publish@release/v1
66
+
67
+ docker:
68
+ needs: [verify, test]
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ with:
73
+ ref: ${{ inputs.tag || github.ref }}
74
+
75
+ - uses: docker/setup-qemu-action@v3
76
+ - uses: docker/setup-buildx-action@v3
77
+
78
+ - uses: docker/login-action@v3
79
+ with:
80
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
81
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
82
+
83
+ - name: Build and push
84
+ uses: docker/build-push-action@v6
85
+ with:
86
+ context: .
87
+ file: ./Dockerfile
88
+ platforms: linux/amd64,linux/arm64
89
+ push: true
90
+ tags: |
91
+ kalexnolasco/webgate:${{ needs.verify.outputs.version }}
92
+ kalexnolasco/webgate:latest
93
+ cache-from: type=gha
94
+ cache-to: type=gha,mode=max
95
+
96
+ - name: Keep the Docker Hub description in step with the README
97
+ uses: peter-evans/dockerhub-description@v4
98
+ continue-on-error: true
99
+ with:
100
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
101
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
102
+ repository: kalexnolasco/webgate
103
+ readme-filepath: ./README.md
104
+
105
+ github_release:
106
+ needs: [verify, pypi, docker]
107
+ runs-on: ubuntu-latest
108
+ permissions:
109
+ contents: write
110
+ steps:
111
+ - uses: actions/checkout@v4
112
+ with:
113
+ ref: ${{ inputs.tag || github.ref }}
114
+ - uses: astral-sh/setup-uv@v5
115
+
116
+ - name: Notes come from CHANGELOG.md, not a second description of the same release
117
+ run: uv run python scripts/release_notes.py "${{ needs.verify.outputs.version }}" > notes.md
118
+
119
+ - uses: softprops/action-gh-release@v2
120
+ with:
121
+ tag_name: ${{ inputs.tag || github.ref_name }}
122
+ name: v${{ needs.verify.outputs.version }}
123
+ body_path: notes.md
@@ -26,6 +26,8 @@ htmlcov/
26
26
 
27
27
  # Database
28
28
  *.db
29
+ *.db.bak*
30
+ *.db-journal
29
31
 
30
32
  # OS
31
33
  .DS_Store
@@ -35,5 +37,6 @@ Thumbs.db
35
37
  CLAUDE.md
36
38
  CLAUDE.local.md
37
39
  .claude/
40
+ .cursor/
38
41
  .playwright-cli/
39
42
  site/