Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e223f7d327 | ||
|
|
30df055e39 | ||
|
|
5393c4405a | ||
|
|
cc532b914c | ||
|
|
c09eff2214 | ||
|
|
d7c9416713 | ||
|
|
238079da66 |
+8
-2
@@ -1,10 +1,16 @@
|
|||||||
// Ignore everything
|
# Ignore everything
|
||||||
*
|
*
|
||||||
|
|
||||||
// Allow what is needed
|
# Allow what is needed
|
||||||
!crates
|
!crates
|
||||||
!tests
|
!tests
|
||||||
!resources
|
!resources
|
||||||
|
|
||||||
|
# The patched dependency Cargo.toml's [patch.crates-io] points at. Without
|
||||||
|
# it the build context has no vendor/, and `cargo chef cook` fails on
|
||||||
|
# "failed to load source for dependency sieve-rs" -- which CI cannot see,
|
||||||
|
# because CI builds from a checkout and only the image build has a context.
|
||||||
|
!vendor
|
||||||
|
|
||||||
!Cargo.lock
|
!Cargo.lock
|
||||||
!Cargo.toml
|
!Cargo.toml
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ jobs:
|
|||||||
- run: python3 tools/fork/name-check.py
|
- run: python3 tools/fork/name-check.py
|
||||||
- if: always()
|
- if: always()
|
||||||
run: python3 tools/fork/notice-check.py
|
run: python3 tools/fork/notice-check.py
|
||||||
|
# Cargo can patch a dependency to a directory in this repository, and
|
||||||
|
# the image builds from a context .dockerignore prunes to almost
|
||||||
|
# nothing. CI never sees the difference; a release does.
|
||||||
|
- if: always()
|
||||||
|
run: python3 tools/fork/context-check.py
|
||||||
|
|
||||||
build:
|
build:
|
||||||
# Either runner (host1 or host2): the build needs no docker socket.
|
# Either runner (host1 or host2): the build needs no docker socket.
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
|||||||
g++-x86-64-linux-gnu binutils-x86-64-linux-gnu
|
g++-x86-64-linux-gnu binutils-x86-64-linux-gnu
|
||||||
RUN rustup target add "$(cat /target.txt)"
|
RUN rustup target add "$(cat /target.txt)"
|
||||||
COPY --from=planner /recipe.json /recipe.json
|
COPY --from=planner /recipe.json /recipe.json
|
||||||
|
# inbuxa: [patch.crates-io] points sieve-rs at vendor/, and the recipe only
|
||||||
|
# carries the workspace's own manifests, so cooking the dependencies needs the
|
||||||
|
# vendored crate itself (the context allows it since #27; this puts it here).
|
||||||
|
COPY vendor/ vendor/
|
||||||
RUN RUSTFLAGS="$(cat /flags.txt)" cargo chef cook --target "$(cat /target.txt)" --release --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats" --recipe-path /recipe.json
|
RUN RUSTFLAGS="$(cat /flags.txt)" cargo chef cook --target "$(cat /target.txt)" --release --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats" --recipe-path /recipe.json
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN RUSTFLAGS="$(cat /flags.txt)" cargo build --target "$(cat /target.txt)" --release -p inbuxa --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats"
|
RUN RUSTFLAGS="$(cat /flags.txt)" cargo build --target "$(cat /target.txt)" --release -p inbuxa --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats"
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ fn legacy_setting(name: &str, is_set: impl Fn(&str) -> bool) -> Option<String> {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! brand_version {
|
macro_rules! brand_version {
|
||||||
() => {
|
() => {
|
||||||
"2026.9.24"
|
"2026.9.24.3"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Executable
+120
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-FileCopyrightText: 2026 Coffey Labs
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Every path Cargo patches has to be in the image's build context.
|
||||||
|
|
||||||
|
Cargo.toml's [patch.crates-io] can point at a directory in this repository,
|
||||||
|
and the Dockerfile builds from a context that .dockerignore prunes to almost
|
||||||
|
nothing. Those two facts met on 2026-09-23: a vendored, patched sieve-rs
|
||||||
|
landed, CI stayed green -- it builds from a checkout, where the directory is
|
||||||
|
simply there -- and the release build failed on
|
||||||
|
|
||||||
|
failed to load source for dependency `sieve-rs`
|
||||||
|
failed to read /build/vendor/sieve-rs/Cargo.toml
|
||||||
|
|
||||||
|
after a tag had already been pushed. This is seconds, and it runs beside the
|
||||||
|
other fork checks rather than waiting for a release to find out.
|
||||||
|
|
||||||
|
Being in the context isn't enough on its own: the Dockerfile cooks the
|
||||||
|
dependencies (`cargo chef cook`) before it copies the tree in, from a recipe
|
||||||
|
that carries only the workspace's manifests. So each patched path must also be
|
||||||
|
copied into that stage before the cook step, or the same error comes back
|
||||||
|
there -- as it did for 2026.9.24.2, the first tag after the context fix.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
root = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def patched_paths(manifest: Path) -> list[str]:
|
||||||
|
"""Directories named by a [patch...] section's `path = "..."` entries."""
|
||||||
|
out, in_patch = [], False
|
||||||
|
for line in manifest.read_text().splitlines():
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.startswith("["):
|
||||||
|
in_patch = stripped.startswith("[patch")
|
||||||
|
continue
|
||||||
|
if not in_patch:
|
||||||
|
continue
|
||||||
|
m = re.search(r'path\s*=\s*"([^"]+)"', stripped)
|
||||||
|
if m:
|
||||||
|
out.append(m.group(1))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def allowed(dockerignore: Path) -> set[str]:
|
||||||
|
"""The first path segment of every re-inclusion rule."""
|
||||||
|
keep = set()
|
||||||
|
for line in dockerignore.read_text().splitlines():
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.startswith("!"):
|
||||||
|
keep.add(stripped[1:].strip("/").split("/")[0])
|
||||||
|
return keep
|
||||||
|
|
||||||
|
|
||||||
|
def copied_before_cook(dockerfile: Path) -> list[str] | None:
|
||||||
|
"""Sources COPY'd into the stage that runs `cargo chef cook`, before it.
|
||||||
|
|
||||||
|
None when no stage cooks. A `COPY . .` covers everything.
|
||||||
|
"""
|
||||||
|
stage: list[str] = []
|
||||||
|
for line in dockerfile.read_text().splitlines():
|
||||||
|
stripped = line.strip()
|
||||||
|
if re.match(r"(?i)^FROM\s", stripped):
|
||||||
|
stage = []
|
||||||
|
continue
|
||||||
|
if "cargo chef cook" in stripped:
|
||||||
|
return stage
|
||||||
|
m = re.match(r"(?i)^COPY\s+(?!--from)(.+)$", stripped)
|
||||||
|
if m:
|
||||||
|
parts = m.group(1).split()
|
||||||
|
stage.extend(p.strip("./").split("/")[0] or "." for p in parts[:-1])
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
paths = patched_paths(root / "Cargo.toml")
|
||||||
|
if not paths:
|
||||||
|
print("no patched paths to check")
|
||||||
|
return 0
|
||||||
|
keep = allowed(root / ".dockerignore")
|
||||||
|
bad = []
|
||||||
|
for p in paths:
|
||||||
|
top = p.strip("/").split("/")[0]
|
||||||
|
if top not in keep:
|
||||||
|
bad.append((p, top))
|
||||||
|
elif not (root / p).is_dir():
|
||||||
|
bad.append((p, None))
|
||||||
|
for path, top in bad:
|
||||||
|
if top is None:
|
||||||
|
print(f"Cargo.toml patches {path}, which does not exist", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Cargo.toml patches {path}, but .dockerignore does not re-include {top!r}:\n"
|
||||||
|
f" the image build would not see it, and cargo would fail on it.\n"
|
||||||
|
f" Add `!{top}` to .dockerignore.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
copied = copied_before_cook(root / "Dockerfile")
|
||||||
|
if copied is not None and "." not in copied:
|
||||||
|
for p in paths:
|
||||||
|
top = p.strip("/").split("/")[0]
|
||||||
|
if top not in copied:
|
||||||
|
print(
|
||||||
|
f"Cargo.toml patches {p}, but the Dockerfile doesn't copy {top!r} into the\n"
|
||||||
|
f" stage that runs `cargo chef cook` before that step, so cooking the\n"
|
||||||
|
f" dependencies fails on it. Add `COPY {top}/ {top}/` before the cook.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
bad.append((p, top))
|
||||||
|
if bad:
|
||||||
|
return 1
|
||||||
|
print(f"build context and cook stage include every patched path: {', '.join(paths)}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user