Write the package credential only into a build that asks for it
A per-run .npmrc in every build context put a changing credential in COPY . . of modules that resolve no mesh package — a non-deterministic image (a needless rollout every build, which recreated the control plane) and a credential in a build stage. Now it is written only for a package artifact or an image whose Dockerfile names .npmrc. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
+29
-10
@@ -125,16 +125,19 @@ func Build(ctx context.Context, run Runner, publish Publisher,
|
||||
}
|
||||
say("manifest", "%s v%s — %d artifact(s)", manifest.Module, manifest.Version, artifactCount(manifest))
|
||||
|
||||
// A build-time credential, written where a build can mount it but never where it can be copied
|
||||
// into an image or committed: under the workspace, beside the clone, not inside it. Absent when
|
||||
// this mesh has no package registry yet, which is the bootstrap case (novox/hq ADR 0076).
|
||||
// A build-time credential, written into the build context as .npmrc, but ONLY for a module that
|
||||
// asks for it: a `package` artifact (which publishes), or an image whose Dockerfile COPYs .npmrc.
|
||||
// Writing it into every context would put a per-run credential in `COPY . .` of modules that
|
||||
// never resolve a mesh package — making their image non-deterministic (a needless rollout every
|
||||
// build) and leaking the credential into a build stage. Absent entirely with no registry, which
|
||||
// is the bootstrap case (novox/hq ADR 0076).
|
||||
var npmrcPath string
|
||||
if npmrc.Enabled() {
|
||||
if npmrc.Enabled() && manifest.Build != nil && wantsPackages(manifest, within) {
|
||||
content, err := npmrc.File()
|
||||
if err != nil {
|
||||
return Result{}, err
|
||||
}
|
||||
npmrcPath = filepath.Join(workspace, "npmrc")
|
||||
npmrcPath = filepath.Join(within, ".npmrc")
|
||||
if err := os.WriteFile(npmrcPath, []byte(content), 0o600); err != nil {
|
||||
return Result{}, fmt.Errorf("cannot write the package-registry credential for the build: %w", err)
|
||||
}
|
||||
@@ -297,6 +300,24 @@ func against(within string, manifest catalogue.Manifest) []string {
|
||||
// setting somebody has to find.
|
||||
const ManifestName = "module.json"
|
||||
|
||||
// wantsPackages reports whether this module's build resolves anything from the mesh's package
|
||||
// registry, so the credential is written into its context only then. A package artifact always
|
||||
// does; an image does when its Dockerfile names .npmrc — the file it would COPY to authenticate.
|
||||
func wantsPackages(manifest catalogue.Manifest, within string) bool {
|
||||
for _, a := range manifest.Build.Artifacts {
|
||||
switch a.Kind {
|
||||
case catalogue.ArtifactPackage:
|
||||
return true
|
||||
case catalogue.ArtifactImage:
|
||||
raw, err := os.ReadFile(filepath.Join(within, a.From))
|
||||
if err == nil && strings.Contains(string(raw), ".npmrc") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func one(ctx context.Context, run Runner, publish Publisher,
|
||||
module, tree, commit string, a catalogue.Artifact, args []string,
|
||||
held map[string]string, npmrc string, say func(step, format string, args ...any)) (catalogue.Built, error) {
|
||||
@@ -329,12 +350,10 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
||||
invocation = append(invocation, "--target", a.Target)
|
||||
}
|
||||
if npmrc != "" {
|
||||
// Given to the build as a buildkit secret, so a RUN that needs the package registry mounts
|
||||
// it at that step and it is in no image layer. A Dockerfile that does not ask for it is
|
||||
// unaffected; the secret is simply not read (novox/hq ADR 0076).
|
||||
invocation = append(invocation, "--secret", "id=npmrc,src="+npmrc)
|
||||
// Host network for the build, so a RUN reaching the package registry finds it where the
|
||||
// binding says it is — the machine's own loopback, where the registry answers.
|
||||
// binding says it is — the machine's own loopback, where the registry answers. The
|
||||
// credential itself is in the context as .npmrc, COPY'd by a stage that is not published;
|
||||
// buildkit is not required, because this machine's docker may not carry buildx.
|
||||
invocation = append(invocation, "--network", "host")
|
||||
}
|
||||
invocation = append(invocation, ".")
|
||||
|
||||
Reference in New Issue
Block a user