gitops 0.11.0__tar.gz → 0.11.3__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-0.11.0 → gitops-0.11.3}/Dockerfile +1 -0
- {gitops-0.11.0 → gitops-0.11.3}/PKG-INFO +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/Chart.yaml +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/environment.yml +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/docker-compose.yml +0 -1
- {gitops-0.11.0 → gitops-0.11.3}/gitops/__init__.py +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/utils/__init__.py +4 -3
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/utils/git.py +23 -5
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/deployer/deploy.py +31 -27
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/deployer/worker.py +13 -2
- {gitops-0.11.0 → gitops-0.11.3}/pyproject.toml +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/uv.lock +1 -1
- {gitops-0.11.0 → gitops-0.11.3}/.dockerignore +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/.github/workflows/publish.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/.github/workflows/test.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/.gitignore +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/.mise.toml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/.pre-commit-config.yaml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/LICENSE +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/README.md +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/.helmignore +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/README.md +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/_helpers.tpl +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/certificate.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/deployment.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/ingress.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/issuer.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/secrets.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/service.yml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/templates/serviceaccount.yaml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/charts/gitops/values.yaml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/common/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/common/app.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/common/utils.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/core.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/db.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/main.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/py.typed +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/settings.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/shorthands.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/apps.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/async_runner.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/cli.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/exceptions.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/images.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/kube.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/tags.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops/utils/yaml.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/README.md +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/app.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/main.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/settings.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/types.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/utils/github.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/utils/slack.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/deployer/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/deployer/hooks.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/status_updater/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/gitops_server/workers/status_updater/worker.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/poetry.lock +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/release.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/ruff.toml +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/secrets.example.env +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/setup.cfg +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/__init__.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/sample_data.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/test_app.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/test_core.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/test_deploy.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/test_gitops_server.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/test_utils.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/utils.py +0 -0
- {gitops-0.11.0 → gitops-0.11.3}/tests/webhook_sample_data.py +0 -0
|
@@ -39,6 +39,7 @@ COPY --link=true pyproject.toml uv.lock /app/
|
|
|
39
39
|
RUN --mount=type=cache,target=/root/.cache/ \
|
|
40
40
|
(uv sync --frozen --no-install-project --extra server || uv sync --frozen --no-install-project --extra server)
|
|
41
41
|
# Install dependencies
|
|
42
|
+
RUN git config --global advice.detachedHead false
|
|
42
43
|
|
|
43
44
|
COPY cluster.key /app/
|
|
44
45
|
COPY gitops /app/gitops/
|
|
@@ -15,11 +15,12 @@ async def run(command, suppress_errors=False) -> RunOutput:
|
|
|
15
15
|
exit_code = 0
|
|
16
16
|
logger.info(f'Running "{command}".')
|
|
17
17
|
proc = await asyncio.create_subprocess_shell(
|
|
18
|
-
command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
|
18
|
+
command, # stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
-
stdout, stderr =
|
|
22
|
-
|
|
21
|
+
stdout, stderr = b"", b""
|
|
22
|
+
await proc.communicate()
|
|
23
|
+
exit_code = proc.returncode if proc.returncode not in (None, 128) else 1 # type: ignore
|
|
23
24
|
if exit_code == 0:
|
|
24
25
|
return RunOutput(exit_code=exit_code, output=stdout.decode())
|
|
25
26
|
else:
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
import os
|
|
3
4
|
import tempfile
|
|
4
5
|
from collections.abc import AsyncGenerator
|
|
5
6
|
from contextlib import asynccontextmanager
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
|
|
7
9
|
from opentelemetry.trace import get_tracer
|
|
8
10
|
|
|
9
11
|
from . import run
|
|
10
12
|
|
|
11
13
|
tracer = get_tracer(__name__)
|
|
12
|
-
|
|
13
|
-
BASE_REPO_DIR = "/var/gitops/repos"
|
|
14
|
-
|
|
15
14
|
logger = logging.getLogger("gitops")
|
|
16
15
|
|
|
16
|
+
REPO_CACHE_DIR = Path("/tmp/gitops/repocache")
|
|
17
|
+
REPO_CACHE: dict[str, Path] = {}
|
|
18
|
+
repo_lock = asyncio.Semaphore(1)
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
async def clone_repo(git_repo_url: str, path: str, sha: str | None = None):
|
|
19
22
|
"""Shallow Clones a git repo url to path and git-crypt unlocks all encrypted files"""
|
|
@@ -22,7 +25,10 @@ async def clone_repo(git_repo_url: str, path: str, sha: str | None = None):
|
|
|
22
25
|
url_with_oauth_token = git_repo_url.replace("://", f"://{os.environ['GITHUB_OAUTH_TOKEN'].strip()}@")
|
|
23
26
|
|
|
24
27
|
with tracer.start_as_current_span("tempo_repo.clone_repo"):
|
|
25
|
-
|
|
28
|
+
if sha:
|
|
29
|
+
await run(f"git clone --depth 100 {url_with_oauth_token} {path}; cd {path}; git checkout {sha}")
|
|
30
|
+
else:
|
|
31
|
+
await run(f"git clone --depth 100 {url_with_oauth_token} {path};")
|
|
26
32
|
|
|
27
33
|
with tracer.start_as_current_span("temp_repo.git_crypt_unlock"):
|
|
28
34
|
await run(f'cd {path}; git-crypt unlock {os.environ["GIT_CRYPT_KEY_FILE"]}')
|
|
@@ -32,6 +38,18 @@ async def clone_repo(git_repo_url: str, path: str, sha: str | None = None):
|
|
|
32
38
|
async def temp_repo(git_repo_url: str, sha: str | None = None) -> AsyncGenerator[str, None]:
|
|
33
39
|
"""Checks out a git_repo_url to a temporary folder location. Returns temporary folder location"""
|
|
34
40
|
with tracer.start_as_current_span("checkout_temp_repo"):
|
|
41
|
+
cache_path = REPO_CACHE_DIR / git_repo_url.split("/")[-1].split(".")[0]
|
|
42
|
+
if not (cache_path / ".git").exists():
|
|
43
|
+
logger.info("Repo %s not in cache, cloning", git_repo_url)
|
|
44
|
+
async with repo_lock:
|
|
45
|
+
if cache_path.exists():
|
|
46
|
+
await run(f"rm -rf {cache_path}", suppress_errors=True)
|
|
47
|
+
if not cache_path.exists():
|
|
48
|
+
cache_path.mkdir(parents=True)
|
|
49
|
+
REPO_CACHE[git_repo_url] = cache_path
|
|
50
|
+
await clone_repo(git_repo_url, path=str(cache_path))
|
|
35
51
|
with tempfile.TemporaryDirectory() as temporary_folder_path:
|
|
36
|
-
await
|
|
52
|
+
await run(f"rm -rf {temporary_folder_path}", suppress_errors=True)
|
|
53
|
+
await run(f"cp -r {cache_path} {temporary_folder_path}")
|
|
54
|
+
await run(f"cd {temporary_folder_path};git fetch; git checkout {sha}")
|
|
37
55
|
yield temporary_folder_path
|
|
@@ -115,33 +115,37 @@ class Deployer:
|
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
async def deploy(self):
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
118
|
+
added_apps, updated_apps, removed_apps = self.calculate_app_deltas()
|
|
119
|
+
current_span = trace.get_current_span()
|
|
120
|
+
if current_span:
|
|
121
|
+
current_span.set_attribute("gitops.added_apps", len(added_apps))
|
|
122
|
+
current_span.set_attribute("gitops.updated_aps", len(updated_apps))
|
|
123
|
+
current_span.set_attribute("gitops.removed_app", len(removed_apps))
|
|
124
|
+
if not (added_apps | updated_apps | removed_apps):
|
|
125
|
+
logger.info("No deltas; aborting.")
|
|
126
|
+
return
|
|
127
|
+
logger.info(
|
|
128
|
+
f"Running deployment for these deltas: A{list(added_apps)}, U{list(updated_apps)},"
|
|
129
|
+
f" R{list(removed_apps)}"
|
|
130
|
+
)
|
|
131
|
+
await post_init_summary(
|
|
132
|
+
source=self.current_app_definitions.name,
|
|
133
|
+
username=self.author_name,
|
|
134
|
+
added_apps=added_apps,
|
|
135
|
+
updated_apps=updated_apps,
|
|
136
|
+
removed_apps=removed_apps,
|
|
137
|
+
commit_message=self.commit_message,
|
|
138
|
+
)
|
|
139
|
+
update_results = await asyncio.gather(
|
|
140
|
+
*[
|
|
141
|
+
self.update_app_deployment(self.current_app_definitions.apps[app_name])
|
|
142
|
+
for app_name in (added_apps | updated_apps)
|
|
143
|
+
]
|
|
144
|
+
)
|
|
145
|
+
uninstall_results = await asyncio.gather(
|
|
146
|
+
*[self.uninstall_app(self.previous_app_definitions.apps[app_name]) for app_name in removed_apps]
|
|
147
|
+
)
|
|
148
|
+
await post_result_summary(self.current_app_definitions.name, update_results + uninstall_results)
|
|
145
149
|
|
|
146
150
|
async def uninstall_app(self, app: App) -> UpdateAppResult:
|
|
147
151
|
with tracer.start_as_current_span("uninstall_app", attributes={"app": app.name}):
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
+
from opentelemetry import trace
|
|
5
|
+
|
|
4
6
|
from .deploy import Deployer
|
|
5
7
|
|
|
6
8
|
logger = logging.getLogger("gitops_worker")
|
|
7
9
|
|
|
10
|
+
tracer = trace.get_tracer(__name__)
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
class DeployQueueWorker:
|
|
10
14
|
"""Simple synchronous background work queue.
|
|
@@ -52,5 +56,12 @@ class DeployQueueWorker:
|
|
|
52
56
|
ref = work.get("ref")
|
|
53
57
|
logger.info(f'Have a push to "{ref}".')
|
|
54
58
|
if ref == "refs/heads/master":
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
with tracer.start_as_current_span("gitops_process_webhook") as current_span:
|
|
60
|
+
deployer = await Deployer.from_push_event(work)
|
|
61
|
+
current_span.set_attribute("gitops.ref", ref)
|
|
62
|
+
current_span.set_attribute("gitops.after", work.get("after"))
|
|
63
|
+
current_span.set_attribute("gitops.before", work.get("before"))
|
|
64
|
+
current_span.set_attribute("gitops.author_email", deployer.author_email)
|
|
65
|
+
current_span.set_attribute("gitops.author_name", deployer.author_name)
|
|
66
|
+
current_span.set_attribute("gitops.commit_message", deployer.commit_message)
|
|
67
|
+
await deployer.deploy()
|
|
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
|