gitlab-cicd-python-wrapper 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+ from gitlab_cicd_python_wrapper.artifacts import Artifacts
6
+ from gitlab_cicd_python_wrapper.cache import Cache
7
+ from gitlab_cicd_python_wrapper.common import WhenCondition
8
+ from gitlab_cicd_python_wrapper.environment import Environment
9
+ from gitlab_cicd_python_wrapper.image import Image, Service
10
+ from gitlab_cicd_python_wrapper.needs import Need, NeedsPipeline
11
+ from gitlab_cicd_python_wrapper.pages import Pages
12
+ from gitlab_cicd_python_wrapper.release import Release
13
+ from gitlab_cicd_python_wrapper.retry import Retry
14
+ from gitlab_cicd_python_wrapper.rules import Rule
15
+ from gitlab_cicd_python_wrapper.secrets import Secret
16
+ from gitlab_cicd_python_wrapper.trigger import Trigger
17
+ from gitlab_cicd_python_wrapper.variables import Variable
18
+
19
+
20
+ class AllowFailure(BaseModel):
21
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
22
+
23
+ exit_codes: list[int] | int
24
+
25
+
26
+ class DastConfiguration(BaseModel):
27
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
28
+
29
+ site_profile: str | None = None
30
+ scanner_profile: str | None = None
31
+
32
+
33
+ class Identity(BaseModel):
34
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
35
+
36
+ aud: str
37
+
38
+
39
+ class Inherit(BaseModel):
40
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
41
+
42
+ default: bool | list[str] | None = None
43
+ variables: bool | list[str] | None = None
44
+
45
+
46
+ class Parallel(BaseModel):
47
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
48
+
49
+ matrix: list[dict[str, list[str]]]
50
+
51
+
52
+ class RunConfig(BaseModel):
53
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
54
+
55
+ shell: str | None = None
56
+ timeout: int | None = None
57
+
58
+
59
+ class Job(BaseModel):
60
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
61
+
62
+ script: list[str] | None = None
63
+ before_script: list[str] | None = None
64
+ after_script: list[str] | None = None
65
+ stage: str | None = None
66
+ image: str | Image | None = None
67
+ services: list[str | Service] | None = None
68
+ variables: dict[str, str | Variable] | None = None
69
+ rules: list[Rule] | None = None
70
+ allow_failure: bool | AllowFailure | None = None
71
+ artifacts: Artifacts | None = None
72
+ cache: Cache | list[Cache] | None = None
73
+ needs: list[str | Need | NeedsPipeline] | None = None
74
+ tags: list[str] | None = None
75
+ when: WhenCondition | None = None
76
+ environment: str | Environment | None = None
77
+ extends: str | list[str] | None = None
78
+ dependencies: list[str] | None = None
79
+ coverage: str | None = None
80
+ retry: int | Retry | None = None
81
+ timeout: str | None = None
82
+ parallel: int | Parallel | None = None
83
+ trigger: str | Trigger | None = None
84
+ resource_group: str | None = None
85
+ interruptible: bool | None = None
86
+ start_in: str | None = None
87
+ release: Release | None = None
88
+ secrets: dict[str, Secret] | None = None
89
+ pages: Pages | None = None
90
+ inherit: Inherit | None = None
91
+ dast_configuration: DastConfiguration | None = None
92
+ identity: Identity | None = None
93
+ manual_confirmation: str | None = None
94
+ run: RunConfig | None = None
95
+ id_tokens: dict[str, dict[str, str]] | None = None
96
+ hooks: dict[str, list[str]] | None = None
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class Need(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
8
+
9
+ job: str
10
+ artifacts: bool | None = None
11
+ optional: bool | None = None
12
+
13
+
14
+ class NeedsPipeline(BaseModel):
15
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
16
+
17
+ pipeline: str
18
+ job: str
19
+ artifacts: bool | None = None
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class Pages(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
8
+
9
+ publish: str | None = None
10
+ expire_in: str | None = None
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, model_validator
7
+
8
+ from gitlab_cicd_python_wrapper.globals import Default, Workflow
9
+ from gitlab_cicd_python_wrapper.job import Job
10
+ from gitlab_cicd_python_wrapper.serialization import dump_yaml, load_yaml
11
+ from gitlab_cicd_python_wrapper.spec import ComponentSpec
12
+ from gitlab_cicd_python_wrapper.variables import Variable
13
+
14
+ GLOBAL_KEYWORDS = frozenset(
15
+ {
16
+ "stages",
17
+ "variables",
18
+ "default",
19
+ "workflow",
20
+ "include",
21
+ "spec",
22
+ }
23
+ )
24
+
25
+
26
+ class Pipeline(BaseModel):
27
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
28
+
29
+ stages: list[str] | None = None
30
+ variables: dict[str, str | Variable] | None = None
31
+ default: Default | None = None
32
+ workflow: Workflow | None = None
33
+ include: list[dict[str, Any] | str] | None = None
34
+ spec: ComponentSpec | None = None
35
+ jobs: dict[str, Job] = {}
36
+
37
+ _raw: Any = None
38
+
39
+ @model_validator(mode="before")
40
+ @classmethod
41
+ def separate_jobs_from_globals(cls, data: Any) -> Any:
42
+ if not isinstance(data, dict):
43
+ return data
44
+ if "jobs" in data:
45
+ return data
46
+ globals_ = {}
47
+ jobs = {}
48
+ for key, value in data.items():
49
+ if key in GLOBAL_KEYWORDS:
50
+ globals_[key] = value
51
+ else:
52
+ jobs[key] = value
53
+ globals_["jobs"] = jobs
54
+ return globals_
55
+
56
+ @classmethod
57
+ def from_yaml(cls, source: str | Path) -> Pipeline:
58
+ data, raw = load_yaml(source)
59
+ pipeline = cls.model_validate(data)
60
+ pipeline._raw = raw
61
+ return pipeline
62
+
63
+ def to_yaml(self, target: str | Path | None = None) -> str:
64
+ if self._raw is not None:
65
+ return dump_yaml(self._raw, target)
66
+ from ruamel.yaml.comments import CommentedMap
67
+
68
+ raw = CommentedMap()
69
+ if self.stages:
70
+ raw["stages"] = self.stages
71
+ if self.variables:
72
+ raw["variables"] = {
73
+ k: v if isinstance(v, str) else v.model_dump(exclude_none=True) for k, v in self.variables.items()
74
+ }
75
+ if self.default:
76
+ raw["default"] = self.default.model_dump(exclude_none=True, by_alias=True)
77
+ if self.workflow:
78
+ raw["workflow"] = self.workflow.model_dump(exclude_none=True, by_alias=True)
79
+ if self.include:
80
+ raw["include"] = self.include
81
+ for name, job in self.jobs.items():
82
+ raw[name] = job.model_dump(exclude_none=True, by_alias=True)
83
+ result = dump_yaml(raw)
84
+ if target is not None:
85
+ Path(target).write_text(result)
86
+ return result
87
+
88
+ @classmethod
89
+ def validate_file(cls, path: Path) -> list[str]:
90
+ try:
91
+ cls.from_yaml(path)
92
+ return []
93
+ except Exception as e:
94
+ return [str(e)]
95
+
96
+ @classmethod
97
+ def validate_file_from_string(cls, content: str) -> list[str]:
98
+ try:
99
+ cls.from_yaml(content)
100
+ return []
101
+ except Exception as e:
102
+ return [str(e)]
File without changes
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class Release(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
8
+
9
+ tag_name: str
10
+ description: str
11
+ name: str | None = None
12
+ ref: str | None = None
13
+ milestones: list[str] | None = None
14
+ released_at: str | None = None
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+ from gitlab_cicd_python_wrapper.common import RetryWhen
6
+
7
+
8
+ class Retry(BaseModel):
9
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
10
+
11
+ max: int = Field(ge=0, le=5)
12
+ when: list[RetryWhen] | None = None
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+ from gitlab_cicd_python_wrapper.common import WhenCondition
6
+
7
+
8
+ class Rule(BaseModel):
9
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
10
+
11
+ if_: str | None = Field(None, alias="if")
12
+ changes: list[str] | None = None
13
+ exists: list[str] | None = None
14
+ when: WhenCondition | None = None
15
+ allow_failure: bool | None = None
16
+ variables: dict[str, str] | None = None
17
+ start_in: str | None = None
18
+
19
+
20
+ class WorkflowRule(BaseModel):
21
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
22
+
23
+ if_: str | None = Field(None, alias="if")
24
+ changes: list[str] | None = None
25
+ exists: list[str] | None = None
26
+ when: WhenCondition | None = None
27
+ variables: dict[str, str] | None = None
28
+ auto_cancel: dict | None = None
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class VaultEngine(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
8
+
9
+ name: str
10
+ path: str
11
+
12
+
13
+ class VaultConfig(BaseModel):
14
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
15
+
16
+ engine: VaultEngine
17
+ path: str
18
+ field: str
19
+
20
+
21
+ class Secret(BaseModel):
22
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
23
+
24
+ vault: VaultConfig
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from io import StringIO
4
+ from pathlib import Path
5
+
6
+ from ruamel.yaml import YAML
7
+ from ruamel.yaml.comments import CommentedMap
8
+
9
+
10
+ def _make_yaml() -> YAML:
11
+ yaml = YAML(typ="rt")
12
+ yaml.preserve_quotes = True
13
+ yaml.width = 4096
14
+ yaml.indent(mapping=2, sequence=4, offset=2)
15
+ return yaml
16
+
17
+
18
+ def load_yaml(source: str | Path) -> tuple[dict, CommentedMap]:
19
+ yaml = _make_yaml()
20
+ if isinstance(source, Path):
21
+ with open(source) as fh:
22
+ raw = yaml.load(fh)
23
+ else:
24
+ raw = yaml.load(source)
25
+ plain = dict(raw)
26
+ return plain, raw
27
+
28
+
29
+ def dump_yaml(raw: CommentedMap, target: str | Path | None = None) -> str:
30
+ yaml = _make_yaml()
31
+ buf = StringIO()
32
+ yaml.dump(raw, buf)
33
+ text = buf.getvalue()
34
+ if target is not None:
35
+ path = Path(target)
36
+ path.write_text(text)
37
+ return text
38
+
39
+
40
+ def yaml_round_trip(source: str | Path) -> str:
41
+ _, raw = load_yaml(source)
42
+ return dump_yaml(raw)
43
+
44
+
45
+ def load_yaml_multi(source: str | Path) -> tuple[list[dict], list[CommentedMap]]:
46
+ yaml = _make_yaml()
47
+ if isinstance(source, Path):
48
+ with open(source) as fh:
49
+ docs = list(yaml.load_all(fh))
50
+ else:
51
+ docs = list(yaml.load_all(source))
52
+ plains = [dict(d) for d in docs]
53
+ return plains, docs
54
+
55
+
56
+ def dump_yaml_multi(raws: list[CommentedMap], target: str | Path | None = None) -> str:
57
+ yaml = _make_yaml()
58
+ buf = StringIO()
59
+ yaml.dump_all(raws, buf)
60
+ text = buf.getvalue()
61
+ if target is not None:
62
+ path = Path(target)
63
+ path.write_text(text)
64
+ return text
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
6
+
7
+ from gitlab_cicd_python_wrapper.common import InputType
8
+
9
+
10
+ class InputRule(BaseModel):
11
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
12
+
13
+ if_: str = Field(alias="if")
14
+ options: list[str | int | float | bool] | None = None
15
+ default: str | int | float | bool | list | None = None
16
+
17
+
18
+ class ComponentInput(BaseModel):
19
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
20
+
21
+ type: InputType = InputType.string
22
+ default: str | int | float | bool | list | None = None
23
+ description: str | None = None
24
+ options: list[str | int | float | bool] | None = None
25
+ regex: str | None = None
26
+ rules: list[InputRule] | None = None
27
+
28
+ @model_validator(mode="after")
29
+ def validate_default_in_options(self) -> ComponentInput:
30
+ if self.options is not None and self.default is not None:
31
+ if self.default not in self.options:
32
+ raise ValueError(f"default {self.default!r} must be one of options {self.options!r}")
33
+ return self
34
+
35
+ @model_validator(mode="after")
36
+ def validate_regex_only_for_string(self) -> ComponentInput:
37
+ if self.regex is not None and self.type != InputType.string:
38
+ raise ValueError("regex is only valid for type 'string'")
39
+ return self
40
+
41
+
42
+ class ComponentSpec(BaseModel):
43
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
44
+
45
+ description: str | None = None
46
+ inputs: dict[str, ComponentInput] | None = None
47
+ include: list[dict[str, Any]] | None = None
48
+ component: list[str] | None = None
49
+
50
+ @field_validator("inputs")
51
+ @classmethod
52
+ def inputs_not_empty(cls, v: dict[str, ComponentInput] | None) -> dict[str, ComponentInput] | None:
53
+ if v is not None and len(v) == 0:
54
+ raise ValueError("spec:inputs cannot be empty")
55
+ return v
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, ConfigDict
6
+
7
+
8
+ class TriggerForward(BaseModel):
9
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
10
+
11
+ yaml_variables: bool | None = None
12
+ pipeline_variables: bool | None = None
13
+ dotenv_variables: bool | None = None
14
+
15
+
16
+ class Trigger(BaseModel):
17
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
18
+
19
+ project: str | None = None
20
+ branch: str | None = None
21
+ strategy: str | None = None
22
+ include: list[dict[str, Any]] | None = None
23
+ forward: TriggerForward | None = None
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class Variable(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
8
+
9
+ value: str
10
+ description: str | None = None
11
+ options: list[str] | None = None
12
+ expand: bool | None = None