gitops 0.11.1__tar.gz → 0.11.4__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 (78) hide show
  1. {gitops-0.11.1 → gitops-0.11.4}/PKG-INFO +1 -1
  2. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/Chart.yaml +1 -1
  3. {gitops-0.11.1 → gitops-0.11.4}/gitops/__init__.py +1 -1
  4. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/utils/__init__.py +33 -4
  5. gitops-0.11.4/gitops_server/utils/git.py +88 -0
  6. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/deployer/deploy.py +33 -29
  7. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/deployer/worker.py +13 -2
  8. {gitops-0.11.1 → gitops-0.11.4}/pyproject.toml +2 -1
  9. {gitops-0.11.1 → gitops-0.11.4}/release.py +2 -1
  10. gitops-0.11.4/tests/test_git.py +50 -0
  11. {gitops-0.11.1 → gitops-0.11.4}/uv.lock +15 -1
  12. gitops-0.11.1/gitops_server/utils/git.py +0 -37
  13. {gitops-0.11.1 → gitops-0.11.4}/.dockerignore +0 -0
  14. {gitops-0.11.1 → gitops-0.11.4}/.github/workflows/publish.yml +0 -0
  15. {gitops-0.11.1 → gitops-0.11.4}/.github/workflows/test.yml +0 -0
  16. {gitops-0.11.1 → gitops-0.11.4}/.gitignore +0 -0
  17. {gitops-0.11.1 → gitops-0.11.4}/.mise.toml +0 -0
  18. {gitops-0.11.1 → gitops-0.11.4}/.pre-commit-config.yaml +0 -0
  19. {gitops-0.11.1 → gitops-0.11.4}/Dockerfile +0 -0
  20. {gitops-0.11.1 → gitops-0.11.4}/LICENSE +0 -0
  21. {gitops-0.11.1 → gitops-0.11.4}/README.md +0 -0
  22. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/.helmignore +0 -0
  23. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/README.md +0 -0
  24. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/_helpers.tpl +0 -0
  25. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/certificate.yml +0 -0
  26. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/deployment.yml +0 -0
  27. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/environment.yml +0 -0
  28. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/ingress.yml +0 -0
  29. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/issuer.yml +0 -0
  30. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/secrets.yml +0 -0
  31. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/service.yml +0 -0
  32. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/templates/serviceaccount.yaml +0 -0
  33. {gitops-0.11.1 → gitops-0.11.4}/charts/gitops/values.yaml +0 -0
  34. {gitops-0.11.1 → gitops-0.11.4}/docker-compose.yml +0 -0
  35. {gitops-0.11.1 → gitops-0.11.4}/gitops/common/__init__.py +0 -0
  36. {gitops-0.11.1 → gitops-0.11.4}/gitops/common/app.py +0 -0
  37. {gitops-0.11.1 → gitops-0.11.4}/gitops/common/utils.py +0 -0
  38. {gitops-0.11.1 → gitops-0.11.4}/gitops/core.py +0 -0
  39. {gitops-0.11.1 → gitops-0.11.4}/gitops/db.py +0 -0
  40. {gitops-0.11.1 → gitops-0.11.4}/gitops/main.py +0 -0
  41. {gitops-0.11.1 → gitops-0.11.4}/gitops/py.typed +0 -0
  42. {gitops-0.11.1 → gitops-0.11.4}/gitops/settings.py +0 -0
  43. {gitops-0.11.1 → gitops-0.11.4}/gitops/shorthands.py +0 -0
  44. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/__init__.py +0 -0
  45. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/apps.py +0 -0
  46. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/async_runner.py +0 -0
  47. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/cli.py +0 -0
  48. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/exceptions.py +0 -0
  49. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/images.py +0 -0
  50. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/kube.py +0 -0
  51. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/tags.py +0 -0
  52. {gitops-0.11.1 → gitops-0.11.4}/gitops/utils/yaml.py +0 -0
  53. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/README.md +0 -0
  54. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/__init__.py +0 -0
  55. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/app.py +0 -0
  56. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/main.py +0 -0
  57. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/settings.py +0 -0
  58. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/types.py +0 -0
  59. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/utils/github.py +0 -0
  60. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/utils/slack.py +0 -0
  61. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/__init__.py +0 -0
  62. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/deployer/__init__.py +0 -0
  63. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/deployer/hooks.py +0 -0
  64. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/status_updater/__init__.py +0 -0
  65. {gitops-0.11.1 → gitops-0.11.4}/gitops_server/workers/status_updater/worker.py +0 -0
  66. {gitops-0.11.1 → gitops-0.11.4}/poetry.lock +0 -0
  67. {gitops-0.11.1 → gitops-0.11.4}/ruff.toml +0 -0
  68. {gitops-0.11.1 → gitops-0.11.4}/secrets.example.env +0 -0
  69. {gitops-0.11.1 → gitops-0.11.4}/setup.cfg +0 -0
  70. {gitops-0.11.1 → gitops-0.11.4}/tests/__init__.py +0 -0
  71. {gitops-0.11.1 → gitops-0.11.4}/tests/sample_data.py +0 -0
  72. {gitops-0.11.1 → gitops-0.11.4}/tests/test_app.py +0 -0
  73. {gitops-0.11.1 → gitops-0.11.4}/tests/test_core.py +0 -0
  74. {gitops-0.11.1 → gitops-0.11.4}/tests/test_deploy.py +0 -0
  75. {gitops-0.11.1 → gitops-0.11.4}/tests/test_gitops_server.py +0 -0
  76. {gitops-0.11.1 → gitops-0.11.4}/tests/test_utils.py +0 -0
  77. {gitops-0.11.1 → gitops-0.11.4}/tests/utils.py +0 -0
  78. {gitops-0.11.1 → gitops-0.11.4}/tests/webhook_sample_data.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gitops
