mirror of
https://github.com/d9pouces/id_configurator.git
synced 2026-11-09 01:47:21 +01:00
feat: add Python-alpine Dockerfile for enhanced build process and testing with id-configurator
Signed-off-by: d9pouces <github@19pouces.net>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""Build a minimal, offline, hand-rolled crate and its sparse registry index
|
||||
entry, for integration-testing the `rust` module of id-configurator.
|
||||
|
||||
No network access and no build backend (cargo) is used: the crate is a
|
||||
plain gzipped tar assembled with the standard library only, so this script
|
||||
can run in a minimal Python image with nothing installed.
|
||||
|
||||
See https://doc.rust-lang.org/cargo/reference/registries.html#index-format
|
||||
for the sparse registry index/config.json format this reproduces.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import pathlib
|
||||
import tarfile
|
||||
|
||||
CRATE = "idconfiguratortestpkg"
|
||||
VERSION = "0.0.1"
|
||||
CRATE_FILE = f"{CRATE}-{VERSION}.crate"
|
||||
OUT_DIR = pathlib.Path("/srv")
|
||||
|
||||
CARGO_TOML = f"""[package]
|
||||
name = "{CRATE}"
|
||||
version = "{VERSION}"
|
||||
edition = "2021"
|
||||
"""
|
||||
LIB_RS = "// test-only crate served by the id-configurator integration tests\n"
|
||||
|
||||
|
||||
def _add_file(archive: tarfile.TarFile, name: str, content: bytes) -> None:
|
||||
info = tarfile.TarInfo(name=f"{CRATE}-{VERSION}/{name}")
|
||||
info.size = len(content)
|
||||
archive.addfile(info, io.BytesIO(content))
|
||||
|
||||
|
||||
def build_crate() -> bytes:
|
||||
buffer = io.BytesIO()
|
||||
with tarfile.open(fileobj=buffer, mode="w:gz") as archive:
|
||||
_add_file(archive, "Cargo.toml", CARGO_TOML.encode("utf-8"))
|
||||
_add_file(archive, "src/lib.rs", LIB_RS.encode("utf-8"))
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def index_path(name: str) -> pathlib.Path:
|
||||
"""Mirror cargo's sparse index path convention for a crate name."""
|
||||
if len(name) <= 2:
|
||||
return pathlib.Path(str(len(name))) / name
|
||||
if len(name) == 3:
|
||||
return pathlib.Path("3") / name[0] / name
|
||||
return pathlib.Path(name[0:2]) / name[2:4] / name
|
||||
|
||||
|
||||
def write_index(out_dir: pathlib.Path, crate: bytes) -> None:
|
||||
entry = {
|
||||
"name": CRATE,
|
||||
"vers": VERSION,
|
||||
"deps": [],
|
||||
"cksum": hashlib.sha256(crate).hexdigest(),
|
||||
"features": {},
|
||||
"yanked": False,
|
||||
}
|
||||
path = out_dir / index_path(CRATE)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(entry) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def write_config_json(out_dir: pathlib.Path) -> None:
|
||||
# Cargo requires a fully-qualified "dl" download template, so this bakes
|
||||
# in a placeholder that nginx's sub_filter rewrites to the real
|
||||
# https://<host used by the client>/ at request time (see
|
||||
# cargo-repo.nginx.conf) - the host and port this fixture is actually
|
||||
# reached on aren't known at image-build time.
|
||||
config = {
|
||||
"dl": "__BASE_URL__/api/v1/crates/{crate}/{version}/download",
|
||||
"api": "__BASE_URL__",
|
||||
}
|
||||
(out_dir / "config.json").write_text(json.dumps(config), encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
crate = build_crate()
|
||||
download_path = OUT_DIR / "api/v1/crates" / CRATE / VERSION / "download"
|
||||
download_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
download_path.write_bytes(crate)
|
||||
write_index(OUT_DIR, crate)
|
||||
write_config_json(OUT_DIR)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Build a minimal, offline, hand-rolled Composer package and its
|
||||
`packages.json` index, for integration-testing the `php` module of
|
||||
id-configurator.
|
||||
|
||||
No network access and no build backend (composer) is used: the package is a
|
||||
plain zip file assembled with the standard library only, so this script can
|
||||
run in a minimal Python image with nothing installed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import pathlib
|
||||
import zipfile
|
||||
|
||||
VENDOR = "interdiode"
|
||||
PACKAGE = "idconfiguratortestpkg"
|
||||
NAME = f"{VENDOR}/{PACKAGE}"
|
||||
VERSION = "0.0.1"
|
||||
ZIP_NAME = f"{PACKAGE}-{VERSION}.zip"
|
||||
OUT_DIR = pathlib.Path("/srv")
|
||||
|
||||
COMPOSER_JSON = json.dumps({"name": NAME, "version": VERSION, "type": "library"})
|
||||
|
||||
|
||||
def build_zip() -> bytes:
|
||||
buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as archive:
|
||||
archive.writestr("composer.json", COMPOSER_JSON)
|
||||
archive.writestr(
|
||||
"marker.txt",
|
||||
"test-only package served by the id-configurator integration tests\n",
|
||||
)
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def write_packages_json(out_dir: pathlib.Path, zip_content: bytes) -> None:
|
||||
# Composer requires a fully-qualified dist URL: unlike PyPI's simple
|
||||
# index, it does not resolve relative/root-relative ones against the
|
||||
# repository's own URL for this (non-v2) repository format. The host
|
||||
# and port this fixture will actually be reached on aren't known at
|
||||
# image-build time (tox-docker only assigns them once the container
|
||||
# starts), so this bakes in a placeholder that nginx's sub_filter
|
||||
# rewrites to the real https://<host used by the client>/ at request
|
||||
# time (see php-repo.nginx.conf).
|
||||
packages = {
|
||||
"packages": {
|
||||
NAME: {
|
||||
VERSION: {
|
||||
"name": NAME,
|
||||
"version": VERSION,
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": f"__BASE_URL__/{ZIP_NAME}",
|
||||
"shasum": hashlib.sha1(zip_content).hexdigest(), # nosec B324
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(out_dir / "packages.json").write_text(
|
||||
json.dumps(packages, indent=2), encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
zip_content = build_zip()
|
||||
(OUT_DIR / ZIP_NAME).write_bytes(zip_content)
|
||||
write_packages_json(OUT_DIR, zip_content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Build a minimal, offline, hand-rolled npm package and its registry
|
||||
metadata document, for integration-testing the `npm` module of
|
||||
id-configurator.
|
||||
|
||||
No network access and no build backend (npm/node) is used: the tarball is a
|
||||
plain gzipped tar assembled with the standard library only, so this script
|
||||
can run in a minimal Python image with nothing installed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import pathlib
|
||||
import tarfile
|
||||
|
||||
PACKAGE = "idconfiguratortestpkg"
|
||||
VERSION = "0.0.1"
|
||||
TARBALL_NAME = f"{PACKAGE}-{VERSION}.tgz"
|
||||
OUT_DIR = pathlib.Path("/srv")
|
||||
|
||||
PACKAGE_JSON = json.dumps({"name": PACKAGE, "version": VERSION})
|
||||
MARKER = "test-only package served by the id-configurator integration tests\n"
|
||||
|
||||
|
||||
def _add_file(archive: tarfile.TarFile, name: str, content: bytes) -> None:
|
||||
info = tarfile.TarInfo(name=f"package/{name}")
|
||||
info.size = len(content)
|
||||
archive.addfile(info, io.BytesIO(content))
|
||||
|
||||
|
||||
def build_tarball() -> bytes:
|
||||
buffer = io.BytesIO()
|
||||
with tarfile.open(fileobj=buffer, mode="w:gz") as archive:
|
||||
_add_file(archive, "package.json", PACKAGE_JSON.encode("utf-8"))
|
||||
_add_file(archive, "marker.txt", MARKER.encode("utf-8"))
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def write_registry_metadata(out_dir: pathlib.Path, tarball: bytes) -> None:
|
||||
# npm requires a fully-qualified tarball URL, so this bakes in a
|
||||
# placeholder that nginx's sub_filter rewrites to the real
|
||||
# https://<host used by the client>/ at request time (see
|
||||
# npm-repo.nginx.conf) - the host and port this fixture is actually
|
||||
# reached on aren't known at image-build time.
|
||||
sha1 = hashlib.sha1(tarball).hexdigest() # nosec B324
|
||||
sha512_b64 = base64.b64encode(hashlib.sha512(tarball).digest()).decode("ascii")
|
||||
metadata = {
|
||||
"name": PACKAGE,
|
||||
"dist-tags": {"latest": VERSION},
|
||||
"versions": {
|
||||
VERSION: {
|
||||
"name": PACKAGE,
|
||||
"version": VERSION,
|
||||
"dist": {
|
||||
"shasum": sha1,
|
||||
"integrity": f"sha512-{sha512_b64}",
|
||||
"tarball": f"__BASE_URL__/{TARBALL_NAME}",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
(out_dir / PACKAGE).write_text(json.dumps(metadata, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
tarball = build_tarball()
|
||||
(OUT_DIR / TARBALL_NAME).write_bytes(tarball)
|
||||
write_registry_metadata(OUT_DIR, tarball)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,17 @@
|
||||
# Minimal cargo sparse registry (index entry, config.json and one hand-built
|
||||
# crate) served over TLS, using the same Test CA as the other *-repo
|
||||
# fixtures, used by tox-docker to exercise the `rust` module integration
|
||||
# test (tests/configs/Rust.yaml.j2, tests/dockerfiles/Rust.Dockerfile).
|
||||
#
|
||||
# Built by tox-docker, whose build context is this directory (not the repo
|
||||
# root) - every COPY source below must live under tests/dockerfiles/repos/.
|
||||
FROM python:3.13-slim AS packagebuild
|
||||
COPY build_test_cargo_package.py /build_test_cargo_package.py
|
||||
RUN python3 /build_test_cargo_package.py
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=packagebuild /srv /srv
|
||||
COPY repo-server.crt.pem /etc/nginx/certs/repo-server.crt.pem
|
||||
COPY repo-server.key.pem /etc/nginx/certs/repo-server.key.pem
|
||||
COPY cargo-repo.nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 8443
|
||||
@@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen 8443 ssl default_server;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/repo-server.crt.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/repo-server.key.pem;
|
||||
|
||||
root /srv;
|
||||
autoindex off;
|
||||
|
||||
# config.json bakes in a __BASE_URL__ placeholder for its "dl"/"api"
|
||||
# templates, since the host:port this fixture is actually reached on is
|
||||
# only known once tox-docker starts the container, not at image-build
|
||||
# time. $http_host reflects exactly what the client (cargo) used to
|
||||
# connect, e.g. host.docker.internal:55123.
|
||||
location = /config.json {
|
||||
default_type application/json;
|
||||
sub_filter '__BASE_URL__' 'https://$http_host';
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Minimal npm registry (one package's metadata document plus its tarball)
|
||||
# served over TLS, using the same Test CA as the other *-repo fixtures, used
|
||||
# by tox-docker to exercise the `npm` module integration test
|
||||
# (tests/configs/Npm.yaml.j2, tests/dockerfiles/Npm.Dockerfile).
|
||||
#
|
||||
# Built by tox-docker, whose build context is this directory (not the repo
|
||||
# root) - every COPY source below must live under tests/dockerfiles/repos/.
|
||||
FROM python:3.13-slim AS packagebuild
|
||||
COPY build_test_npm_package.py /build_test_npm_package.py
|
||||
RUN python3 /build_test_npm_package.py
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=packagebuild /srv /srv
|
||||
COPY repo-server.crt.pem /etc/nginx/certs/repo-server.crt.pem
|
||||
COPY repo-server.key.pem /etc/nginx/certs/repo-server.key.pem
|
||||
COPY npm-repo.nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 8443
|
||||
@@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen 8443 ssl default_server;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/repo-server.crt.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/repo-server.key.pem;
|
||||
|
||||
root /srv;
|
||||
autoindex off;
|
||||
|
||||
# The package metadata document bakes in a __BASE_URL__ placeholder for
|
||||
# its tarball URL, since the host:port this fixture is actually reached
|
||||
# on is only known once tox-docker starts the container, not at
|
||||
# image-build time. $http_host reflects exactly what the client (npm)
|
||||
# used to connect, e.g. host.docker.internal:55123.
|
||||
location = /idconfiguratortestpkg {
|
||||
default_type application/json;
|
||||
sub_filter '__BASE_URL__' 'https://$http_host';
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Minimal Composer repository ("composer" type: a static packages.json plus
|
||||
# one hand-built zip package) served over TLS, using the same Test CA as the
|
||||
# other *-repo fixtures, used by tox-docker to exercise the `php` module
|
||||
# integration test (tests/configs/Php.yaml.j2, tests/dockerfiles/Php.Dockerfile).
|
||||
#
|
||||
# Built by tox-docker, whose build context is this directory (not the repo
|
||||
# root) - every COPY source below must live under tests/dockerfiles/repos/.
|
||||
FROM python:3.13-slim AS packagebuild
|
||||
COPY build_test_composer_package.py /build_test_composer_package.py
|
||||
RUN python3 /build_test_composer_package.py
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=packagebuild /srv /srv
|
||||
COPY repo-server.crt.pem /etc/nginx/certs/repo-server.crt.pem
|
||||
COPY repo-server.key.pem /etc/nginx/certs/repo-server.key.pem
|
||||
COPY php-repo.nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 8443
|
||||
@@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen 8443 ssl default_server;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/repo-server.crt.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/repo-server.key.pem;
|
||||
|
||||
root /srv;
|
||||
autoindex off;
|
||||
|
||||
# packages.json bakes in a __BASE_URL__ placeholder for its dist URL,
|
||||
# since the host:port this fixture is actually reached on is only known
|
||||
# once tox-docker starts the container, not at image-build time.
|
||||
# $http_host reflects exactly what the client (Composer) used to
|
||||
# connect, e.g. host.docker.internal:55123.
|
||||
location = /packages.json {
|
||||
default_type application/json;
|
||||
sub_filter '__BASE_URL__' 'https://$http_host';
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user