gitops 1.11.0__tar.gz → 1.12.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.
- gitops-1.12.0/.release-please-manifest.json +5 -0
- {gitops-1.11.0 → gitops-1.12.0}/CHANGELOG.md +15 -0
- {gitops-1.11.0 → gitops-1.12.0}/PKG-INFO +1 -1
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/CHANGELOG.md +8 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/Chart.yaml +1 -1
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/deployment.yml +2 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/values.yaml +2 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/__init__.py +1 -1
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/app.py +15 -8
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/settings.py +8 -0
- gitops-1.12.0/gitops_server/version.py +1 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/deploy.py +14 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/worker.py +17 -1
- {gitops-1.11.0 → gitops-1.12.0}/pyproject.toml +1 -1
- gitops-1.12.0/tests/test_shutdown.py +78 -0
- {gitops-1.11.0 → gitops-1.12.0}/uv.lock +10 -10
- gitops-1.11.0/.release-please-manifest.json +0 -5
- gitops-1.11.0/gitops_server/version.py +0 -1
- {gitops-1.11.0 → gitops-1.12.0}/.dockerignore +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.github/CODEOWNERS +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.github/workflows/release-please.yaml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.github/workflows/test.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.gitignore +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.mise.toml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/.pre-commit-config.yaml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/CLAUDE.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/Dockerfile +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/LICENSE +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/README.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/.helmignore +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/README.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/_helpers.tpl +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/certificate.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/environment.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/ingress.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/issuer.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/pdb.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/secrets.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/service.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/charts/gitops/templates/serviceaccount.yaml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/docker-compose.yml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/docs/plans/2026-03-13-kms-encryption-design.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/common/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/common/app.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/common/utils.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/core.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/main.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/py.typed +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/settings.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/shorthands.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/apps.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/async_runner.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/cli.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/exceptions.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/images.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/kube.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/tags.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops/utils/yaml.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/CHANGELOG.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/README.md +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/main.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/types.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/utils/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/utils/git.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/utils/github.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/utils/github_auth.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/utils/slack.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/disruption.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/hooks.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/gitops_server/workers/deployer/semaphore_manager.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/release-please-config.json +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/release.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/renovate.json +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/ruff.toml +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/secrets.example.env +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/setup.cfg +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/__init__.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/conftest.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/sample_data.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_app.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_clone_credentials.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_core.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_deploy.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_deploy_concurrent.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_disruption_guard.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_git.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_github_auth.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_gitops_server.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_load_app_definitions.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_utils.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/test_worker_concurrency.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/utils.py +0 -0
- {gitops-1.11.0 → gitops-1.12.0}/tests/webhook_sample_data.py +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.12.0](https://github.com/uptick/gitops/compare/gitops-v1.11.0...gitops-v1.12.0) (2026-11-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **server:** finish running helm upgrades on shutdown ([92a7952](https://github.com/uptick/gitops/commit/92a7952f91b90543ed573d8c96d8853ff8f7caf5))
|
|
9
|
+
* **server:** finish running helm upgrades on shutdown ([ef16fd2](https://github.com/uptick/gitops/commit/ef16fd21ae53cba6987aecc4455ffdf6b254f4e1))
|
|
10
|
+
* **server:** report work dropped at shutdown, at ERROR by default ([eb2717b](https://github.com/uptick/gitops/commit/eb2717b3ead89abdcffe37e0b14cfd9688ed9994))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **deps:** upgrade botocore to 1.43.117 ([7d1e151](https://github.com/uptick/gitops/commit/7d1e1517770c5f7ffa66a883a6e860852405c011))
|
|
16
|
+
* **deps:** upgrade botocore to 1.43.117 ([77e7bcf](https://github.com/uptick/gitops/commit/77e7bcfbc1d7f46124d486f67cb35a9d3ac18467))
|
|
17
|
+
|
|
3
18
|
## [1.11.0](https://github.com/uptick/gitops/compare/gitops-v1.10.2...gitops-v1.11.0) (2026-10-27)
|
|
4
19
|
|
|
5
20
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.0](https://github.com/uptick/gitops/compare/helm-v1.5.0...helm-v1.6.0) (2026-11-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **server:** finish running helm upgrades on shutdown ([92a7952](https://github.com/uptick/gitops/commit/92a7952f91b90543ed573d8c96d8853ff8f7caf5))
|
|
9
|
+
* **server:** finish running helm upgrades on shutdown ([ef16fd2](https://github.com/uptick/gitops/commit/ef16fd21ae53cba6987aecc4455ffdf6b254f4e1))
|
|
10
|
+
|
|
3
11
|
## [1.5.0](https://github.com/uptick/gitops/compare/helm-v1.4.1...helm-v1.5.0) (2026-10-27)
|
|
4
12
|
|
|
5
13
|
|
|
@@ -24,6 +24,8 @@ spec:
|
|
|
24
24
|
spec:
|
|
25
25
|
serviceAccountName: {{ include "uptick.serviceAccountName" . }}
|
|
26
26
|
priorityClassName: system-node-critical
|
|
27
|
+
# Must exceed GITOPS_SHUTDOWN_TIMEOUT_SECONDS, or running helm upgrades are killed.
|
|
28
|
+
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
|
|
27
29
|
containers:
|
|
28
30
|
- name: gitops
|
|
29
31
|
image: {{ .Values.image }}
|
|
@@ -26,17 +26,24 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
|
26
26
|
|
|
27
27
|
loop = asyncio.get_running_loop()
|
|
28
28
|
deploy_queue_worker = DeployQueueWorker.get_worker()
|
|
29
|
-
|
|
29
|
+
worker_task = asyncio.ensure_future(deploy_queue_worker.run(), loop=loop)
|
|
30
|
+
guard_task = None
|
|
30
31
|
if guard := build_disruption_guard(deploy_queue_worker.is_busy):
|
|
31
32
|
deploy_queue_worker.on_activity = guard.poke
|
|
32
|
-
|
|
33
|
+
guard_task = asyncio.ensure_future(guard.run(), loop=loop)
|
|
33
34
|
yield
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
await _cancel(worker_task)
|
|
36
|
+
await deploy_queue_worker.drain(float(settings.GITOPS_SHUTDOWN_TIMEOUT_SECONDS))
|
|
37
|
+
if guard_task:
|
|
38
|
+
await _cancel(guard_task)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def _cancel(task: asyncio.Future[None]) -> None:
|
|
42
|
+
task.cancel()
|
|
43
|
+
try:
|
|
44
|
+
await task
|
|
45
|
+
except asyncio.CancelledError:
|
|
46
|
+
pass
|
|
40
47
|
|
|
41
48
|
|
|
42
49
|
app = FastAPI(lifespan=lifespan)
|
|
@@ -30,6 +30,14 @@ POD_NAMESPACE = os.environ.get("POD_NAMESPACE", "")
|
|
|
30
30
|
# Report to Sentry once karpenter.sh/do-not-disrupt has been held this long without a break.
|
|
31
31
|
GITOPS_DO_NOT_DISRUPT_WARN_SECONDS = os.environ.get("GITOPS_DO_NOT_DISRUPT_WARN_SECONDS", "10800")
|
|
32
32
|
|
|
33
|
+
# How long shutdown waits for running helm upgrades. Covers helm's 600s --timeout;
|
|
34
|
+
# keep it below the chart's terminationGracePeriodSeconds.
|
|
35
|
+
GITOPS_SHUTDOWN_TIMEOUT_SECONDS = os.environ.get("GITOPS_SHUTDOWN_TIMEOUT_SECONDS", "660")
|
|
36
|
+
|
|
37
|
+
# Log level for work shutdown drops: skipped apps, queued pushes, upgrades still running.
|
|
38
|
+
# ERROR and above reach Sentry.
|
|
39
|
+
GITOPS_SHUTDOWN_DROPPED_LOG_LEVEL = os.environ.get("GITOPS_SHUTDOWN_DROPPED_LOG_LEVEL", "ERROR")
|
|
40
|
+
|
|
33
41
|
# Pass --disable-openapi-validation to helm. Off by default: helm keeps checking
|
|
34
42
|
# manifests against the cluster schema before applying them, which costs each
|
|
35
43
|
# helm process a download and parse of that schema. The API server still
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.12.0"
|
|
@@ -33,6 +33,11 @@ MAX_HELM_HISTORY = 3
|
|
|
33
33
|
# Kube api may rate limit otherwise
|
|
34
34
|
helm_parallel_semaphore = asyncio.Semaphore(int(settings.GITOPS_MAX_PARALLEL_DEPLOYS))
|
|
35
35
|
|
|
36
|
+
# Set on SIGTERM. Apps that haven't started their helm upgrade are skipped, so the
|
|
37
|
+
# drain waits for at most one upgrade rather than a whole push.
|
|
38
|
+
shutting_down = asyncio.Event()
|
|
39
|
+
DROPPED_LOG_LEVEL = logging.getLevelNamesMapping()[settings.GITOPS_SHUTDOWN_DROPPED_LOG_LEVEL.upper()]
|
|
40
|
+
|
|
36
41
|
|
|
37
42
|
@tracer.start_as_current_span("post_result_summary")
|
|
38
43
|
async def post_result_summary(source: str, results: list[UpdateAppResult]) -> None:
|
|
@@ -138,6 +143,7 @@ class Deployer:
|
|
|
138
143
|
# Track which ones we've deployed and which ones failed
|
|
139
144
|
self.successful_apps: set[str] = set()
|
|
140
145
|
self.failed_apps: set[str] = set()
|
|
146
|
+
self.skipped_on_shutdown: list[str] = []
|
|
141
147
|
|
|
142
148
|
self.post_ts: str | None = None
|
|
143
149
|
self.skip_deploy = "--skip-deploy" in commit_message
|
|
@@ -195,6 +201,11 @@ class Deployer:
|
|
|
195
201
|
*[self.uninstall_app(self.previous_app_definitions.apps[app_name]) for app_name in self.removed_apps]
|
|
196
202
|
)
|
|
197
203
|
without_nones = [r for r in update_results + uninstall_results if r is not None]
|
|
204
|
+
if self.skipped_on_shutdown:
|
|
205
|
+
# Not posting the result summary: skipped apps would be missing from the counts.
|
|
206
|
+
# Args, not an f-string, so Sentry groups these into one issue.
|
|
207
|
+
logger.log(DROPPED_LOG_LEVEL, "Shutdown skipped apps: %s", sorted(self.skipped_on_shutdown))
|
|
208
|
+
return
|
|
198
209
|
await post_result_summary(self.current_app_definitions.name, without_nones)
|
|
199
210
|
|
|
200
211
|
async def uninstall_app(self, app: App) -> UpdateAppResult:
|
|
@@ -232,6 +243,9 @@ class Deployer:
|
|
|
232
243
|
from_timestamp = time.time()
|
|
233
244
|
async with self.semaphore_manager.app_semaphore(app.name):
|
|
234
245
|
async with helm_parallel_semaphore:
|
|
246
|
+
if shutting_down.is_set():
|
|
247
|
+
self.skipped_on_shutdown.append(app.name)
|
|
248
|
+
return None
|
|
235
249
|
try:
|
|
236
250
|
return await self._update_app_deployment(app)
|
|
237
251
|
except Exception as e:
|
|
@@ -6,7 +6,7 @@ from typing import Any
|
|
|
6
6
|
from opentelemetry import trace
|
|
7
7
|
|
|
8
8
|
from ... import settings
|
|
9
|
-
from .deploy import Deployer
|
|
9
|
+
from .deploy import DROPPED_LOG_LEVEL, Deployer, shutting_down
|
|
10
10
|
from .semaphore_manager import AppSemaphoreManager
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger("gitops_worker")
|
|
@@ -91,6 +91,22 @@ class DeployQueueWorker:
|
|
|
91
91
|
except Exception as e:
|
|
92
92
|
logger.error(str(e), exc_info=True)
|
|
93
93
|
|
|
94
|
+
async def drain(self, timeout: float) -> None:
|
|
95
|
+
"""Wait up to `timeout` for running deployments, skipping apps not yet started.
|
|
96
|
+
|
|
97
|
+
Cancel `run()` first, or it keeps dequeuing.
|
|
98
|
+
"""
|
|
99
|
+
shutting_down.set()
|
|
100
|
+
if queued := self.queue.qsize():
|
|
101
|
+
logger.log(DROPPED_LOG_LEVEL, "Shutting down with %d pushes still queued.", queued)
|
|
102
|
+
running = [task for task in self.active_deployments if not task.done()]
|
|
103
|
+
if not running:
|
|
104
|
+
return
|
|
105
|
+
logger.info(f"Waiting up to {timeout:.0f}s for {len(running)} deployments to finish.")
|
|
106
|
+
_, pending = await asyncio.wait(running, timeout=timeout)
|
|
107
|
+
if pending:
|
|
108
|
+
logger.log(DROPPED_LOG_LEVEL, "%d deployments still running at shutdown.", len(pending))
|
|
109
|
+
|
|
94
110
|
async def process_deployment(self, work: Any) -> None:
|
|
95
111
|
"""Process a single deployment in a separate task.
|
|
96
112
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from gitops_server.workers.deployer import Deployer
|
|
7
|
+
from gitops_server.workers.deployer.deploy import shutting_down
|
|
8
|
+
from gitops_server.workers.deployer.semaphore_manager import AppSemaphoreManager
|
|
9
|
+
from gitops_server.workers.deployer.worker import DeployQueueWorker
|
|
10
|
+
|
|
11
|
+
from .sample_data import SAMPLE_GITHUB_PAYLOAD
|
|
12
|
+
from .utils import mock_load_app_definitions
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture(autouse=True)
|
|
16
|
+
def reset_shutdown():
|
|
17
|
+
AppSemaphoreManager._instance = None
|
|
18
|
+
yield
|
|
19
|
+
shutting_down.clear()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def start_worker(deployment) -> tuple[DeployQueueWorker, asyncio.Task]:
|
|
23
|
+
worker = DeployQueueWorker()
|
|
24
|
+
worker._process_deployment = deployment # type: ignore[method-assign]
|
|
25
|
+
runner = asyncio.create_task(worker.run())
|
|
26
|
+
await worker.enqueue({"ref": "refs/heads/master"})
|
|
27
|
+
for _ in range(5):
|
|
28
|
+
await asyncio.sleep(0)
|
|
29
|
+
return worker, runner
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.asyncio
|
|
33
|
+
async def test_drain_waits_for_running_deployment():
|
|
34
|
+
finished = asyncio.Event()
|
|
35
|
+
|
|
36
|
+
async def deployment(work, ref):
|
|
37
|
+
await asyncio.sleep(0.05)
|
|
38
|
+
finished.set()
|
|
39
|
+
|
|
40
|
+
worker, runner = await start_worker(deployment)
|
|
41
|
+
runner.cancel()
|
|
42
|
+
await worker.drain(timeout=5)
|
|
43
|
+
|
|
44
|
+
assert finished.is_set()
|
|
45
|
+
assert shutting_down.is_set()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.mark.asyncio
|
|
49
|
+
async def test_drain_gives_up_after_timeout():
|
|
50
|
+
async def deployment(work, ref):
|
|
51
|
+
await asyncio.Event().wait()
|
|
52
|
+
|
|
53
|
+
worker, runner = await start_worker(deployment)
|
|
54
|
+
runner.cancel()
|
|
55
|
+
await asyncio.wait_for(worker.drain(timeout=0.05), timeout=1)
|
|
56
|
+
|
|
57
|
+
assert worker.is_busy(), "the stuck deployment is left running"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.asyncio
|
|
61
|
+
@patch("gitops_server.workers.deployer.deploy.run")
|
|
62
|
+
@patch("gitops_server.workers.deployer.deploy.post_result_summary")
|
|
63
|
+
@patch("gitops_server.utils.slack.post")
|
|
64
|
+
@patch("gitops_server.workers.deployer.deploy.load_app_definitions", mock_load_app_definitions)
|
|
65
|
+
@patch("gitops_server.workers.deployer.deploy.temp_repo")
|
|
66
|
+
async def test_apps_not_started_are_skipped_once_shutting_down(
|
|
67
|
+
temp_repo_mock, post_mock, summary_mock, run_mock, caplog
|
|
68
|
+
):
|
|
69
|
+
deployer = await Deployer.from_push_event(SAMPLE_GITHUB_PAYLOAD, AppSemaphoreManager())
|
|
70
|
+
shutting_down.set()
|
|
71
|
+
|
|
72
|
+
await deployer.deploy()
|
|
73
|
+
|
|
74
|
+
run_mock.assert_not_called()
|
|
75
|
+
summary_mock.assert_not_called()
|
|
76
|
+
[record] = [r for r in caplog.records if r.levelname == "ERROR"]
|
|
77
|
+
assert record.args == (sorted(deployer.skipped_on_shutdown),)
|
|
78
|
+
assert deployer.skipped_on_shutdown, "the error names the apps to redeploy"
|
|
@@ -222,16 +222,16 @@ wheels = [
|
|
|
222
222
|
|
|
223
223
|
[[package]]
|
|
224
224
|
name = "boto3"
|
|
225
|
-
version = "1.
|
|
225
|
+
version = "1.43.117"
|
|
226
226
|
source = { registry = "https://pypi.org/simple" }
|
|
227
227
|
dependencies = [
|
|
228
228
|
{ name = "botocore" },
|
|
229
229
|
{ name = "jmespath" },
|
|
230
230
|
{ name = "s3transfer" },
|
|
231
231
|
]
|
|
232
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
232
|
+
sdist = { url = "https://files.pythonhosted.org/packages/94/a7/61f0295f79dfdcebb5aa01f86c614fac7e1294faabbd66259d3609c50539/boto3-1.43.117.tar.gz", hash = "sha256:f3d314908d866b577ee6b74ed432c21e565c4c4dbd640ef3fd5e0da30fa13a98", size = 112677, upload-time = "2026-10-21T19:30:03.849Z" }
|
|
233
233
|
wheels = [
|
|
234
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
234
|
+
{ url = "https://files.pythonhosted.org/packages/f3/3b/22b05a545fb95983733d84681364c6b0a36afb0710d832f77732d1fc4c0b/boto3-1.43.117-py3-none-any.whl", hash = "sha256:a5b6cd7e67653987fcf0e212187c16f7b58af2ae192e368f7212ddab7c05b25d", size = 140043, upload-time = "2026-10-21T19:30:02.114Z" },
|
|
235
235
|
]
|
|
236
236
|
|
|
237
237
|
[[package]]
|
|
@@ -249,16 +249,16 @@ wheels = [
|
|
|
249
249
|
|
|
250
250
|
[[package]]
|
|
251
251
|
name = "botocore"
|
|
252
|
-
version = "1.
|
|
252
|
+
version = "1.43.117"
|
|
253
253
|
source = { registry = "https://pypi.org/simple" }
|
|
254
254
|
dependencies = [
|
|
255
255
|
{ name = "jmespath" },
|
|
256
256
|
{ name = "python-dateutil" },
|
|
257
257
|
{ name = "urllib3" },
|
|
258
258
|
]
|
|
259
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
259
|
+
sdist = { url = "https://files.pythonhosted.org/packages/6e/a0/d0a5c44ad6a01f195681ddcb4ce203f06c712811daf1d15f8f8785e8a9f8/botocore-1.43.117.tar.gz", hash = "sha256:71993702832cc37c2495048b9945f2929d306fccb3fb767e9964e437e637fa6c", size = 16366888, upload-time = "2026-10-21T19:29:59.274Z" }
|
|
260
260
|
wheels = [
|
|
261
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
261
|
+
{ url = "https://files.pythonhosted.org/packages/10/94/050e35c9febf6dd2fce4a76be587ae4a323964865700467d9d7b082f7133/botocore-1.43.117-py3-none-any.whl", hash = "sha256:5920ef9b3002e1361846549078549d9154a6e0004c22853c01ce7b279d05d495", size = 16061765, upload-time = "2026-10-21T19:29:56.001Z" },
|
|
262
262
|
]
|
|
263
263
|
|
|
264
264
|
[[package]]
|
|
@@ -536,7 +536,7 @@ wheels = [
|
|
|
536
536
|
|
|
537
537
|
[[package]]
|
|
538
538
|
name = "gitops"
|
|
539
|
-
version = "1.
|
|
539
|
+
version = "1.12.0"
|
|
540
540
|
source = { editable = "." }
|
|
541
541
|
dependencies = [
|
|
542
542
|
{ name = "boto3" },
|
|
@@ -1532,14 +1532,14 @@ wheels = [
|
|
|
1532
1532
|
|
|
1533
1533
|
[[package]]
|
|
1534
1534
|
name = "s3transfer"
|
|
1535
|
-
version = "0.
|
|
1535
|
+
version = "0.19.3"
|
|
1536
1536
|
source = { registry = "https://pypi.org/simple" }
|
|
1537
1537
|
dependencies = [
|
|
1538
1538
|
{ name = "botocore" },
|
|
1539
1539
|
]
|
|
1540
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1540
|
+
sdist = { url = "https://files.pythonhosted.org/packages/49/c5/0a5ce655109cb96b8dfad36908a4e108d15f5dc2eff8fbc3146c4312e704/s3transfer-0.19.3.tar.gz", hash = "sha256:8326acc3d056d7f7b8a37110c7d2aea41f67e83f6ae79cd3b5fbcca34c726406", size = 166087, upload-time = "2026-10-19T19:19:49.751Z" }
|
|
1541
1541
|
wheels = [
|
|
1542
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1542
|
+
{ url = "https://files.pythonhosted.org/packages/e2/a4/88f9e10498759121060b8050a1554753dfc04df39bbbcc7aad9ecc8d9440/s3transfer-0.19.3-py3-none-any.whl", hash = "sha256:24eb7535804306355e31c952a90022bf5b2f9d760f9a11062c7493a217c08553", size = 90252, upload-time = "2026-10-19T19:19:48.309Z" },
|
|
1543
1543
|
]
|
|
1544
1544
|
|
|
1545
1545
|
[[package]]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.11.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|