pg-maker 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.
- pg_maker/__init__.py +3 -0
- pg_maker/__main__.py +3 -0
- pg_maker/api.py +594 -0
- pg_maker/artifacts/__init__.py +4 -0
- pg_maker/artifacts/catalog.py +67 -0
- pg_maker/artifacts/manifest.py +301 -0
- pg_maker/assets.py +93 -0
- pg_maker/builders/__init__.py +4 -0
- pg_maker/builders/autotools.py +430 -0
- pg_maker/builders/components.py +359 -0
- pg_maker/bundled/PROJECT.md +57 -0
- pg_maker/bundled/build.yaml +29 -0
- pg_maker/bundled/components/pg-stat-kcache.yaml +30 -0
- pg_maker/bundled/components/pg-stat-statements.yaml +24 -0
- pg_maker/bundled/components/pg-wait-sampling.yaml +30 -0
- pg_maker/bundled/config/defaults.yaml +80 -0
- pg_maker/bundled/config/documentation.yaml +24 -0
- pg_maker/bundled/config/features/my-feature.yaml +17 -0
- pg_maker/bundled/config/vendor.yaml +13 -0
- pg_maker/bundled/documentation/README.md +20 -0
- pg_maker/bundled/documentation/pg-stat-kcache.sgml +99 -0
- pg_maker/bundled/documentation/pg-stat-kcache.yaml +15 -0
- pg_maker/bundled/documentation/pg-wait-sampling.sgml +122 -0
- pg_maker/bundled/documentation/pg-wait-sampling.yaml +15 -0
- pg_maker/bundled/patches/unix-socket-directory.patch +5 -0
- pg_maker/changes/__init__.py +1 -0
- pg_maker/changes/apply.py +298 -0
- pg_maker/changes/loader.py +135 -0
- pg_maker/changes/workspace.py +221 -0
- pg_maker/cli.py +546 -0
- pg_maker/components/__init__.py +4 -0
- pg_maker/config.py +514 -0
- pg_maker/development/__init__.py +1 -0
- pg_maker/development/cli.py +104 -0
- pg_maker/development/git.py +362 -0
- pg_maker/development/session.py +735 -0
- pg_maker/documentation/__init__.py +4 -0
- pg_maker/documentation/builder.py +467 -0
- pg_maker/documentation/cli.py +24 -0
- pg_maker/documentation/config.py +127 -0
- pg_maker/documentation/install.py +79 -0
- pg_maker/documentation/site.py +166 -0
- pg_maker/documentation/sphinx_ext.py +150 -0
- pg_maker/documentation/theme/_static/pg-maker.css +26 -0
- pg_maker/documentation/theme/_static/pg-maker.js +35 -0
- pg_maker/documentation/theme/_templates/footer_en.html +5 -0
- pg_maker/documentation/theme/_templates/footer_metadata.html +13 -0
- pg_maker/documentation/theme/_templates/footer_ru.html +5 -0
- pg_maker/documentation/translation.py +341 -0
- pg_maker/documentation/validation.py +132 -0
- pg_maker/documentation/worker.py +62 -0
- pg_maker/documentation/writer.py +324 -0
- pg_maker/documentation/xml.py +210 -0
- pg_maker/documents.py +118 -0
- pg_maker/errors.py +50 -0
- pg_maker/execution/__init__.py +1 -0
- pg_maker/execution/build.py +502 -0
- pg_maker/execution/cache.py +287 -0
- pg_maker/execution/cancellation.py +60 -0
- pg_maker/execution/checkpoints.py +218 -0
- pg_maker/execution/cleanup.py +122 -0
- pg_maker/execution/component_cache.py +164 -0
- pg_maker/execution/development.py +109 -0
- pg_maker/execution/engine.py +171 -0
- pg_maker/execution/events.py +65 -0
- pg_maker/execution/incremental.py +178 -0
- pg_maker/execution/preparation.py +130 -0
- pg_maker/execution/process.py +155 -0
- pg_maker/execution/resources.py +41 -0
- pg_maker/execution/state.py +82 -0
- pg_maker/execution/worker.py +45 -0
- pg_maker/identity.py +116 -0
- pg_maker/integrations/__init__.py +1 -0
- pg_maker/integrations/machine.py +163 -0
- pg_maker/integrations/runtime.py +387 -0
- pg_maker/models.py +245 -0
- pg_maker/packaging/__init__.py +4 -0
- pg_maker/packaging/deb.py +337 -0
- pg_maker/packaging/service.py +165 -0
- pg_maker/packaging/spec.py +64 -0
- pg_maker/packaging/templates/deb-control.in +8 -0
- pg_maker/packaging/tree.py +239 -0
- pg_maker/planning/__init__.py +1 -0
- pg_maker/planning/documents.py +65 -0
- pg_maker/planning/environments.py +318 -0
- pg_maker/planning/graph.py +23 -0
- pg_maker/planning/plan.py +418 -0
- pg_maker/planning/resolve.py +117 -0
- pg_maker/planning/reuse.py +80 -0
- pg_maker/planning/runtime_repository.py +90 -0
- pg_maker/postgresql.py +59 -0
- pg_maker/progress.py +64 -0
- pg_maker/publishing/__init__.py +4 -0
- pg_maker/py.typed +0 -0
- pg_maker/recipes/README.md +13 -0
- pg_maker/recipes/platforms/debian-13.yaml +125 -0
- pg_maker/recipes/postgresql.yaml +114 -0
- pg_maker/runners/__init__.py +1 -0
- pg_maker/runners/base.py +29 -0
- pg_maker/runners/docker.py +312 -0
- pg_maker/schemas/build.schema.json +5 -0
- pg_maker/schemas/change-set.schema.json +5 -0
- pg_maker/schemas/component-envelope.schema.json +69 -0
- pg_maker/schemas/component.schema.json +5 -0
- pg_maker/schemas/defaults.schema.json +5 -0
- pg_maker/schemas/doc-rules.schema.json +5 -0
- pg_maker/schemas/documentation.schema.json +5 -0
- pg_maker/schemas/environment.schema.json +228 -0
- pg_maker/schemas/feature.schema.json +5 -0
- pg_maker/schemas/insertions.schema.json +5 -0
- pg_maker/schemas/lock.schema.json +3963 -0
- pg_maker/schemas/operation-input.schema.json +306 -0
- pg_maker/schemas/operation-state.schema.json +127 -0
- pg_maker/schemas/plan.schema.json +284 -0
- pg_maker/schemas/planning-common.schema.json +8192 -0
- pg_maker/schemas/platform.schema.json +5 -0
- pg_maker/schemas/postgresql-catalog.schema.json +5 -0
- pg_maker/schemas/run-event.schema.json +72 -0
- pg_maker/schemas/translations.schema.json +5 -0
- pg_maker/schemas/vendor.schema.json +5 -0
- pg_maker/security.py +73 -0
- pg_maker/sources/__init__.py +1 -0
- pg_maker/sources/resolver.py +238 -0
- pg_maker/sources/store.py +279 -0
- pg_maker/storage.py +127 -0
- pg_maker/validation/__init__.py +4 -0
- pg_maker/validation/installed.py +667 -0
- pg_maker/validation/packages.py +309 -0
- pg_maker/validation/systemctl.py +185 -0
- pg_maker/yamlio.py +54 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/CHANGELOG.md +22 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/CONTRIBUTING.md +63 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/LICENSE +21 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/README.md +163 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/architecture.md +55 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/development-storage.md +72 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/development.md +86 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/README.md +32 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/api.md +75 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/build.md +112 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/changes.md +58 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/cleanup.md +17 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/components.md +39 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/configuration.md +83 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/development.md +88 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/documentation.md +241 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/documents.md +42 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/environments.md +40 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/execution.md +49 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/machine.md +31 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/observability.md +28 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/packaging.md +107 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/planning.md +63 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/postgresql.md +79 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/publication.md +20 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/recovery.md +83 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/runtime.md +31 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/sources.md +48 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/state.md +49 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/interfaces/storage.md +24 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/releasing.md +98 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/reuse.md +24 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/roadmap.md +54 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/default-components.md +56 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/development-release.md +49 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/documentation-theme.md +45 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/integration.md +49 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/postgresql-majors.md +107 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/review-fixes.md +68 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/stage4.md +65 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/stage56.md +66 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/docs/validation/stage7.md +72 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/tests/fixtures/guc/README.md +11 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/tests/fixtures/stage4/README.md +31 -0
- pg_maker-0.1.0.data/data/share/doc/pg-maker/tests/fixtures/stage7/README.md +16 -0
- pg_maker-0.1.0.dist-info/METADATA +200 -0
- pg_maker-0.1.0.dist-info/RECORD +181 -0
- pg_maker-0.1.0.dist-info/WHEEL +5 -0
- pg_maker-0.1.0.dist-info/entry_points.txt +4 -0
- pg_maker-0.1.0.dist-info/licenses/LICENSE +21 -0
- pg_maker-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""Explicit Make/PGXS components and fresh-cluster acceptance checks."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
import sys
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from pg_maker.builders.autotools import regression_evidence
|
|
10
|
+
from pg_maker.documents import relative_output
|
|
11
|
+
from pg_maker.errors import CommandError, MakerError, PreconditionError, ValidationError
|
|
12
|
+
from pg_maker.storage import read_json, write_json
|
|
13
|
+
from pg_maker.yamlio import load_yaml
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def integrate(branch, components):
|
|
17
|
+
for component in components:
|
|
18
|
+
spec, identifier = component["spec"], component["metadata"]["id"]
|
|
19
|
+
if spec["type"] != "source-tree":
|
|
20
|
+
continue
|
|
21
|
+
relative = spec.get("source_directory")
|
|
22
|
+
if not relative or not relative_output(relative):
|
|
23
|
+
raise ValidationError("source-tree component requires a relative integration directory")
|
|
24
|
+
destination = branch.source / relative
|
|
25
|
+
if destination.exists() or destination.is_symlink():
|
|
26
|
+
raise PreconditionError("source-tree component must not overwrite PostgreSQL files")
|
|
27
|
+
if any(p.is_symlink() for p in destination.parents):
|
|
28
|
+
raise ValidationError("component destination traverses a symlink")
|
|
29
|
+
shutil.copytree(
|
|
30
|
+
branch.inputs.tree(spec["source"]["source_hash"]), destination, symlinks=True
|
|
31
|
+
)
|
|
32
|
+
branch.report.setdefault("integrated_components", []).append(identifier)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@contextmanager
|
|
36
|
+
def cluster(branch, name, *, database="postgres", preload=()):
|
|
37
|
+
if not re.fullmatch(r"[A-Za-z0-9_.-]+", name):
|
|
38
|
+
raise ValidationError("invalid cluster identifier")
|
|
39
|
+
root = branch.root / "clusters" / name
|
|
40
|
+
root.mkdir(parents=True)
|
|
41
|
+
container = branch.container_root + "/clusters/" + name
|
|
42
|
+
data = container + "/data"
|
|
43
|
+
sockets = container + "/socket"
|
|
44
|
+
(root / "socket").mkdir()
|
|
45
|
+
prefix = branch.prefix
|
|
46
|
+
env = {
|
|
47
|
+
"PGHOST": sockets,
|
|
48
|
+
"PGPORT": "55432",
|
|
49
|
+
"PGUSER": "builder",
|
|
50
|
+
"PGDATABASE": database,
|
|
51
|
+
"PATH": prefix + "/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
52
|
+
"LC_ALL": "C.UTF-8",
|
|
53
|
+
}
|
|
54
|
+
branch.command(
|
|
55
|
+
[prefix + "/bin/initdb", "-D", data, "-A", "trust", "--no-locale", "-U", "builder"]
|
|
56
|
+
)
|
|
57
|
+
settings = "listen_addresses = ''\nport = 55432\nunix_socket_directories = '" + sockets + "'\n"
|
|
58
|
+
if preload:
|
|
59
|
+
if any(not re.fullmatch(r"[A-Za-z0-9_/-]+", item) for item in preload):
|
|
60
|
+
raise ValidationError("invalid preload library")
|
|
61
|
+
settings += "shared_preload_libraries = '" + ",".join(preload) + "'\n"
|
|
62
|
+
with (root / "data/postgresql.conf").open("a") as stream:
|
|
63
|
+
stream.write(settings)
|
|
64
|
+
# pg_ctl readiness is bounded; all PostgreSQL file logs stay under PGDATA.
|
|
65
|
+
branch.command(
|
|
66
|
+
[prefix + "/bin/pg_ctl", "-D", data, "-l", data + "/server.log", "-w", "-t", "60", "start"]
|
|
67
|
+
)
|
|
68
|
+
try:
|
|
69
|
+
if database != "postgres":
|
|
70
|
+
branch.command(
|
|
71
|
+
[prefix + "/bin/createdb", database], env={**env, "PGDATABASE": "postgres"}
|
|
72
|
+
)
|
|
73
|
+
yield env, root / "data/server.log"
|
|
74
|
+
finally:
|
|
75
|
+
# Cancellation stops the entire owned container; no unrelated host process is touched.
|
|
76
|
+
if not branch.context.token.requested():
|
|
77
|
+
original_error = sys.exc_info()[0] is not None
|
|
78
|
+
try:
|
|
79
|
+
branch.command(
|
|
80
|
+
[
|
|
81
|
+
prefix + "/bin/pg_ctl",
|
|
82
|
+
"-D",
|
|
83
|
+
data,
|
|
84
|
+
"-w",
|
|
85
|
+
"-t",
|
|
86
|
+
"60",
|
|
87
|
+
"-m",
|
|
88
|
+
"immediate",
|
|
89
|
+
"stop",
|
|
90
|
+
]
|
|
91
|
+
)
|
|
92
|
+
except MakerError:
|
|
93
|
+
# A timed-out command may already have stopped the container. Preserve its
|
|
94
|
+
# failure; the outer engine still releases/verifies the entire owned resource.
|
|
95
|
+
if not original_error:
|
|
96
|
+
raise
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_components(branch, components, *, run_tests=True):
|
|
100
|
+
reports = []
|
|
101
|
+
for component in components:
|
|
102
|
+
spec, identifier = component["spec"], component["metadata"]["id"]
|
|
103
|
+
kind = spec["type"]
|
|
104
|
+
if kind in {"contrib", "source-tree"}:
|
|
105
|
+
relative = spec.get("source_directory", "contrib/" + identifier)
|
|
106
|
+
if not (branch.source / relative / "Makefile").is_file():
|
|
107
|
+
raise PreconditionError("component source directory is missing")
|
|
108
|
+
cwd = branch.container_root + "/obj/" + relative
|
|
109
|
+
(branch.obj / relative).mkdir(parents=True, exist_ok=True)
|
|
110
|
+
component_source = branch.source / relative
|
|
111
|
+
container_source = branch.container_root + "/source/" + relative
|
|
112
|
+
else:
|
|
113
|
+
path = branch.root / "components" / identifier
|
|
114
|
+
if not path.exists():
|
|
115
|
+
shutil.copytree(
|
|
116
|
+
branch.inputs.tree(spec["source"]["source_hash"]), path, symlinks=True
|
|
117
|
+
)
|
|
118
|
+
cwd = branch.container_root + "/components/" + identifier
|
|
119
|
+
if spec.get("source_directory"):
|
|
120
|
+
cwd += "/" + spec["source_directory"]
|
|
121
|
+
path /= spec["source_directory"]
|
|
122
|
+
component_source, container_source = path, cwd
|
|
123
|
+
if spec.get("configure_args") or (
|
|
124
|
+
kind in {"utility", "library"} and (path / "configure").is_file()
|
|
125
|
+
):
|
|
126
|
+
branch.command(
|
|
127
|
+
["./configure", "--prefix=" + branch.prefix, *spec.get("configure_args", [])],
|
|
128
|
+
cwd=cwd,
|
|
129
|
+
)
|
|
130
|
+
make = ["make", "--output-sync=target", "-j", str(branch.variant["build"]["jobs"])]
|
|
131
|
+
if kind in {"contrib", "source-tree"}:
|
|
132
|
+
make += ["-f", container_source + "/Makefile"]
|
|
133
|
+
if kind == "pgxs":
|
|
134
|
+
make += ["USE_PGXS=1", "PG_CONFIG=" + branch.prefix + "/bin/pg_config"]
|
|
135
|
+
elif kind in {"utility", "library"}:
|
|
136
|
+
make += ["prefix=" + branch.prefix, "PG_CONFIG=" + branch.prefix + "/bin/pg_config"]
|
|
137
|
+
for targets in (spec["make_targets"], spec["install_targets"]):
|
|
138
|
+
if targets:
|
|
139
|
+
branch.command([*make, *targets], cwd=cwd)
|
|
140
|
+
if not run_tests:
|
|
141
|
+
reports.append(
|
|
142
|
+
{"id": identifier, "built": True, "native_targets": [], "tests": "skipped"}
|
|
143
|
+
)
|
|
144
|
+
continue
|
|
145
|
+
expected = None
|
|
146
|
+
if kind in {"pgxs", "contrib", "source-tree"} and spec["test_targets"]:
|
|
147
|
+
result = branch.command(
|
|
148
|
+
[
|
|
149
|
+
*make,
|
|
150
|
+
*(["-f", "Makefile"] if kind == "pgxs" else []),
|
|
151
|
+
"-f",
|
|
152
|
+
"/work/source/inventory.mk",
|
|
153
|
+
"maker-inventory",
|
|
154
|
+
],
|
|
155
|
+
cwd=cwd,
|
|
156
|
+
)
|
|
157
|
+
values = dict(
|
|
158
|
+
re.findall(r"^PG_MAKER_([A-Z_]+)=(.*)$", Path(result.stdout_log).read_text(), re.M)
|
|
159
|
+
)
|
|
160
|
+
expected = {
|
|
161
|
+
"regression": values["REGRESS"].split(),
|
|
162
|
+
"isolation": values["ISOLATION"].split(),
|
|
163
|
+
"tap": [
|
|
164
|
+
p.relative_to(component_source).as_posix()
|
|
165
|
+
for p in sorted((component_source / "t").glob("*.pl"))
|
|
166
|
+
]
|
|
167
|
+
if values["TAP"] == "1"
|
|
168
|
+
else [],
|
|
169
|
+
}
|
|
170
|
+
if not any(expected.values()):
|
|
171
|
+
raise PreconditionError("component has no declared native test inventory")
|
|
172
|
+
write_json(branch.output / "components" / identifier / "expected.json", expected)
|
|
173
|
+
with cluster(branch, "component-" + identifier, preload=spec.get("preload", [])) as (
|
|
174
|
+
env,
|
|
175
|
+
_,
|
|
176
|
+
):
|
|
177
|
+
if spec.get("shared_library"):
|
|
178
|
+
library = spec["shared_library"].replace("'", "''")
|
|
179
|
+
branch.command(
|
|
180
|
+
[
|
|
181
|
+
branch.prefix + "/bin/psql",
|
|
182
|
+
"-X",
|
|
183
|
+
"-v",
|
|
184
|
+
"ON_ERROR_STOP=1",
|
|
185
|
+
"-c",
|
|
186
|
+
"LOAD '" + library + "'",
|
|
187
|
+
],
|
|
188
|
+
env=env,
|
|
189
|
+
)
|
|
190
|
+
if spec.get("sql_extension"):
|
|
191
|
+
extension = spec["sql_extension"].replace('"', '""')
|
|
192
|
+
branch.command(
|
|
193
|
+
[
|
|
194
|
+
branch.prefix + "/bin/psql",
|
|
195
|
+
"-X",
|
|
196
|
+
"-v",
|
|
197
|
+
"ON_ERROR_STOP=1",
|
|
198
|
+
"-c",
|
|
199
|
+
'CREATE EXTENSION "' + extension + '" CASCADE',
|
|
200
|
+
],
|
|
201
|
+
env=env,
|
|
202
|
+
)
|
|
203
|
+
for target in spec["test_targets"]:
|
|
204
|
+
tap = branch.output / "components" / identifier / target / "tap"
|
|
205
|
+
tap.mkdir(parents=True)
|
|
206
|
+
test_env = {
|
|
207
|
+
**env,
|
|
208
|
+
"PERL5LIB": "/work/source",
|
|
209
|
+
"PG_MAKER_SOURCE": container_source,
|
|
210
|
+
"PG_MAKER_TAP": "/work/output/"
|
|
211
|
+
+ branch.id
|
|
212
|
+
+ "/components/"
|
|
213
|
+
+ identifier
|
|
214
|
+
+ "/"
|
|
215
|
+
+ target
|
|
216
|
+
+ "/tap",
|
|
217
|
+
}
|
|
218
|
+
result = branch.command(
|
|
219
|
+
[*make, target, "PROVE_FLAGS=--formatter=PgMaker::Formatter"],
|
|
220
|
+
cwd=cwd,
|
|
221
|
+
env=test_env,
|
|
222
|
+
)
|
|
223
|
+
if expected is not None:
|
|
224
|
+
verify_component_tests(expected, Path(result.stdout_log).read_text(), tap)
|
|
225
|
+
reports.append(
|
|
226
|
+
{
|
|
227
|
+
"id": identifier,
|
|
228
|
+
"built": True,
|
|
229
|
+
"native_targets": spec["test_targets"],
|
|
230
|
+
"tests": "passed",
|
|
231
|
+
"passed": True,
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
branch.report.setdefault("components", []).extend(reports)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def verify_component_tests(expected, stdout, tap):
|
|
238
|
+
evidence = regression_evidence(stdout)
|
|
239
|
+
for kind in ("regression", "isolation"):
|
|
240
|
+
values = [v for (k, _), v in evidence.items() if k == kind]
|
|
241
|
+
actual = [name for v in values for name in v["tests"]]
|
|
242
|
+
if sorted(expected[kind]) != sorted(actual) or any(
|
|
243
|
+
v["failed"] or v["plans"] != [len(v["tests"])] for v in values
|
|
244
|
+
):
|
|
245
|
+
raise CommandError("component regression inventory mismatch", details={"kind": kind})
|
|
246
|
+
values = [read_json(p) for p in tap.glob("*.json")]
|
|
247
|
+
if sorted(v["path"] for v in values) != sorted(expected["tap"]) or any(
|
|
248
|
+
v["tests"] <= len(v["skipped"])
|
|
249
|
+
or v["tests"] != v["planned"]
|
|
250
|
+
or v["failed"]
|
|
251
|
+
or v["errors"]
|
|
252
|
+
or v["skip_all"]
|
|
253
|
+
or v["exit"]
|
|
254
|
+
or v["wait"]
|
|
255
|
+
for v in values
|
|
256
|
+
):
|
|
257
|
+
raise CommandError("component TAP inventory mismatch")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def acceptance(branch, changes):
|
|
261
|
+
reports, seen = [], set()
|
|
262
|
+
for change in changes:
|
|
263
|
+
for test in change["spec"]["acceptance"]:
|
|
264
|
+
if branch.branch not in test["expectations"]:
|
|
265
|
+
continue
|
|
266
|
+
if test["id"] in seen:
|
|
267
|
+
continue
|
|
268
|
+
seen.add(test["id"])
|
|
269
|
+
blob = branch.inputs.blob(test["script"]["sha256"])
|
|
270
|
+
script = branch.helper / ("acceptance-" + test["id"])
|
|
271
|
+
shutil.copyfile(blob, script)
|
|
272
|
+
script_path = "/work/source/" + script.name
|
|
273
|
+
with cluster(
|
|
274
|
+
branch, "acceptance-" + test["id"], database=test["setup"]["database"]
|
|
275
|
+
) as (env, log):
|
|
276
|
+
if test["kind"] == "command":
|
|
277
|
+
document = load_yaml(blob)
|
|
278
|
+
if (
|
|
279
|
+
not isinstance(document, dict)
|
|
280
|
+
or set(document) != {"argv"}
|
|
281
|
+
or not isinstance(document["argv"], list)
|
|
282
|
+
or not document["argv"]
|
|
283
|
+
or not all(isinstance(v, str) and "\x00" not in v for v in document["argv"])
|
|
284
|
+
):
|
|
285
|
+
raise ValidationError(
|
|
286
|
+
"command acceptance script must be YAML {argv: [program, arguments...]}"
|
|
287
|
+
)
|
|
288
|
+
argv = [arg.replace("{prefix}", branch.prefix) for arg in document["argv"]]
|
|
289
|
+
else:
|
|
290
|
+
argv = [
|
|
291
|
+
branch.prefix + "/bin/psql",
|
|
292
|
+
"-X",
|
|
293
|
+
"-A",
|
|
294
|
+
"-t",
|
|
295
|
+
"-v",
|
|
296
|
+
"ON_ERROR_STOP=1",
|
|
297
|
+
"-v",
|
|
298
|
+
"VERBOSITY=verbose",
|
|
299
|
+
"-f",
|
|
300
|
+
script_path,
|
|
301
|
+
]
|
|
302
|
+
sqlstate_assertion = test["kind"] != "command" and any(
|
|
303
|
+
a["source"] == "sqlstate" for a in test["assertions"]
|
|
304
|
+
)
|
|
305
|
+
result = branch.command(
|
|
306
|
+
argv,
|
|
307
|
+
env=env,
|
|
308
|
+
timeout=test["timeout_seconds"],
|
|
309
|
+
accepted_exit_codes=(0, 3) if sqlstate_assertion else (0,),
|
|
310
|
+
)
|
|
311
|
+
stdout, stderr = (
|
|
312
|
+
Path(result.stdout_log).read_text(),
|
|
313
|
+
Path(result.stderr_log).read_text(),
|
|
314
|
+
)
|
|
315
|
+
outputs = {
|
|
316
|
+
"stdout": stdout,
|
|
317
|
+
"stderr": stderr,
|
|
318
|
+
"sql": stdout,
|
|
319
|
+
"server-log": log.read_text(),
|
|
320
|
+
"sqlstate": sqlstate(result.returncode, stderr),
|
|
321
|
+
}
|
|
322
|
+
failures = []
|
|
323
|
+
for assertion in test["assertions"]:
|
|
324
|
+
output = outputs[assertion["source"]]
|
|
325
|
+
if "contains" in assertion:
|
|
326
|
+
passed = assertion["contains"] in output
|
|
327
|
+
else:
|
|
328
|
+
expected = assertion["equals"]
|
|
329
|
+
if isinstance(expected, bool):
|
|
330
|
+
expected = "t" if expected else "f"
|
|
331
|
+
passed = output.strip() == str(expected)
|
|
332
|
+
if not passed:
|
|
333
|
+
failures.append(assertion["id"])
|
|
334
|
+
expectation = test["expectations"][branch.branch]
|
|
335
|
+
passed = (
|
|
336
|
+
not failures
|
|
337
|
+
if expectation["outcome"] == "pass"
|
|
338
|
+
else failures == [expectation.get("assertion_id")]
|
|
339
|
+
)
|
|
340
|
+
report = {
|
|
341
|
+
"id": test["id"],
|
|
342
|
+
"expectation": expectation,
|
|
343
|
+
"failed_assertions": failures,
|
|
344
|
+
"passed": passed,
|
|
345
|
+
}
|
|
346
|
+
reports.append(report)
|
|
347
|
+
write_json(branch.output / "acceptance.json", reports)
|
|
348
|
+
if not passed:
|
|
349
|
+
raise CommandError("acceptance expectation mismatch", details=report)
|
|
350
|
+
branch.report["acceptance"] = reports
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def sqlstate(returncode, stderr):
|
|
354
|
+
if returncode == 0:
|
|
355
|
+
return "00000"
|
|
356
|
+
match = re.search(r"ERROR:\s+([0-9A-Z]{5}):", stderr)
|
|
357
|
+
if returncode != 3 or not match:
|
|
358
|
+
raise CommandError("SQL failure has no valid SQLSTATE evidence")
|
|
359
|
+
return match[1]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# pg_maker project
|
|
2
|
+
|
|
3
|
+
The initial target is PostgreSQL 18.1 / Debian 13 / x86_64 with feature my-feature.
|
|
4
|
+
Edit vendor Test, paths, build modes and resources in config/. Defaults enable the
|
|
5
|
+
common socket patch. Configuration and file logs live in PGDATA; headers, PGXS and
|
|
6
|
+
ecpg are not shipped.
|
|
7
|
+
|
|
8
|
+
The feature selects pg_stat_statements, pg_stat_kcache 2.3.2 and pg_wait_sampling 1.1.11
|
|
9
|
+
(SQL version 1.1). Editable component recipes pin external sources by commit SHA;
|
|
10
|
+
pg_stat_statements uses PostgreSQL contrib. Set components: [] for no extra extensions.
|
|
11
|
+
Do not remove pg_stat_statements while pg_stat_kcache requires it.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pg-maker validate build.yaml
|
|
15
|
+
pg-maker config show build.yaml --resolved
|
|
16
|
+
pg-maker package names build.yaml
|
|
17
|
+
pg-maker env prepare build.yaml --out environments.json
|
|
18
|
+
pg-maker resolve build.yaml --environments environments.json --out lock.json --input-store inputs
|
|
19
|
+
pg-maker plan lock.json --out plan.json --goal build
|
|
20
|
+
pg-maker build lock.json --plan plan.json --detach
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Environment preparation installs dependencies into separate Docker images. Resolve
|
|
24
|
+
freezes sources/changes; offline resolve reuses stored inputs without networking.
|
|
25
|
+
Plan verifies hashes and describes work. Database/component tests, acceptance and
|
|
26
|
+
baseline are disabled by default for fast development; results remain unverified.
|
|
27
|
+
Use build --run-db-tests for full testing, or create a matching saved plan first.
|
|
28
|
+
The default is config/defaults.yaml spec.validation.run_db_tests. Utility tests are
|
|
29
|
+
unaffected. Build --goal package produces three DEBs and manifests; verified requests
|
|
30
|
+
long source/installed suites. The systemctl emulator does not qualify native systemd.
|
|
31
|
+
The build user must match the non-root host workspace owner.
|
|
32
|
+
|
|
33
|
+
Enable documentation in config/documentation.yaml before environment preparation and
|
|
34
|
+
resolve. Docs build produces selected HTML/man/PDF; prepare-translations exports bundles
|
|
35
|
+
for an external translator. Register completed bundles under spec.translations and
|
|
36
|
+
resolve again. Rendering is offline. With docs, a fourth docs package is added and
|
|
37
|
+
server only Suggests it. Selected external-component guides join navigation/search/PDF;
|
|
38
|
+
Russian builds require corresponding translation bundles.
|
|
39
|
+
|
|
40
|
+
Package installation does not activate extensions in existing clusters. Configure
|
|
41
|
+
shared_preload_libraries in order pg_stat_statements,pg_stat_kcache,pg_wait_sampling,
|
|
42
|
+
restart and create extensions in the needed databases, following their manuals.
|
|
43
|
+
|
|
44
|
+
Long operations support --detach and --request-id. Status/events/wait/cancel accept
|
|
45
|
+
operations/OPERATION_ID. Do not edit active input/state/journal files. Logs live under
|
|
46
|
+
attempts/N/logs, progress under attempts/N/progress.log and worker output in worker.log.
|
|
47
|
+
Repeated init retains identical files and reports conflicts before any remaining writes.
|
|
48
|
+
|
|
49
|
+
Resume build with the original plan hash; preparation requires its input hash. Changed
|
|
50
|
+
code belongs in a new resolve/build or a dev session. Dev open --from-run creates an
|
|
51
|
+
owned Git workspace for intermediate commits, patch replay and selected component builds.
|
|
52
|
+
|
|
53
|
+
Export preserves packages/reports/evidence; --include-inputs adds source trees/blobs,
|
|
54
|
+
not Docker images. Shared cache defaults to operations/.cache. No-cache disables shared
|
|
55
|
+
reuse but preserves private checkpoints. Preview cache prune before applying it.
|
|
56
|
+
Monitor df/du/docker system df. Export needed results before explicit scratch cleanup;
|
|
57
|
+
cleaned runs cannot resume. Global Docker prune is never automatic.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
api_version: pg_maker/v1
|
|
2
|
+
kind: PostgreSQLBuild
|
|
3
|
+
metadata:
|
|
4
|
+
name: test-18-1
|
|
5
|
+
spec:
|
|
6
|
+
config_dir: ./config
|
|
7
|
+
source:
|
|
8
|
+
kind: git
|
|
9
|
+
repository: https://github.com/postgres/postgres.git
|
|
10
|
+
ref: REL_18_1
|
|
11
|
+
postgresql:
|
|
12
|
+
version: '18.1'
|
|
13
|
+
product:
|
|
14
|
+
release: 0
|
|
15
|
+
package_revision: 1
|
|
16
|
+
features:
|
|
17
|
+
- my-feature
|
|
18
|
+
targets:
|
|
19
|
+
- id: debian13-x64
|
|
20
|
+
platform: debian-13
|
|
21
|
+
architecture: x86_64
|
|
22
|
+
cpu_target: x86-64
|
|
23
|
+
artifacts:
|
|
24
|
+
- deb
|
|
25
|
+
documentation:
|
|
26
|
+
profile: ./config/documentation.yaml
|
|
27
|
+
validation:
|
|
28
|
+
profile: full
|
|
29
|
+
baseline: auto
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
api_version: pg_maker/component/v1
|
|
2
|
+
kind: ComponentRecipe
|
|
3
|
+
metadata:
|
|
4
|
+
id: pg-stat-kcache
|
|
5
|
+
revision: 1
|
|
6
|
+
spec:
|
|
7
|
+
type: pgxs
|
|
8
|
+
source:
|
|
9
|
+
kind: git
|
|
10
|
+
repository: https://github.com/powa-team/pg_stat_kcache.git
|
|
11
|
+
# Release REL2_3_2 (2.3.2).
|
|
12
|
+
ref: 1e39814453ab747a3817cfdf4f6f6bd5c0fb9507
|
|
13
|
+
compatible_postgresql: '>=14,<20'
|
|
14
|
+
requires: [pg-stat-statements]
|
|
15
|
+
conflicts: []
|
|
16
|
+
build_dependencies: []
|
|
17
|
+
runtime_dependencies: []
|
|
18
|
+
make_targets: [all]
|
|
19
|
+
test_targets: [installcheck]
|
|
20
|
+
install_targets: [install]
|
|
21
|
+
required_tests: []
|
|
22
|
+
sql_extension: pg_stat_kcache
|
|
23
|
+
shared_library: pg_stat_kcache
|
|
24
|
+
preload: [pg_stat_statements, pg_stat_kcache]
|
|
25
|
+
runtime_files:
|
|
26
|
+
- lib/postgresql/pg_stat_kcache.so
|
|
27
|
+
- share/postgresql/extension/pg_stat_kcache.control
|
|
28
|
+
- share/postgresql/extension/pg_stat_kcache--*.sql
|
|
29
|
+
documentation:
|
|
30
|
+
rules: [../documentation/pg-stat-kcache.yaml]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
api_version: pg_maker/component/v1
|
|
2
|
+
kind: ComponentRecipe
|
|
3
|
+
metadata:
|
|
4
|
+
id: pg-stat-statements
|
|
5
|
+
revision: 1
|
|
6
|
+
spec:
|
|
7
|
+
type: contrib
|
|
8
|
+
source_directory: contrib/pg_stat_statements
|
|
9
|
+
compatible_postgresql: '>=14,<20'
|
|
10
|
+
requires: []
|
|
11
|
+
conflicts: []
|
|
12
|
+
build_dependencies: []
|
|
13
|
+
runtime_dependencies: []
|
|
14
|
+
make_targets: [all]
|
|
15
|
+
test_targets: [check]
|
|
16
|
+
install_targets: [install]
|
|
17
|
+
required_tests: []
|
|
18
|
+
sql_extension: pg_stat_statements
|
|
19
|
+
shared_library: pg_stat_statements
|
|
20
|
+
preload: [pg_stat_statements]
|
|
21
|
+
runtime_files:
|
|
22
|
+
- lib/postgresql/pg_stat_statements.so
|
|
23
|
+
- share/postgresql/extension/pg_stat_statements.control
|
|
24
|
+
- share/postgresql/extension/pg_stat_statements--*.sql
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
api_version: pg_maker/component/v1
|
|
2
|
+
kind: ComponentRecipe
|
|
3
|
+
metadata:
|
|
4
|
+
id: pg-wait-sampling
|
|
5
|
+
revision: 1
|
|
6
|
+
spec:
|
|
7
|
+
type: pgxs
|
|
8
|
+
source:
|
|
9
|
+
kind: git
|
|
10
|
+
repository: https://github.com/postgrespro/pg_wait_sampling.git
|
|
11
|
+
# Release v1.1.11; the SQL extension version is 1.1.
|
|
12
|
+
ref: 7e3a5fc937393596c0a51c5881dd63d7208408d1
|
|
13
|
+
compatible_postgresql: '>=14,<20'
|
|
14
|
+
requires: []
|
|
15
|
+
conflicts: []
|
|
16
|
+
build_dependencies: []
|
|
17
|
+
runtime_dependencies: []
|
|
18
|
+
make_targets: [all]
|
|
19
|
+
test_targets: [installcheck]
|
|
20
|
+
install_targets: [install]
|
|
21
|
+
required_tests: []
|
|
22
|
+
sql_extension: pg_wait_sampling
|
|
23
|
+
shared_library: pg_wait_sampling
|
|
24
|
+
preload: [pg_wait_sampling]
|
|
25
|
+
runtime_files:
|
|
26
|
+
- lib/postgresql/pg_wait_sampling.so
|
|
27
|
+
- share/postgresql/extension/pg_wait_sampling.control
|
|
28
|
+
- share/postgresql/extension/pg_wait_sampling--*.sql
|
|
29
|
+
documentation:
|
|
30
|
+
rules: [../documentation/pg-wait-sampling.yaml]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
api_version: pg_maker/defaults/v1
|
|
2
|
+
kind: MakerDefaults
|
|
3
|
+
metadata:
|
|
4
|
+
id: default
|
|
5
|
+
spec:
|
|
6
|
+
paths:
|
|
7
|
+
runtime_prefix: /opt/{vendor.slug}/{feature.id}/{postgresql.major}{build.mode_suffix}
|
|
8
|
+
data_directory: /var/lib/{vendor.slug}/{feature.id}/{postgresql.major}{build.mode_suffix}/data
|
|
9
|
+
documentation_directory: /opt/{vendor.slug}/{feature.id}/{postgresql.major}{build.mode_suffix}/share/doc/{product.version}
|
|
10
|
+
service:
|
|
11
|
+
user: postgres
|
|
12
|
+
group: postgres
|
|
13
|
+
home: /var/lib/postgresql
|
|
14
|
+
port: 5432
|
|
15
|
+
naming:
|
|
16
|
+
package: '{vendor.slug}-{feature.id}-{role}{role.abi_suffix}-{postgresql.major}{build.mode_suffix}'
|
|
17
|
+
service: '{vendor.slug}-{feature.id}-server-{postgresql.major}{build.mode_suffix}'
|
|
18
|
+
deb_version: '{product.version}-{package_revision}.{platform.package_suffix}'
|
|
19
|
+
rpm_version: '{product.version}'
|
|
20
|
+
rpm_release: '{package_revision}.{platform.package_suffix}'
|
|
21
|
+
common_changes:
|
|
22
|
+
- builtin: unix-socket-directory
|
|
23
|
+
build:
|
|
24
|
+
backend: autotools
|
|
25
|
+
mode: release
|
|
26
|
+
options:
|
|
27
|
+
llvm: true
|
|
28
|
+
openssl: true
|
|
29
|
+
icu: true
|
|
30
|
+
nls_languages:
|
|
31
|
+
- en
|
|
32
|
+
- ru
|
|
33
|
+
message_locale: C.UTF-8
|
|
34
|
+
jobs: 4
|
|
35
|
+
flags:
|
|
36
|
+
cppflags:
|
|
37
|
+
- -Wno-missing-braces
|
|
38
|
+
- -Wformat
|
|
39
|
+
- -Werror=format-security
|
|
40
|
+
- -fstack-protector-strong
|
|
41
|
+
ldflags:
|
|
42
|
+
- -Wl,-z,relro
|
|
43
|
+
- -Wl,-z,now
|
|
44
|
+
lto: false
|
|
45
|
+
build_modes:
|
|
46
|
+
release:
|
|
47
|
+
naming_suffix: ''
|
|
48
|
+
configure_options: []
|
|
49
|
+
cflags:
|
|
50
|
+
- -pipe
|
|
51
|
+
- -O2
|
|
52
|
+
cppflags:
|
|
53
|
+
- -D_FORTIFY_SOURCE=2
|
|
54
|
+
debug:
|
|
55
|
+
naming_suffix: -debug
|
|
56
|
+
configure_options:
|
|
57
|
+
- --enable-debug
|
|
58
|
+
- --enable-cassert
|
|
59
|
+
cflags:
|
|
60
|
+
- -pipe
|
|
61
|
+
- -O0
|
|
62
|
+
- -ggdb3
|
|
63
|
+
cppflags: []
|
|
64
|
+
validation:
|
|
65
|
+
profile: full
|
|
66
|
+
# Temporary development default; enable explicitly for source verification.
|
|
67
|
+
run_db_tests: false
|
|
68
|
+
baseline: auto
|
|
69
|
+
database_locales:
|
|
70
|
+
- en_US.UTF-8
|
|
71
|
+
- ru_RU.UTF-8
|
|
72
|
+
execution:
|
|
73
|
+
runner: docker
|
|
74
|
+
max_parallel_targets: 1
|
|
75
|
+
build_user: 1000:1000
|
|
76
|
+
limits:
|
|
77
|
+
memory_bytes: 8589934592
|
|
78
|
+
min_free_disk_bytes: 21474836480
|
|
79
|
+
stage_timeout_seconds: 7200
|
|
80
|
+
keep_failed_environment: false
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
api_version: pg_maker/documentation/v1
|
|
2
|
+
kind: DocumentationProfile
|
|
3
|
+
metadata:
|
|
4
|
+
id: default
|
|
5
|
+
spec:
|
|
6
|
+
enabled: false
|
|
7
|
+
source: postgres-tree
|
|
8
|
+
source_language: en
|
|
9
|
+
languages:
|
|
10
|
+
- en
|
|
11
|
+
language_aliases: {en: en, en-US: en, ru: ru, ru-RU: ru}
|
|
12
|
+
formats:
|
|
13
|
+
- html
|
|
14
|
+
- man
|
|
15
|
+
html_backend: sphinx
|
|
16
|
+
branding: vendor-metadata
|
|
17
|
+
rules: []
|
|
18
|
+
translations: []
|
|
19
|
+
missing_translation: error
|
|
20
|
+
require_reviewed: false
|
|
21
|
+
glossary: {revision: '1', terms: {}}
|
|
22
|
+
install:
|
|
23
|
+
html: docs-package
|
|
24
|
+
man: owning-package
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
api_version: pg_maker/feature/v1
|
|
2
|
+
kind: FeatureProfile
|
|
3
|
+
metadata:
|
|
4
|
+
id: my-feature
|
|
5
|
+
revision: 2
|
|
6
|
+
spec:
|
|
7
|
+
description: Test
|
|
8
|
+
numeric_id: null
|
|
9
|
+
changes: []
|
|
10
|
+
components:
|
|
11
|
+
- ../../components/pg-stat-statements.yaml
|
|
12
|
+
- ../../components/pg-stat-kcache.yaml
|
|
13
|
+
- ../../components/pg-wait-sampling.yaml
|
|
14
|
+
build_options: {}
|
|
15
|
+
documentation:
|
|
16
|
+
rules: []
|
|
17
|
+
required_tests: []
|