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:
@@ -152,6 +152,42 @@ class JavaConfig(BaseModel):
|
||||
repositories: list[JavaRepository] = Field(default_factory=list)
|
||||
|
||||
|
||||
class PhpRepository(BaseModel):
|
||||
"""A Composer repository (see the "repositories" key in composer.json)."""
|
||||
|
||||
name: str
|
||||
url: str
|
||||
|
||||
|
||||
class PhpConfig(BaseModel):
|
||||
repositories: list[PhpRepository] = Field(default_factory=list)
|
||||
|
||||
|
||||
class NpmRepository(BaseModel):
|
||||
"""An npm registry. The first entry replaces the default registry; any
|
||||
further ones are scoped to `name` (`@name:registry`), e.g. for a private
|
||||
org's packages alongside the default public registry."""
|
||||
|
||||
name: str
|
||||
url: str
|
||||
|
||||
|
||||
class NpmConfig(BaseModel):
|
||||
repositories: list[NpmRepository] = Field(default_factory=list)
|
||||
|
||||
|
||||
class RustRepository(BaseModel):
|
||||
"""A cargo registry (see the "source" tables in .cargo/config.toml),
|
||||
served over the sparse HTTP protocol."""
|
||||
|
||||
name: str
|
||||
url: str
|
||||
|
||||
|
||||
class RustConfig(BaseModel):
|
||||
repositories: list[RustRepository] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ConfigurationsBlock(BaseModel):
|
||||
certificates: list[NamedContent] = Field(default_factory=list)
|
||||
apt: AptConfig = Field(default_factory=AptConfig)
|
||||
@@ -159,6 +195,9 @@ class ConfigurationsBlock(BaseModel):
|
||||
yum: YumConfig = Field(default_factory=YumConfig)
|
||||
python: PythonConfig = Field(default_factory=PythonConfig)
|
||||
java: JavaConfig = Field(default_factory=JavaConfig)
|
||||
php: PhpConfig = Field(default_factory=PhpConfig)
|
||||
npm: NpmConfig = Field(default_factory=NpmConfig)
|
||||
rust: RustConfig = Field(default_factory=RustConfig)
|
||||
|
||||
|
||||
class Configuration(BaseModel):
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
Importing this package registers every built-in module (see `base.register`).
|
||||
"""
|
||||
|
||||
from id_configurator.modules import apk, apt, certificates, java, python, yum
|
||||
from id_configurator.modules import (
|
||||
apk,
|
||||
apt,
|
||||
certificates,
|
||||
java,
|
||||
npm,
|
||||
php,
|
||||
python,
|
||||
rust,
|
||||
yum,
|
||||
)
|
||||
|
||||
__all__ = ["apk", "apt", "certificates", "java", "python", "yum"]
|
||||
__all__ = [
|
||||
"apk",
|
||||
"apt",
|
||||
"certificates",
|
||||
"java",
|
||||
"npm",
|
||||
"php",
|
||||
"python",
|
||||
"rust",
|
||||
"yum",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Configure npm to use the configured NpmRepository entries: the first one
|
||||
replaces the default registry, and any further ones are registered scoped to
|
||||
their own name (`@name:registry`), e.g. for a private org's packages
|
||||
alongside the default public registry.
|
||||
|
||||
Unlike Composer or pip, npm (via Node.js) does not trust the system CA store
|
||||
by default - Node bundles its own root CA list - so this also points npm's
|
||||
own `cafile` setting at the bundle the `certificates` module writes. Unlike
|
||||
Poetry's per-repository certificate, `cafile` is a single, global npm
|
||||
setting.
|
||||
|
||||
Written to npm's "global" config file (what `npm config --global` edits),
|
||||
`$prefix/etc/npmrc` - not the "user" config, `$HOME/.npmrc`, which is a
|
||||
separate, higher-precedence level in npm's own terminology. `$prefix`
|
||||
defaults to `/usr/local` for essentially every standard Node.js install
|
||||
(the official Docker image included); unlike a config dropped in one
|
||||
specific user's home, this applies regardless of which user invokes npm.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from id_configurator.config import NpmRepository
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.base import Module, register
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_NPMRC = "usr/local/etc/npmrc"
|
||||
_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
|
||||
def _write_npmrc(context: Context, repositories: list[NpmRepository]) -> None:
|
||||
lines = [f"cafile={_CA_BUNDLE}", f"registry={repositories[0].url}"]
|
||||
lines.extend(
|
||||
f"@{repository.name}:registry={repository.url}"
|
||||
for repository in repositories[1:]
|
||||
)
|
||||
context.write_file(_NPMRC, ("\n".join(lines) + "\n").encode("utf-8"))
|
||||
logger.info("configured npm to use %s", repositories[0].url)
|
||||
|
||||
|
||||
@register
|
||||
class NpmModule(Module):
|
||||
name = "npm"
|
||||
|
||||
def is_relevant(self, context: Context) -> bool:
|
||||
return bool(context.config.configurations.npm.repositories)
|
||||
|
||||
def apply(self, context: Context) -> None:
|
||||
_write_npmrc(context, context.config.configurations.npm.repositories)
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Configure Composer to use the configured PhpRepository entries instead of
|
||||
(in addition to) Packagist.
|
||||
|
||||
Composer has no "replace the default registry" switch either: Packagist
|
||||
stays enabled unless a repository is explicitly declared under the name
|
||||
"packagist.org" and disabled. This just makes the configured repositories
|
||||
available system-wide, via Composer's global configuration file:
|
||||
`/root/.composer/config.json` (`COMPOSER_HOME/config.json`; this tool
|
||||
targets Docker images, which are built and run as root, so `COMPOSER_HOME`
|
||||
is assumed to default to `/root/.composer`, exactly like a fresh Composer
|
||||
install does when not overridden).
|
||||
|
||||
Composer's own HTTPS client (PHP's stream wrapper, generally backed by
|
||||
curl) already trusts whatever the `certificates` module installs into the
|
||||
system CA store, so no per-repository certificate configuration is needed
|
||||
here, unlike Poetry's.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from id_configurator.config import PhpRepository
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.base import Module, register
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_COMPOSER_CONFIG = "root/.composer/config.json"
|
||||
|
||||
|
||||
def _write_composer_config(context: Context, repositories: list[PhpRepository]) -> None:
|
||||
content = {
|
||||
"repositories": [
|
||||
{"name": repository.name, "type": "composer", "url": repository.url}
|
||||
for repository in repositories
|
||||
]
|
||||
}
|
||||
context.write_file(
|
||||
_COMPOSER_CONFIG, (json.dumps(content, indent=4) + "\n").encode("utf-8")
|
||||
)
|
||||
logger.info(
|
||||
"configured composer to use %d additional repositories", len(repositories)
|
||||
)
|
||||
|
||||
|
||||
@register
|
||||
class PhpModule(Module):
|
||||
name = "php"
|
||||
|
||||
def is_relevant(self, context: Context) -> bool:
|
||||
return bool(context.config.configurations.php.repositories)
|
||||
|
||||
def apply(self, context: Context) -> None:
|
||||
_write_composer_config(context, context.config.configurations.php.repositories)
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Configure cargo to use the configured RustRepository entries: the first
|
||||
one replaces crates.io entirely (via `[source.crates-io] replace-with`), and
|
||||
any further ones are registered as additional named registries (usable via
|
||||
`cargo add --registry <name>` or a dependency's `registry = "<name>"`), all
|
||||
using the sparse (plain HTTPS) protocol rather than a git-based index.
|
||||
|
||||
Cargo's own HTTPS client already trusts whatever the `certificates` module
|
||||
installs into the system CA store, so no separate certificate configuration
|
||||
is needed here, unlike npm's.
|
||||
|
||||
Written to `/.cargo/config.toml` - the filesystem root, not `$CARGO_HOME`
|
||||
(which defaults to `$HOME/.cargo` but is routinely overridden, e.g. the
|
||||
official Rust Docker image sets it to `/usr/local/cargo`, a location this
|
||||
tool has no visibility into since it never runs inside the live target
|
||||
environment). Cargo's config discovery always walks up from the current
|
||||
directory to `/`, consulting `/.cargo/config.toml` there, *before* falling
|
||||
back to `$CARGO_HOME/config.toml` - so this is the one genuinely global
|
||||
location: found regardless of which user, working directory or `CARGO_HOME`
|
||||
is in effect, unlike a config dropped in one specific user's home.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from id_configurator.config import RustRepository
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.base import Module, register
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_CARGO_CONFIG = ".cargo/config.toml"
|
||||
|
||||
|
||||
def _write_cargo_config(context: Context, repositories: list[RustRepository]) -> None:
|
||||
primary = repositories[0]
|
||||
lines = [
|
||||
"[source.crates-io]",
|
||||
f'replace-with = "{primary.name}"',
|
||||
"",
|
||||
f"[source.{primary.name}]",
|
||||
f'registry = "sparse+{primary.url}"',
|
||||
]
|
||||
for repository in repositories[1:]:
|
||||
lines += [
|
||||
"",
|
||||
f"[registries.{repository.name}]",
|
||||
f'index = "sparse+{repository.url}"',
|
||||
]
|
||||
context.write_file(_CARGO_CONFIG, ("\n".join(lines) + "\n").encode("utf-8"))
|
||||
logger.info("configured cargo to use %s", primary.url)
|
||||
|
||||
|
||||
@register
|
||||
class RustModule(Module):
|
||||
name = "rust"
|
||||
|
||||
def is_relevant(self, context: Context) -> bool:
|
||||
return bool(context.config.configurations.rust.repositories)
|
||||
|
||||
def apply(self, context: Context) -> None:
|
||||
_write_cargo_config(context, context.config.configurations.rust.repositories)
|
||||
@@ -21,3 +21,9 @@ configurations:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -24,3 +24,9 @@ configurations:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -18,3 +18,9 @@ configurations:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -35,3 +35,7 @@ configurations:
|
||||
# resolver, since the test artifact is already found via "central".
|
||||
- name: maven-central-fallback
|
||||
url: https://repo1.maven.org/maven2/
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
uuid: f8348fe2-3f69-4eee-9c3d-d5f092c9ff80
|
||||
organisation: InterDiode
|
||||
description: Npm test configuration
|
||||
configurations:
|
||||
certificates:
|
||||
- name: Test CA
|
||||
content: |
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
repositories: []
|
||||
keys: []
|
||||
python:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories:
|
||||
- name: demo
|
||||
url: https://host.docker.internal:{{ 'NPM_REPO_PORT' | env }}/
|
||||
rust:
|
||||
repositories: []
|
||||
@@ -0,0 +1,28 @@
|
||||
uuid: 7135f49c-44c4-4583-8512-2ed5c6ee726b
|
||||
organisation: InterDiode
|
||||
description: Php test configuration
|
||||
configurations:
|
||||
certificates:
|
||||
- name: Test CA
|
||||
content: |
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
repositories: []
|
||||
keys: []
|
||||
python:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories:
|
||||
- name: demo
|
||||
url: https://host.docker.internal:{{ 'PHP_REPO_PORT' | env }}/
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
@@ -20,3 +20,9 @@ configurations:
|
||||
index-url: https://host.docker.internal:{{ 'PYPI_REPO_PORT' | env }}/simple/
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
uuid: 63db4c0e-a4c4-402c-bc4f-4eddeef0d890
|
||||
organisation: InterDiode
|
||||
description: Rust test configuration
|
||||
configurations:
|
||||
certificates:
|
||||
- name: Test CA
|
||||
content: |
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
repositories: []
|
||||
keys: []
|
||||
python:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories:
|
||||
- name: demo
|
||||
url: https://host.docker.internal:{{ 'CARGO_REPO_PORT' | env }}/
|
||||
@@ -20,3 +20,9 @@ configurations:
|
||||
repositories: []
|
||||
java:
|
||||
repositories: []
|
||||
php:
|
||||
repositories: []
|
||||
npm:
|
||||
repositories: []
|
||||
rust:
|
||||
repositories: []
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM node:20-slim AS target
|
||||
FROM interdiode/id-configurator:latest AS configuration
|
||||
COPY --from=target / /src/
|
||||
COPY --from=generated-config Npm.yaml /config.yaml
|
||||
RUN id-configurator apply --config /config.yaml --src /src --dst /dst
|
||||
FROM target AS runtime
|
||||
COPY --from=configuration /dst/ /
|
||||
RUN mkdir -p /tmp/npmtest && cd /tmp/npmtest \
|
||||
&& npm init -y >/dev/null \
|
||||
&& npm install idconfiguratortestpkg \
|
||||
&& grep -qx 'test-only package served by the id-configurator integration tests' \
|
||||
node_modules/idconfiguratortestpkg/marker.txt
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM php:8.3-cli AS target
|
||||
FROM interdiode/id-configurator:latest AS configuration
|
||||
COPY --from=target / /src/
|
||||
COPY --from=generated-config Php.yaml /config.yaml
|
||||
RUN id-configurator apply --config /config.yaml --src /src --dst /dst
|
||||
FROM target AS runtime
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates unzip curl \
|
||||
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=configuration /dst/ /
|
||||
RUN mkdir -p /tmp/phptest && cd /tmp/phptest \
|
||||
&& composer init -n --name test/phptest \
|
||||
&& composer require interdiode/idconfiguratortestpkg \
|
||||
&& grep -qx 'test-only package served by the id-configurator integration tests' \
|
||||
vendor/interdiode/idconfiguratortestpkg/marker.txt
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM rust:1-slim AS target
|
||||
FROM interdiode/id-configurator:latest AS configuration
|
||||
COPY --from=target / /src/
|
||||
COPY --from=generated-config Rust.yaml /config.yaml
|
||||
RUN id-configurator apply --config /config.yaml --src /src --dst /dst
|
||||
FROM target AS runtime
|
||||
COPY --from=configuration /dst/ /
|
||||
# Deliberately not touching CARGO_HOME (the official image sets it to
|
||||
# /usr/local/cargo): id-configurator writes /.cargo/config.toml, which
|
||||
# cargo's config search always consults regardless of CARGO_HOME, precisely
|
||||
# so this doesn't need to line up with wherever CARGO_HOME happens to point.
|
||||
RUN cargo new /tmp/rusttest -q && cd /tmp/rusttest \
|
||||
&& cargo add idconfiguratortestpkg \
|
||||
&& cargo fetch \
|
||||
&& grep -qx '// test-only crate served by the id-configurator integration tests' \
|
||||
"$(find "$CARGO_HOME/registry/src" -name lib.rs)"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,10 @@ def test_list_modules() -> None:
|
||||
"apt",
|
||||
"certificates",
|
||||
"java",
|
||||
"npm",
|
||||
"php",
|
||||
"python",
|
||||
"rust",
|
||||
"yum",
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
from id_configurator.config import Configuration
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.npm import NpmModule
|
||||
|
||||
CONFIG_WITH_REPO = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"npm": {"repositories": [{"name": "demo", "url": "https://example.invalid/"}]}
|
||||
},
|
||||
}
|
||||
|
||||
CONFIG_WITH_TWO_REPOS = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"npm": {
|
||||
"repositories": [
|
||||
{"name": "demo", "url": "https://example.invalid/"},
|
||||
{"name": "myorg", "url": "https://other.invalid/"},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
EMPTY_CONFIG = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
}
|
||||
|
||||
|
||||
def make_config(data: dict) -> Configuration:
|
||||
return Configuration.model_validate(data)
|
||||
|
||||
|
||||
def test_is_relevant_false_when_no_repositories(tmp_path) -> None:
|
||||
context = Context(config=make_config(EMPTY_CONFIG), dst=tmp_path / "dst", src=None)
|
||||
assert NpmModule().is_relevant(context) is False
|
||||
|
||||
|
||||
def test_is_relevant_true_with_repository(tmp_path) -> None:
|
||||
context = Context(
|
||||
config=make_config(CONFIG_WITH_REPO), dst=tmp_path / "dst", src=None
|
||||
)
|
||||
assert NpmModule().is_relevant(context) is True
|
||||
|
||||
|
||||
def test_apply_writes_default_registry_and_cafile(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_REPO), dst=dst, src=None)
|
||||
|
||||
NpmModule().apply(context)
|
||||
|
||||
npmrc = (dst / "usr/local/etc/npmrc").read_text(encoding="utf-8")
|
||||
assert npmrc == (
|
||||
"cafile=/etc/ssl/certs/ca-certificates.crt\nregistry=https://example.invalid/\n"
|
||||
)
|
||||
|
||||
|
||||
def test_apply_writes_additional_repositories_scoped(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_TWO_REPOS), dst=dst, src=None)
|
||||
|
||||
NpmModule().apply(context)
|
||||
|
||||
npmrc = (dst / "usr/local/etc/npmrc").read_text(encoding="utf-8")
|
||||
assert npmrc == (
|
||||
"cafile=/etc/ssl/certs/ca-certificates.crt\n"
|
||||
"registry=https://example.invalid/\n"
|
||||
"@myorg:registry=https://other.invalid/\n"
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
import json
|
||||
|
||||
from id_configurator.config import Configuration
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.php import PhpModule
|
||||
|
||||
CONFIG_WITH_REPO = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"php": {
|
||||
"repositories": [
|
||||
{"name": "demo", "url": "https://example.invalid/composer"}
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
CONFIG_WITH_TWO_REPOS = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"php": {
|
||||
"repositories": [
|
||||
{"name": "demo", "url": "https://example.invalid/composer"},
|
||||
{"name": "other", "url": "https://other.invalid/composer"},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
EMPTY_CONFIG = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
}
|
||||
|
||||
|
||||
def make_config(data: dict) -> Configuration:
|
||||
return Configuration.model_validate(data)
|
||||
|
||||
|
||||
def test_is_relevant_false_when_no_repositories(tmp_path) -> None:
|
||||
context = Context(config=make_config(EMPTY_CONFIG), dst=tmp_path / "dst", src=None)
|
||||
assert PhpModule().is_relevant(context) is False
|
||||
|
||||
|
||||
def test_is_relevant_true_with_repository(tmp_path) -> None:
|
||||
context = Context(
|
||||
config=make_config(CONFIG_WITH_REPO), dst=tmp_path / "dst", src=None
|
||||
)
|
||||
assert PhpModule().is_relevant(context) is True
|
||||
|
||||
|
||||
def test_apply_writes_global_composer_config(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_REPO), dst=dst, src=None)
|
||||
|
||||
PhpModule().apply(context)
|
||||
|
||||
config_path = dst / "root/.composer/config.json"
|
||||
content = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
assert content == {
|
||||
"repositories": [
|
||||
{
|
||||
"name": "demo",
|
||||
"type": "composer",
|
||||
"url": "https://example.invalid/composer",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_apply_writes_multiple_repositories_in_order(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_TWO_REPOS), dst=dst, src=None)
|
||||
|
||||
PhpModule().apply(context)
|
||||
|
||||
config_path = dst / "root/.composer/config.json"
|
||||
content = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
assert [repo["name"] for repo in content["repositories"]] == ["demo", "other"]
|
||||
@@ -0,0 +1,80 @@
|
||||
from id_configurator.config import Configuration
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.rust import RustModule
|
||||
|
||||
CONFIG_WITH_REPO = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"rust": {"repositories": [{"name": "demo", "url": "https://example.invalid/"}]}
|
||||
},
|
||||
}
|
||||
|
||||
CONFIG_WITH_TWO_REPOS = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"rust": {
|
||||
"repositories": [
|
||||
{"name": "demo", "url": "https://example.invalid/"},
|
||||
{"name": "other", "url": "https://other.invalid/"},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
EMPTY_CONFIG = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
}
|
||||
|
||||
|
||||
def make_config(data: dict) -> Configuration:
|
||||
return Configuration.model_validate(data)
|
||||
|
||||
|
||||
def test_is_relevant_false_when_no_repositories(tmp_path) -> None:
|
||||
context = Context(config=make_config(EMPTY_CONFIG), dst=tmp_path / "dst", src=None)
|
||||
assert RustModule().is_relevant(context) is False
|
||||
|
||||
|
||||
def test_is_relevant_true_with_repository(tmp_path) -> None:
|
||||
context = Context(
|
||||
config=make_config(CONFIG_WITH_REPO), dst=tmp_path / "dst", src=None
|
||||
)
|
||||
assert RustModule().is_relevant(context) is True
|
||||
|
||||
|
||||
def test_apply_writes_replacement_source(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_REPO), dst=dst, src=None)
|
||||
|
||||
RustModule().apply(context)
|
||||
|
||||
config_toml = (dst / ".cargo/config.toml").read_text(encoding="utf-8")
|
||||
assert config_toml == (
|
||||
"[source.crates-io]\n"
|
||||
'replace-with = "demo"\n'
|
||||
"\n"
|
||||
"[source.demo]\n"
|
||||
'registry = "sparse+https://example.invalid/"\n'
|
||||
)
|
||||
|
||||
|
||||
def test_apply_writes_additional_repositories_as_named_registries(tmp_path) -> None:
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(CONFIG_WITH_TWO_REPOS), dst=dst, src=None)
|
||||
|
||||
RustModule().apply(context)
|
||||
|
||||
config_toml = (dst / ".cargo/config.toml").read_text(encoding="utf-8")
|
||||
assert config_toml == (
|
||||
"[source.crates-io]\n"
|
||||
'replace-with = "demo"\n'
|
||||
"\n"
|
||||
"[source.demo]\n"
|
||||
'registry = "sparse+https://example.invalid/"\n'
|
||||
"\n"
|
||||
"[registries.other]\n"
|
||||
'index = "sparse+https://other.invalid/"\n'
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
[tox]
|
||||
env_list = py313,py314,coverage,packaging,publish,module-python,module-apt,module-yum,module-apk,module-java,module-certificates
|
||||
env_list = py313,py314,coverage,packaging,publish,module-python,module-apt,module-yum,module-apk,module-java,module-certificates,module-php,module-npm,module-rust
|
||||
isolated_build = true
|
||||
requires =
|
||||
tox>=4
|
||||
@@ -211,3 +211,72 @@ commands =
|
||||
python3 -m id_tools test render-config {tox_root}/tests/configs/Certificate.yaml.j2 {env_dir}/configs/Certificate.yaml
|
||||
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg CERTIFICATE_REPO_PORT={env:CERTIFICATE_REPO_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Certificate.Dockerfile -t id-configurator-certificates .
|
||||
docker rmi id-configurator-certificates
|
||||
|
||||
[docker:php-repo]
|
||||
dockerfile = {tox_root}/tests/dockerfiles/repos/php-repo.Dockerfile
|
||||
expose =
|
||||
PHP_REPO_PORT=8443/tcp
|
||||
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/packages.json || exit 1
|
||||
healthcheck_interval = 1
|
||||
healthcheck_retries = 30
|
||||
healthcheck_timeout = 2
|
||||
healthcheck_start_period = 1
|
||||
|
||||
[testenv:module-php]
|
||||
package = skip
|
||||
change_dir = {tox_root}
|
||||
dependency_groups = dev
|
||||
docker =
|
||||
php-repo
|
||||
allowlist_externals =
|
||||
docker
|
||||
commands =
|
||||
python3 -m id_tools test render-config {tox_root}/tests/configs/Php.yaml.j2 {env_dir}/configs/Php.yaml
|
||||
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Php.Dockerfile -t id-configurator-php .
|
||||
docker rmi id-configurator-php
|
||||
|
||||
[docker:npm-repo]
|
||||
dockerfile = {tox_root}/tests/dockerfiles/repos/npm-repo.Dockerfile
|
||||
expose =
|
||||
NPM_REPO_PORT=8443/tcp
|
||||
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/idconfiguratortestpkg || exit 1
|
||||
healthcheck_interval = 1
|
||||
healthcheck_retries = 30
|
||||
healthcheck_timeout = 2
|
||||
healthcheck_start_period = 1
|
||||
|
||||
[testenv:module-npm]
|
||||
package = skip
|
||||
change_dir = {tox_root}
|
||||
dependency_groups = dev
|
||||
docker =
|
||||
npm-repo
|
||||
allowlist_externals =
|
||||
docker
|
||||
commands =
|
||||
python3 -m id_tools test render-config {tox_root}/tests/configs/Npm.yaml.j2 {env_dir}/configs/Npm.yaml
|
||||
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Npm.Dockerfile -t id-configurator-npm .
|
||||
docker rmi id-configurator-npm
|
||||
|
||||
[docker:cargo-repo]
|
||||
dockerfile = {tox_root}/tests/dockerfiles/repos/cargo-repo.Dockerfile
|
||||
expose =
|
||||
CARGO_REPO_PORT=8443/tcp
|
||||
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/config.json || exit 1
|
||||
healthcheck_interval = 1
|
||||
healthcheck_retries = 30
|
||||
healthcheck_timeout = 2
|
||||
healthcheck_start_period = 1
|
||||
|
||||
[testenv:module-rust]
|
||||
package = skip
|
||||
change_dir = {tox_root}
|
||||
dependency_groups = dev
|
||||
docker =
|
||||
cargo-repo
|
||||
allowlist_externals =
|
||||
docker
|
||||
commands =
|
||||
python3 -m id_tools test render-config {tox_root}/tests/configs/Rust.yaml.j2 {env_dir}/configs/Rust.yaml
|
||||
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Rust.Dockerfile -t id-configurator-rust .
|
||||
docker rmi id-configurator-rust
|
||||
|
||||
Reference in New Issue
Block a user