3
- Version: 0.11.1
3
+ Version: 0.11.4
4
4
  Summary: Manage multiple apps across one or more k8s clusters.
5
5
  Author-email: Jarek Głowacki <jarekwg@gmail.com>
6
6
  License-File: LICENSE
@@ -2,4 +2,4 @@ apiVersion: v1
2
2
  appVersion: "1.0"
3
3
  description: GitOps Server Helm chart.
4
4
  name: gitops
5
- version: 0.11.1
5
+ version: 0.11.4
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
 
5
5
  from .utils.cli import success, warning
6
6
 
7
- __version__ = "0.11.1"
7
+ __version__ = "0.11.4"
8
8
 
9
9
 
10
10
  # Checking gitops version matches cluster repo version.
@@ -1,12 +1,25 @@
1
1
  import asyncio
2
2
  import logging
3
+ from pathlib import Path
4
+ from collections.abc import Callable
3
5
 
4
6
  from ..types import RunOutput
5
7
 
6
- logger = logging.getLogger("gitops")
8
+ logger = logging.getLogger("gitops.run")
7
9
 
8
10
 
9
- async def run(command, suppress_errors=False) -> RunOutput:
11
+ async def _read_stream(stream: asyncio.StreamReader | None, cb: Callable[[bytes], None]) -> None:
12
+ if stream is None:
13
+ return
14
+ while True:
15
+ line = await stream.readline()
16
+ if line:
17
+ cb(line)
18
+ else:
19
+ break
20
+
21
+
22
+ async def run(command: str, suppress_errors: bool = False, cwd: str | Path | None = None) -> RunOutput:
10
23
  """Run a shell command.
11
24
 
12
25
  Runs the command in an asyncio executor to keep things async. Will
@@ -15,10 +28,26 @@ async def run(command, suppress_errors=False) -> RunOutput:
15
28
  exit_code = 0
16
29
  logger.info(f'Running "{command}".')
17
30
  proc = await asyncio.create_subprocess_shell(
18
- command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
31
+ command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=cwd
32
+ )
33
+ stdout, stderr = b"", b""
34
+
35
+ def log_stdout(line: bytes) -> None:
36
+ nonlocal stdout
37
+ stdout += line
38
+ logger.info(f"{line.decode()}")
39
+
40
+ def log_stderr(line: bytes) -> None:
41
+ nonlocal stderr
42
+ stderr += line
43
+ logger.info(f"{line.decode()}")
44
+
45
+ await asyncio.gather(
46
+ _read_stream(proc.stdout, log_stdout),
47
+ _read_stream(proc.stderr, log_stderr),
19
48
  )
20
49
 
21
- stdout, stderr = await proc.communicate()
50
+ await proc.communicate()
22
51
  exit_code = proc.returncode if proc.returncode not in (None, 128) else 1 # type: ignore
23
52
  if exit_code == 0:
24
53
  return RunOutput(exit_code=exit_code, output=stdout.decode())
@@ -0,0 +1,88 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import re
5
+ import tempfile
6
+ from collections.abc import AsyncGenerator
7
+ from contextlib import asynccontextmanager
8
+ from pathlib import Path
9
+
10
+ from opentelemetry.trace import get_tracer
11
+
12
+ from . import run
13
+
14
+ tracer = get_tracer(__name__)
15
+ logger = logging.getLogger("gitops")
16
+
17
+ REPO_CACHE_DIR = Path("/tmp/gitops/repocache")
18
+ REPO_CACHE: dict[str, Path] = {}
19
+ repo_lock = asyncio.Semaphore(1)
20
+
21
+
22
+ async def clone_repo(git_repo_url: str, path: str, sha: str | None = None, branch: str | None = None):
23
+ """Shallow Clones a git repo url to path and git-crypt unlocks all encrypted files"""
24
+ logger.info(f'Cloning "{git_repo_url}".')
25
+
26
+ url_with_oauth_token = git_repo_url.replace("://", f"://{os.environ['GITHUB_OAUTH_TOKEN'].strip()}@")
27
+
28
+ with tracer.start_as_current_span("tempo_repo.clone_repo"):
29
+ if sha:
30
+ await run(f"git clone --depth 100 {url_with_oauth_token} {path}; cd {path}; git checkout {sha}")
31
+ elif branch:
32
+ await run(f"git clone --depth 1 {url_with_oauth_token} {path} --branch {branch}")
33
+ else:
34
+ await run(f"git clone --depth 100 {url_with_oauth_token} {path};")
35
+
36
+ if GIT_CRYPT_KEY_FILE := os.environ.get("GIT_CRYPT_KEY_FILE"):
37
+ with tracer.start_as_current_span("temp_repo.git_crypt_unlock"):
38
+ await run(f"cd {path}; git-crypt unlock {GIT_CRYPT_KEY_FILE}")
39
+
40
+
41
+ def is_sha(sha_or_ref: str | None) -> bool:
42
+ """Check if the given string is a valid SHA-1 hash or a short SHA."""
43
+ if not sha_or_ref:
44
+ return False
45
+ return bool(re.fullmatch(r"[a-fA-F0-9]{4,40}", sha_or_ref))
46
+
47
+
48
+ @asynccontextmanager
49
+ async def temp_repo(git_repo_url: str, ref: str | None) -> AsyncGenerator[str, None]:
50
+ """Checks out a git_repo_url to a temporary folder location. Returns temporary folder location"""
51
+ # Someone passed us a branch name, not a sha
52
+
53
+ if not is_sha(ref):
54
+ async with temp_repo_branch(git_repo_url, branch=ref) as temp_repo_path:
55
+ yield temp_repo_path
56
+ return
57
+
58
+ with tracer.start_as_current_span("checkout_temp_repo"):
59
+ cache_path = REPO_CACHE_DIR / git_repo_url.split("/")[-1].split(".")[0]
60
+
61
+ # Prep the repo cache
62
+ if not (cache_path / ".git").exists():
63
+ logger.info("Repo %s not in cache, cloning", git_repo_url)
64
+ async with repo_lock:
65
+ if cache_path.exists():
66
+ await run(f"rm -rf {cache_path}", suppress_errors=True)
67
+ if not cache_path.exists():
68
+ cache_path.mkdir(parents=True)
69
+ REPO_CACHE[git_repo_url] = cache_path
70
+ await clone_repo(git_repo_url, path=str(cache_path))
71
+
72
+ # Copy the repo cache to a temporary folder
73
+ with tempfile.TemporaryDirectory() as temporary_folder_path:
74
+ await run(f"rm -rf {temporary_folder_path}", suppress_errors=True)
75
+ await run(f"cp -r {cache_path} {temporary_folder_path}")
76
+ await run(f"cd {temporary_folder_path};git fetch; git checkout {ref}")
77
+
78
+ yield temporary_folder_path
79
+
80
+
81
+ @asynccontextmanager
82
+ async def temp_repo_branch(git_repo_url: str, branch: str | None) -> AsyncGenerator[str, None]:
83
+ """Checks out a git_repo_url to a temporary folder location. Returns temporary folder location"""
84
+ with tracer.start_as_current_span("checkout_temp_repo_branch"):
85
+ # Copy the repo cache to a temporary folder
86
+ with tempfile.TemporaryDirectory() as temporary_folder_path:
87
+ await clone_repo(git_repo_url, path=str(temporary_folder_path), branch=branch)
88
+ yield temporary_folder_path
@@ -65,7 +65,7 @@ async def post_result_summary(source: str, results: list[UpdateAppResult]):
65
65
  @tracer.start_as_current_span("load_app_definitions")
66
66
  async def load_app_definitions(url: str, sha: str) -> AppDefinitions:
67
67
  logger.info(f'Loading app definitions at "{sha}".')
68
- async with temp_repo(url, sha=sha) as repo:
68
+ async with temp_repo(url, ref=sha) as repo:
69
69
  app_definitions = AppDefinitions(name=get_repo_name_from_url(url))
70
70
  app_definitions.from_path(repo)
71
71
  return app_definitions
@@ -115,33 +115,37 @@ class Deployer:
115
115
  )
116
116
 
117
117
  async def deploy(self):
118
- with tracer.start_as_current_span("deploy"):
119
- added_apps, updated_apps, removed_apps = self.calculate_app_deltas()
120
- if not (added_apps | updated_apps | removed_apps):
121
- logger.info("No deltas; aborting.")
122
- return
123
- logger.info(
124
- f"Running deployment for these deltas: A{list(added_apps)}, U{list(updated_apps)},"
125
- f" R{list(removed_apps)}"
126
- )
127
- await post_init_summary(
128
- source=self.current_app_definitions.name,
129
- username=self.author_name,
130
- added_apps=added_apps,
131
- updated_apps=updated_apps,
132
- removed_apps=removed_apps,
133
- commit_message=self.commit_message,
134
- )
135
- update_results = await asyncio.gather(
136
- *[
137
- self.update_app_deployment(self.current_app_definitions.apps[app_name])
138
- for app_name in (added_apps | updated_apps)
139
- ]
140
- )
141
- uninstall_results = await asyncio.gather(
142
- *[self.uninstall_app(self.previous_app_definitions.apps[app_name]) for app_name in removed_apps]
143
- )
144
- await post_result_summary(self.current_app_definitions.name, update_results + uninstall_results)
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}):
@@ -169,7 +173,7 @@ class Deployer:
169
173
  if app.chart.type == "git":
170
174
  span.set_attribute("gitops.chart.type", "git")
171
175
  assert app.chart.git_repo_url
172
- async with temp_repo(app.chart.git_repo_url, sha=app.chart.git_sha) as chart_folder_path:
176
+ async with temp_repo(app.chart.git_repo_url, ref=app.chart.git_sha) as chart_folder_path:
173
177
  with tracer.start_as_current_span("helm_dependency_build"):
174
178
  await run(f"cd {chart_folder_path}; helm dependency build")
175
179
 
@@ -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
- deployer = await Deployer.from_push_event(work)
56
- await deployer.deploy()
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()
@@ -1,7 +1,7 @@
1
1
 
2
2
  [project]
3
3
  name = "gitops"
4
- version = "0.11.1"
4
+ version = "0.11.4"
5
5
  description = "Manage multiple apps across one or more k8s clusters."
6
6
  requires-python = ">=3.12"
7
7
  readme = "README.md"
@@ -44,6 +44,7 @@ dev-dependencies = [
44
44
  "types-pyyaml>=6.0.12.20240808",
45
45
  "types-tabulate>=0.9.0.20240106",
46
46
  "boto3-stubs>=1.35.11",
47
+ "pytest-asyncio>=0.24.0",
47
48
  ]
48
49
 
49
50
 
@@ -4,8 +4,9 @@ import re
4
4
  pyproject = pathlib.Path("pyproject.toml")
5
5
  init = pathlib.Path("gitops/__init__.py")
6
6
  chart = pathlib.Path("charts/gitops/Chart.yaml")
7
+ current = re.search(r'version = "(.*)"', pyproject.read_text()).group(1) # type: ignore
7
8
 
8
- version = input("Enter a new version eg: (0.9.1): ").strip()
9
+ version = input(f"Enter a new version. Currently at ({current}): ").strip()
9
10
 
10
11
  pyproject_text = pyproject.read_text()
11
12
  pyproject.write_text(re.sub(r'version = ".*"', f'version = "{version}"', pyproject_text, count=1))
@@ -0,0 +1,50 @@
1
+ import os
2
+ import tempfile
3
+ from contextlib import asynccontextmanager
4
+
5
+ import pytest
6
+
7
+ from gitops_server.utils import git, run
8
+
9
+ os.environ["GITHUB_OAUTH_TOKEN"] = ""
10
+
11
+
12
+ @asynccontextmanager
13
+ async def make_dummy_repo():
14
+ with tempfile.TemporaryDirectory() as temporary_folder_path:
15
+ if os.environ.get("CI"):
16
+ await run("git config --global user.email 'you@example.com'")
17
+ await run("git config --global user.name 'Your Name'")
18
+ await run("git config --global init.defaultBranch main")
19
+ await run("git init", cwd=temporary_folder_path)
20
+ for _ in range(10):
21
+ await run("git commit -m 'temporary commit' --allow-empty", cwd=temporary_folder_path)
22
+ await run("git checkout -b 'test'", cwd=temporary_folder_path)
23
+ for _ in range(10):
24
+ await run("git commit -m 'temporary commit' --allow-empty", cwd=temporary_folder_path)
25
+ await run("git checkout main", cwd=temporary_folder_path)
26
+ yield temporary_folder_path
27
+
28
+
29
+ @pytest.mark.asyncio
30
+ class TestGit:
31
+ async def test_temp_repo_with_branch(self):
32
+ async with make_dummy_repo() as test_repo:
33
+ async with git.temp_repo(test_repo, ref="test"):
34
+ pass
35
+
36
+ async with git.temp_repo(test_repo, ref="main"):
37
+ pass
38
+
39
+ async def test_temp_repo_with_no_ref(self):
40
+ async with make_dummy_repo() as test_repo:
41
+ async with git.temp_repo(test_repo, ref=None):
42
+ pass
43
+
44
+ async def test_temp_repo_with_sha(self):
45
+ async with make_dummy_repo() as test_repo:
46
+ sha = (await run("git rev-parse HEAD", cwd=test_repo))["output"].strip()
47
+ assert git.is_sha(sha)
48
+
49
+ async with git.temp_repo(test_repo, ref=sha):
50
+ pass
@@ -277,7 +277,7 @@ wheels = [
277
277
 
278
278
  [[package]]
279
279
  name = "gitops"
280
- version = "0.11.0"
280
+ version = "0.11.3"
281
281
  source = { editable = "." }
282
282
  dependencies = [
283
283
  { name = "boto3" },
@@ -305,6 +305,7 @@ dev = [
305
305
  { name = "boto3-stubs" },
306
306
  { name = "mypy" },
307
307
  { name = "pytest" },
308
+ { name = "pytest-asyncio" },
308
309
  { name = "ruff" },
309
310
  { name = "types-colorama" },
310
311
  { name = "types-pyyaml" },
@@ -334,6 +335,7 @@ dev = [
334
335
  { name = "boto3-stubs", specifier = ">=1.35.11" },
335
336
  { name = "mypy", specifier = ">=1.11.2" },
336
337
  { name = "pytest", specifier = ">=8.3.2" },
338
+ { name = "pytest-asyncio", specifier = ">=0.24.0" },
337
339
  { name = "ruff", specifier = ">=0.6.3" },
338
340
  { name = "types-colorama", specifier = ">=0.4.15.20240311" },
339
341
  { name = "types-pyyaml", specifier = ">=6.0.12.20240808" },
@@ -941,6 +943,18 @@ wheels = [
941
943
  { url = "https://files.pythonhosted.org/packages/0f/f9/cf155cf32ca7d6fa3601bc4c5dd19086af4b320b706919d48a4c79081cf9/pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", size = 341802 },
942
944
  ]
943
945
 
946
+ [[package]]
947
+ name = "pytest-asyncio"
948
+ version = "0.24.0"
949
+ source = { registry = "https://pypi.org/simple" }
950
+ dependencies = [
951
+ { name = "pytest" },
952
+ ]
953
+ sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 }
954
+ wheels = [
955
+ { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 },
956
+ ]
957
+
944
958
  [[package]]
945
959
  name = "python-dateutil"
946
960
  version = "2.9.0.post0"
@@ -1,37 +0,0 @@
1
- import logging
2
- import os
3
- import tempfile
4
- from collections.abc import AsyncGenerator
5
- from contextlib import asynccontextmanager
6
-
7
- from opentelemetry.trace import get_tracer
8
-
9
- from . import run
10
-
11
- tracer = get_tracer(__name__)
12
-
13
- BASE_REPO_DIR = "/var/gitops/repos"
14
-
15
- logger = logging.getLogger("gitops")
16
-
17
-
18
- async def clone_repo(git_repo_url: str, path: str, sha: str | None = None):
19
- """Shallow Clones a git repo url to path and git-crypt unlocks all encrypted files"""
20
- logger.info(f'Cloning "{git_repo_url}".')
21
-
22
- url_with_oauth_token = git_repo_url.replace("://", f"://{os.environ['GITHUB_OAUTH_TOKEN'].strip()}@")
23
-
24
- with tracer.start_as_current_span("tempo_repo.clone_repo"):
25
- await run(f"git clone {url_with_oauth_token} {path}; cd {path}; git checkout {sha}")
26
-
27
- with tracer.start_as_current_span("temp_repo.git_crypt_unlock"):
28
- await run(f'cd {path}; git-crypt unlock {os.environ["GIT_CRYPT_KEY_FILE"]}')
29
-
30
-
31
- @asynccontextmanager
32
- async def temp_repo(git_repo_url: str, sha: str | None = None) -> AsyncGenerator[str, None]:
33
- """Checks out a git_repo_url to a temporary folder location. Returns temporary folder location"""
34
- with tracer.start_as_current_span("checkout_temp_repo"):
35
- with tempfile.TemporaryDirectory() as temporary_folder_path:
36
- await clone_repo(git_repo_url, path=temporary_folder_path, sha=sha)
37
- yield temporary_folder_path
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