apexcodexpy 0.3.2__tar.gz → 0.4.1__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.
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/PKG-INFO +1 -1
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/pyproject.py +1 -8
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/service.py +8 -12
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/typer/root.py +1 -6
- apexcodexpy-0.4.1/codex/infra/workflows.py +175 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/pyproject.toml +1 -1
- apexcodexpy-0.3.2/codex/infra/dependabot.py +0 -95
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/LICENSE +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/README.md +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/__init__.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/__init__.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/program.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/reporters.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/result.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/tools.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/typer/__init__.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/typer/device.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/infra/typer/testing.py +0 -0
- {apexcodexpy-0.3.2 → apexcodexpy-0.4.1}/codex/runner.py +0 -0
|
@@ -68,8 +68,6 @@ STANDARDS = {
|
|
|
68
68
|
|
|
69
69
|
@dataclass(frozen=True)
|
|
70
70
|
class PyProjectToml:
|
|
71
|
-
dry_run: bool = False
|
|
72
|
-
|
|
73
71
|
_result: ProgramResult = field(init=False, default=ProgramResult(name="PyProject"))
|
|
74
72
|
|
|
75
73
|
NAME = "pyproject.toml"
|
|
@@ -82,17 +80,12 @@ class PyProjectToml:
|
|
|
82
80
|
return self._result.fail(message=f"{self.NAME} not found.")
|
|
83
81
|
|
|
84
82
|
document = tomlkit.parse(pyproject.read_text(encoding="utf-8"))
|
|
85
|
-
|
|
86
|
-
# Create a copy for comparison if dry-run
|
|
87
83
|
original = tomlkit.dumps(document)
|
|
88
84
|
deep_merge(STANDARDS, document)
|
|
89
85
|
modified = tomlkit.dumps(document)
|
|
90
86
|
|
|
91
87
|
if original == modified:
|
|
92
|
-
return self._result.success(message=
|
|
93
|
-
|
|
94
|
-
if self.dry_run:
|
|
95
|
-
return self._result.success(message=f"Changes to apply:\n{modified}")
|
|
88
|
+
return self._result.success(message="Already up to date.\n")
|
|
96
89
|
|
|
97
90
|
pyproject.write_text(modified, encoding="utf-8", newline="\n")
|
|
98
91
|
|
|
@@ -4,16 +4,11 @@ from dataclasses import dataclass
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from time import time
|
|
6
6
|
|
|
7
|
-
from codex.infra.
|
|
8
|
-
from codex.infra.program import (
|
|
9
|
-
Command,
|
|
10
|
-
Executable,
|
|
11
|
-
MultiProgram,
|
|
12
|
-
Program,
|
|
13
|
-
)
|
|
7
|
+
from codex.infra.program import Command, Executable, MultiProgram, Program
|
|
14
8
|
from codex.infra.pyproject import PyProjectToml
|
|
15
9
|
from codex.infra.result import TaskResult
|
|
16
10
|
from codex.infra.tools import MyPy, Pip, Poetry, PoetryRun, Pytest, Ruff
|
|
11
|
+
from codex.infra.workflows import CheckCodeWorkflow, DependabotWorkflow
|
|
17
12
|
|
|
18
13
|
|
|
19
14
|
@dataclass(frozen=True)
|
|
@@ -30,8 +25,8 @@ class Codex:
|
|
|
30
25
|
|
|
31
26
|
return self
|
|
32
27
|
|
|
33
|
-
def sync(self
|
|
34
|
-
return self.tools.sync(
|
|
28
|
+
def sync(self) -> TaskResult:
|
|
29
|
+
return self.tools.sync().execute()
|
|
35
30
|
|
|
36
31
|
def lock(self) -> TaskResult:
|
|
37
32
|
return self.tools.lock().execute()
|
|
@@ -102,11 +97,12 @@ class CiCodex:
|
|
|
102
97
|
class CodexTools:
|
|
103
98
|
target: Path
|
|
104
99
|
|
|
105
|
-
def sync(self
|
|
100
|
+
def sync(self) -> Executable:
|
|
106
101
|
return (
|
|
107
102
|
MultiProgram()
|
|
108
|
-
.attach(PyProjectToml(
|
|
109
|
-
.attach(
|
|
103
|
+
.attach(PyProjectToml().sync(self.target))
|
|
104
|
+
.attach(DependabotWorkflow().sync(self.target))
|
|
105
|
+
.attach(CheckCodeWorkflow().sync(self.target))
|
|
110
106
|
)
|
|
111
107
|
|
|
112
108
|
def lock(self) -> Executable:
|
|
@@ -19,15 +19,10 @@ def _(
|
|
|
19
19
|
"-p",
|
|
20
20
|
help="Target directory.",
|
|
21
21
|
),
|
|
22
|
-
dry_run: bool = typer.Option(
|
|
23
|
-
False,
|
|
24
|
-
"--dry-run",
|
|
25
|
-
help="Show changes without applying them.",
|
|
26
|
-
),
|
|
27
22
|
) -> None:
|
|
28
23
|
(
|
|
29
24
|
Codex.target(Path(path))
|
|
30
|
-
.sync(
|
|
25
|
+
.sync()
|
|
31
26
|
.report(using=DefaultReporter())
|
|
32
27
|
.exit(using=typer.Exit)
|
|
33
28
|
)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
|
+
from functools import cached_property
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
from yaml import MappingNode
|
|
13
|
+
|
|
14
|
+
from codex.infra.program import Executable
|
|
15
|
+
from codex.infra.result import ProgramResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, kw_only=True)
|
|
19
|
+
class YamlWorkflow(ABC):
|
|
20
|
+
name: str
|
|
21
|
+
path: Path
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def standards(self) -> Mapping[Any, Any]:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def sync(self, target: Path) -> Executable:
|
|
29
|
+
path = target / self.path
|
|
30
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
path.write_text(
|
|
32
|
+
"# Auto Generated\n"
|
|
33
|
+
"# By: Apex Codex\n"
|
|
34
|
+
f"# At: {self._utcnow()}\n"
|
|
35
|
+
"# Do not edit this file manually!\n\n"
|
|
36
|
+
+ yaml.dump(
|
|
37
|
+
self.standards,
|
|
38
|
+
Dumper=CustomDumper,
|
|
39
|
+
sort_keys=False,
|
|
40
|
+
default_flow_style=False,
|
|
41
|
+
).replace("'on':", "on:"),
|
|
42
|
+
encoding="utf-8",
|
|
43
|
+
newline="\n",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return self.success()
|
|
47
|
+
|
|
48
|
+
def _utcnow(self) -> str:
|
|
49
|
+
return datetime.now(tz=UTC).replace(microsecond=0).isoformat(sep=" ")
|
|
50
|
+
|
|
51
|
+
def success(self) -> ProgramResult:
|
|
52
|
+
return ProgramResult(name=self.name).success(
|
|
53
|
+
message=f"{self.name} workflow succeeded.\n"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CustomDumper(yaml.Dumper):
|
|
58
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
59
|
+
self.custom_anchors = iter(["checkout", "setup-python"])
|
|
60
|
+
super().__init__(*args, **kwargs)
|
|
61
|
+
|
|
62
|
+
def increase_indent(self, flow: bool = False, indentless: bool = False) -> None:
|
|
63
|
+
_ = indentless
|
|
64
|
+
|
|
65
|
+
return super().increase_indent(flow, False)
|
|
66
|
+
|
|
67
|
+
def generate_anchor(self, node: MappingNode) -> str:
|
|
68
|
+
"""Override PyYAML's default 'id001' generation logic."""
|
|
69
|
+
try:
|
|
70
|
+
return next(self.custom_anchors)
|
|
71
|
+
except StopIteration:
|
|
72
|
+
return super().generate_anchor(node) # type: ignore
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True, kw_only=True)
|
|
76
|
+
class CheckCodeWorkflow(YamlWorkflow):
|
|
77
|
+
name: str = "CheckCode"
|
|
78
|
+
path: Path = Path(".github") / "workflows" / "check_code.yml"
|
|
79
|
+
|
|
80
|
+
python_version: float | int = 3.13
|
|
81
|
+
checkout_version: str = "v6"
|
|
82
|
+
setup_python_version: str = "v6"
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def standards(self) -> Mapping[str, Any]:
|
|
86
|
+
return {
|
|
87
|
+
"name": "Validate Python Code",
|
|
88
|
+
"on": {"pull_request": {"branches": ["main"]}},
|
|
89
|
+
"permissions": {"contents": "read"},
|
|
90
|
+
"jobs": self._jobs(
|
|
91
|
+
lint="codex lint --ci",
|
|
92
|
+
test_unit="codex test unit --ci",
|
|
93
|
+
test_integration="codex test integration --ci",
|
|
94
|
+
test_behaviour="codex test behaviour --ci",
|
|
95
|
+
),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def _jobs(self, **commands: str) -> dict[str, Any]:
|
|
99
|
+
return {
|
|
100
|
+
name.replace("_", "-"): {
|
|
101
|
+
"runs-on": "ubuntu-latest",
|
|
102
|
+
"steps": [
|
|
103
|
+
self._checkout,
|
|
104
|
+
self._setup,
|
|
105
|
+
{
|
|
106
|
+
"name": name.capitalize().replace("_", " "),
|
|
107
|
+
"run": value,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
}
|
|
111
|
+
for name, value in commands.items()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@cached_property
|
|
115
|
+
def _checkout(self) -> dict[str, str]:
|
|
116
|
+
return {"uses": f"actions/checkout@{self.checkout_version}"}
|
|
117
|
+
|
|
118
|
+
@cached_property
|
|
119
|
+
def _setup(self) -> dict[str, Any]:
|
|
120
|
+
return {
|
|
121
|
+
"name": "Set up Python",
|
|
122
|
+
"uses": "actions/setup-python@v6",
|
|
123
|
+
"with": {
|
|
124
|
+
"python-version": self.python_version,
|
|
125
|
+
"pip-install": "apexcodexpy",
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass(frozen=True, kw_only=True)
|
|
131
|
+
class DependabotWorkflow(YamlWorkflow):
|
|
132
|
+
name: str = "Dependabot"
|
|
133
|
+
path: Path = Path(".github") / "dependabot.yml"
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def standards(self) -> Mapping[str, Any]:
|
|
137
|
+
return {
|
|
138
|
+
"version": 2,
|
|
139
|
+
"updates": [
|
|
140
|
+
self._pip_config(),
|
|
141
|
+
self._gh_actions_config(),
|
|
142
|
+
],
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _pip_config() -> dict[str, Any]:
|
|
147
|
+
return {
|
|
148
|
+
"package-ecosystem": "pip",
|
|
149
|
+
"directory": "/",
|
|
150
|
+
"schedule": {
|
|
151
|
+
"interval": "weekly",
|
|
152
|
+
"day": "thursday",
|
|
153
|
+
},
|
|
154
|
+
"groups": {
|
|
155
|
+
"python": {
|
|
156
|
+
"patterns": ["*"],
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def _gh_actions_config() -> dict[str, Any]:
|
|
163
|
+
return {
|
|
164
|
+
"package-ecosystem": "github-actions",
|
|
165
|
+
"directory": "/",
|
|
166
|
+
"schedule": {
|
|
167
|
+
"interval": "weekly",
|
|
168
|
+
"day": "thursday",
|
|
169
|
+
},
|
|
170
|
+
"groups": {
|
|
171
|
+
"gh-actions": {
|
|
172
|
+
"patterns": ["*"],
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from dataclasses import dataclass, field
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
import yaml
|
|
9
|
-
|
|
10
|
-
from codex.infra.program import Executable
|
|
11
|
-
from codex.infra.result import ProgramResult
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _pip_config() -> dict[str, Any]:
|
|
15
|
-
return {
|
|
16
|
-
"package-ecosystem": "pip",
|
|
17
|
-
"directory": "/",
|
|
18
|
-
"schedule": {
|
|
19
|
-
"interval": "weekly",
|
|
20
|
-
"day": "thursday",
|
|
21
|
-
},
|
|
22
|
-
"groups": {
|
|
23
|
-
"python": {
|
|
24
|
-
"patterns": ["*"],
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _gh_actions_config() -> dict[str, Any]:
|
|
31
|
-
return {
|
|
32
|
-
"package-ecosystem": "github-actions",
|
|
33
|
-
"directory": "/",
|
|
34
|
-
"schedule": {
|
|
35
|
-
"interval": "weekly",
|
|
36
|
-
"day": "thursday",
|
|
37
|
-
},
|
|
38
|
-
"groups": {
|
|
39
|
-
"gh-actions": {
|
|
40
|
-
"patterns": ["*"],
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
STANDARDS = {
|
|
47
|
-
"version": 2,
|
|
48
|
-
"updates": [
|
|
49
|
-
_pip_config(),
|
|
50
|
-
_gh_actions_config(),
|
|
51
|
-
],
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@dataclass(frozen=True)
|
|
56
|
-
class DependabotYaml:
|
|
57
|
-
dry_run: bool = False
|
|
58
|
-
|
|
59
|
-
_result: ProgramResult = field(init=False, default=ProgramResult(name="DependaBot"))
|
|
60
|
-
|
|
61
|
-
NAME = "dependabot.yml"
|
|
62
|
-
|
|
63
|
-
def sync(self, target: Path) -> Executable:
|
|
64
|
-
dependabot = self.yaml_on(target)
|
|
65
|
-
dependabot.parent.mkdir(parents=True, exist_ok=True)
|
|
66
|
-
|
|
67
|
-
return self._sync(dependabot)
|
|
68
|
-
|
|
69
|
-
def _sync(self, dependabot: Path) -> Executable:
|
|
70
|
-
if self.dry_run:
|
|
71
|
-
dumped = json.dumps(STANDARDS, indent=2)
|
|
72
|
-
return self._result.success(message=f"Changes to apply:\n{dumped}")
|
|
73
|
-
|
|
74
|
-
with dependabot.open(mode="w", encoding="utf-8", newline="\n") as stream:
|
|
75
|
-
yaml.dump(
|
|
76
|
-
STANDARDS,
|
|
77
|
-
stream,
|
|
78
|
-
Dumper=IndentDumper,
|
|
79
|
-
sort_keys=False,
|
|
80
|
-
default_flow_style=False,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return self._result.success(message=f"{self.NAME} synchronized successfully.\n")
|
|
84
|
-
|
|
85
|
-
def yaml_on(self, path: Path) -> Path:
|
|
86
|
-
return path / ".github" / self.NAME
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class IndentDumper(yaml.Dumper):
|
|
90
|
-
"""Custom dumper to force indentation for list items."""
|
|
91
|
-
|
|
92
|
-
def increase_indent(self, flow: bool = False, indentless: bool = False) -> None:
|
|
93
|
-
_ = indentless
|
|
94
|
-
|
|
95
|
-
return super().increase_indent(flow, False)
|
|
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
|