|
|
|
@@ -18,11 +18,13 @@ package main
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"net/url"
|
|
|
|
|
"os"
|
|
|
|
|
"os/signal"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
|
|
|
@@ -54,6 +56,8 @@ is dialled except the broker.
|
|
|
|
|
MESH_BINDING a file the mesh wrote saying where the artifact store is
|
|
|
|
|
MESH_PACKAGE_BINDING a file the mesh wrote saying where the package registry is
|
|
|
|
|
MESH_NPM_TOKEN_FILE a file the mesh sealed holding the token for it
|
|
|
|
|
MESH_GENESIS_PACKAGE_REGISTRY the document the installer sealed at genesis, read while no
|
|
|
|
|
binding is present (novox/hq ADR 0336)
|
|
|
|
|
MESH_NPM_REGISTRY a package registry URL, when the mesh has not said (a person, the bootstrap)
|
|
|
|
|
MESH_NPM_TOKEN the token for it, likewise
|
|
|
|
|
MESH_NPM_SCOPE the scope it answers for (default: @novox)
|
|
|
|
@@ -108,6 +112,9 @@ func run() error {
|
|
|
|
|
if err := notReady(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// **The builder's own version is part of every reuse key** (novox/hq ADR 0350 §1): a builder that builds
|
|
|
|
|
// differently never takes a build another one made.
|
|
|
|
|
builder.Version = builder.SelfVersion(version)
|
|
|
|
|
if err := undrained(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
@@ -193,6 +200,9 @@ func run() error {
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "building for the mesh, publishing to %s\n", registry)
|
|
|
|
|
publisher := builder.Registry{Address: registry, Run: builder.Command}
|
|
|
|
|
// Check builds are let go of after a week (novox/hq ADR 0350 §5).
|
|
|
|
|
h.scratch = publisher
|
|
|
|
|
go h.expireChecks(ctx, time.Hour, time.Now)
|
|
|
|
|
|
|
|
|
|
return machine.Take(ctx, func(ctx context.Context, work link.Build) {
|
|
|
|
|
defer released()
|
|
|
|
@@ -279,7 +289,11 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri
|
|
|
|
|
}
|
|
|
|
|
say("build", what)
|
|
|
|
|
|
|
|
|
|
npmrc, err := packagesFrom()
|
|
|
|
|
npmrc, err := packagesFrom(on)
|
|
|
|
|
if err == nil {
|
|
|
|
|
say("packages", saysRegistry(npmrc))
|
|
|
|
|
}
|
|
|
|
|
registryHere, isRegistry := publisher.(builder.Registry)
|
|
|
|
|
var built builder.Result
|
|
|
|
|
if request.Check != nil {
|
|
|
|
|
// **A pull request's merge check, not a build** (novox/hq to-be 45 §9): nothing is built,
|
|
|
|
@@ -289,12 +303,21 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri
|
|
|
|
|
if r, ok := publisher.(builder.Registry); ok {
|
|
|
|
|
registry = r.Address
|
|
|
|
|
}
|
|
|
|
|
spec := checkSpecOf(request)
|
|
|
|
|
// What the merge would move, built by the check for the walk to take (novox/hq ADR 0350 §1): into the
|
|
|
|
|
// scratch namespace. **Never with the package registry's credential**: this is code nobody has approved
|
|
|
|
|
// yet, and a recipe could read the credential it is handed (issue 462). A module that resolves packages
|
|
|
|
|
// at build time has no reuse key anyway, so nothing is lost by refusing it here.
|
|
|
|
|
if isRegistry && len(spec.Builds) > 0 {
|
|
|
|
|
spec.BuildWith = &builder.CheckBuilding{Publish: publisher, Scratch: registryHere, Held: request.Held,
|
|
|
|
|
Seats: request.Seats}
|
|
|
|
|
}
|
|
|
|
|
var v builder.CheckVerdict
|
|
|
|
|
v, err = builder.Check(building, builder.Command, checkSpecOf(request), workspace, registry, forgeFrom(), say)
|
|
|
|
|
v, err = builder.Check(building, builder.Command, spec, workspace, registry, forgeFrom(), say)
|
|
|
|
|
if err == nil {
|
|
|
|
|
result.Check = &link.CheckOutcome{Verdict: v.Verdict, Summary: v.Summary, Report: v.Report,
|
|
|
|
|
Took: v.Took.Round(time.Second).String(), Gate: layerOf(v.Gate), RepoCheck: layerOf(v.Repo),
|
|
|
|
|
Head: v.Head, Trunk: v.Trunk, Beside: v.Beside}
|
|
|
|
|
Head: v.Head, Trunk: v.Trunk, Beside: v.Beside, Built: checkBuiltOf(v.Built)}
|
|
|
|
|
// What each of a group's heads was judged as (novox/hq ADR 0323 §6).
|
|
|
|
|
for _, m := range v.Members {
|
|
|
|
|
result.Check.Members = append(result.Check.Members, link.CheckedMember{Owner: m.Owner, Repo: m.Repo,
|
|
|
|
@@ -307,9 +330,30 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri
|
|
|
|
|
// after a clone that then fails at npm ci.
|
|
|
|
|
// Every container it starts is labelled with its id, so a kill finds what outlived the
|
|
|
|
|
// docker client (ADR 0219).
|
|
|
|
|
built, err = builder.Build(building, builder.Labelled(builder.Command, request.ID), publisher,
|
|
|
|
|
request.Repository, request.Path, request.Ref, workspace, request.Held, npmrc,
|
|
|
|
|
forgeFrom(), say, request.Seats)
|
|
|
|
|
if request.Reuse != nil && isRegistry && !request.DryRun {
|
|
|
|
|
// **A walk's build takes the check's build of the same key** (novox/hq ADR 0350 §2).
|
|
|
|
|
reuse := builder.Reuse{Scratch: registryHere}
|
|
|
|
|
for _, p := range request.Reuse.Pulls {
|
|
|
|
|
reuse.Pulls = append(reuse.Pulls, builder.PullRef{Owner: p.Owner, Repo: p.Repo, Number: p.Number})
|
|
|
|
|
}
|
|
|
|
|
// Only what a verdict names is taken (ADR 0350 §2).
|
|
|
|
|
for _, b := range request.Reuse.Builds {
|
|
|
|
|
named := builder.NamedBuild{Pull: builder.PullRef{Owner: b.Pull.Owner, Repo: b.Pull.Repo,
|
|
|
|
|
Number: b.Pull.Number}, Check: b.Check, Module: b.Built.Module, Key: b.Built.Key,
|
|
|
|
|
Digests: map[string]string{}}
|
|
|
|
|
for _, m := range b.Built.Made {
|
|
|
|
|
named.Digests[m.Name] = builder.DigestOf(m.Reference)
|
|
|
|
|
}
|
|
|
|
|
reuse.Named = append(reuse.Named, named)
|
|
|
|
|
}
|
|
|
|
|
built, err = builder.BuildReusing(building, builder.Labelled(builder.Command, request.ID), publisher, reuse,
|
|
|
|
|
request.Repository, request.Path, request.Ref, workspace, request.Held, npmrc,
|
|
|
|
|
forgeFrom(), say, request.Seats)
|
|
|
|
|
} else {
|
|
|
|
|
built, err = builder.Build(building, builder.Labelled(builder.Command, request.ID), publisher,
|
|
|
|
|
request.Repository, request.Path, request.Ref, workspace, request.Held, npmrc,
|
|
|
|
|
forgeFrom(), say, request.Seats)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// What it copied into the store, whatever became of the build (novox/hq ADR 0257).
|
|
|
|
|
result.Mirrored = built.Mirrored
|
|
|
|
@@ -345,6 +389,19 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri
|
|
|
|
|
}
|
|
|
|
|
result.Against = built.Against
|
|
|
|
|
result.SourceFingerprint = built.Source
|
|
|
|
|
// What the walk did instead of building, or why it built (novox/hq ADR 0350 §3, §4).
|
|
|
|
|
result.ReuseKey = built.ReuseKey
|
|
|
|
|
if built.Reused != nil {
|
|
|
|
|
f := built.Reused.From
|
|
|
|
|
result.Reused = built.Reused.Words()
|
|
|
|
|
result.ReusedFrom = &link.ReusedFrom{Pull: link.PullRef{Owner: f.Pull.Owner, Repo: f.Pull.Repo,
|
|
|
|
|
Number: f.Pull.Number}, Head: f.Head, Trunk: f.Trunk, Check: f.Check, Key: f.Key}
|
|
|
|
|
} else if request.Reuse != nil {
|
|
|
|
|
result.BuiltWhy = built.Why
|
|
|
|
|
if result.BuiltWhy == "" {
|
|
|
|
|
result.BuiltWhy = "it has no artifact to build"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result.Trunk, result.OnTrunk, result.Branches = built.Trunk, built.OnTrunk, built.Branches
|
|
|
|
|
for _, r := range built.Read {
|
|
|
|
|
result.Read = append(result.Read, link.ReadRepository{Repository: r.Repository, Ref: r.Ref})
|
|
|
|
@@ -395,6 +452,10 @@ func checkSpecOf(request link.BuildRequest) builder.CheckSpec {
|
|
|
|
|
for dir, b := range c.Beside {
|
|
|
|
|
spec.Beside[dir] = builder.Beside{Repository: b.Repository, Ref: b.Ref}
|
|
|
|
|
}
|
|
|
|
|
for _, b := range c.Builds {
|
|
|
|
|
spec.Builds = append(spec.Builds, builder.CheckBuildSpec{Module: b.Module, Repository: b.Repository, Path: b.Path,
|
|
|
|
|
Ref: b.Ref, Merged: b.Merged, Tier: b.Tier, Skip: b.Skip})
|
|
|
|
|
}
|
|
|
|
|
for _, m := range c.Members {
|
|
|
|
|
spec.Group = append(spec.Group, builder.GroupHead{Owner: m.Owner, Repo: m.Repo, Repository: m.Repository,
|
|
|
|
|
Ref: m.Ref, Paths: m.Paths, Base: m.Base})
|
|
|
|
@@ -402,6 +463,19 @@ func checkSpecOf(request link.BuildRequest) builder.CheckSpec {
|
|
|
|
|
return spec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checkBuiltOf is a check's builds for its merge as the outcome carries them (novox/hq ADR 0350 §1).
|
|
|
|
|
func checkBuiltOf(built []builder.CheckBuilt) []link.CheckBuilt {
|
|
|
|
|
var out []link.CheckBuilt
|
|
|
|
|
for _, b := range built {
|
|
|
|
|
c := link.CheckBuilt{Module: b.Module, Key: b.Key, NotBuilt: b.NotBuilt, Took: b.Took.Round(time.Second).String()}
|
|
|
|
|
for _, m := range b.Made {
|
|
|
|
|
c.Made = append(c.Made, link.MadeArtifact{Name: m.Name, Kind: m.Kind, Reference: m.Reference})
|
|
|
|
|
}
|
|
|
|
|
out = append(out, c)
|
|
|
|
|
}
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// layerOf is one layer of a check as the outcome carries it.
|
|
|
|
|
func layerOf(l *builder.Layer) *link.CheckLayer {
|
|
|
|
|
if l == nil {
|
|
|
|
@@ -411,79 +485,171 @@ func layerOf(l *builder.Layer) *link.CheckLayer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// packagesFrom is where a build resolves the mesh's own published packages — the SDK above all
|
|
|
|
|
// (novox/hq ADR 0076, issue 053).
|
|
|
|
|
// (novox/hq ADR 0076, issue 053) — on this machine.
|
|
|
|
|
//
|
|
|
|
|
// Preferably from the mesh: a package-registry binding names the endpoint the way the artifact
|
|
|
|
|
// store's binding does, and a sealed token file the credential the way the broker's does. The
|
|
|
|
|
// environment variables remain for a builder run by a person, and for the bootstrap, where there is
|
|
|
|
|
// no registry yet — there the result is disabled and a build that needs no mesh-published dependency
|
|
|
|
|
// builds anyway.
|
|
|
|
|
func packagesFrom() (builder.Npmrc, error) {
|
|
|
|
|
// **The binding wins, then the genesis document** (novox/hq ADR 0336 §3, §4). The build agent's
|
|
|
|
|
// npm-package-registry requirement is optional: on genesis's node, before the gitea module is assigned,
|
|
|
|
|
// nothing provides it, and the installer sealed the forge it raised to this agent as one document
|
|
|
|
|
// instead. Once the requirement binds, the binding names the endpoint the way the artifact store's
|
|
|
|
|
// does and a sealed token file the credential, and the genesis document is left unread. A binding
|
|
|
|
|
// file named and absent is a requirement not bound here, never a fault: the environment names the
|
|
|
|
|
// path whether or not the mesh wrote it.
|
|
|
|
|
//
|
|
|
|
|
// The environment variables remain for a builder run by a person. With none of the three the result
|
|
|
|
|
// is disabled and says this machine, so a build that needs the registry is refused naming it (§6),
|
|
|
|
|
// and a build that needs none builds anyway.
|
|
|
|
|
func packagesFrom(machine string) (builder.Npmrc, error) {
|
|
|
|
|
scope := strings.TrimSpace(os.Getenv("MESH_NPM_SCOPE"))
|
|
|
|
|
if scope == "" {
|
|
|
|
|
scope = "@novox"
|
|
|
|
|
}
|
|
|
|
|
none := builder.Npmrc{Machine: machine}
|
|
|
|
|
|
|
|
|
|
registry := strings.TrimSpace(os.Getenv("MESH_NPM_REGISTRY"))
|
|
|
|
|
var username string
|
|
|
|
|
if path := strings.TrimSpace(os.Getenv("MESH_PACKAGE_BINDING")); path != "" {
|
|
|
|
|
raw, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf("cannot read what the mesh said about the package registry: %w", err)
|
|
|
|
|
switch {
|
|
|
|
|
case errors.Is(err, os.ErrNotExist):
|
|
|
|
|
// Not bound on this machine: the genesis document, or nothing.
|
|
|
|
|
case err != nil:
|
|
|
|
|
return none, fmt.Errorf("cannot read what the mesh said about the package registry: %w", err)
|
|
|
|
|
default:
|
|
|
|
|
return fromBinding(path, raw, scope, machine)
|
|
|
|
|
}
|
|
|
|
|
var told struct {
|
|
|
|
|
From string `json:"from"`
|
|
|
|
|
At string `json:"at"`
|
|
|
|
|
As string `json:"as"`
|
|
|
|
|
Serves map[string]any `json:"serves"`
|
|
|
|
|
}
|
|
|
|
|
if path := strings.TrimSpace(os.Getenv("MESH_GENESIS_PACKAGE_REGISTRY")); path != "" {
|
|
|
|
|
raw, err := os.ReadFile(path)
|
|
|
|
|
switch {
|
|
|
|
|
case errors.Is(err, os.ErrNotExist):
|
|
|
|
|
// Not this mesh's genesis node, or the installer has not sealed it yet.
|
|
|
|
|
case err != nil:
|
|
|
|
|
return none, fmt.Errorf("cannot read the genesis document for the package registry: %w", err)
|
|
|
|
|
default:
|
|
|
|
|
return fromGenesis(path, raw, scope, machine)
|
|
|
|
|
}
|
|
|
|
|
if err := json.Unmarshal(raw, &told); err != nil {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf("%s is not a binding: %w", path, err)
|
|
|
|
|
}
|
|
|
|
|
if told.At == "" {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf(
|
|
|
|
|
"%s says the package registry is on %q and gives no address for it", path, told.From)
|
|
|
|
|
}
|
|
|
|
|
// Composed from what the provider serves, so nothing here knows gitea's URL shape from
|
|
|
|
|
// another registry's: it states its port, the path its registry answers on, and the scheme.
|
|
|
|
|
scheme := "https"
|
|
|
|
|
if s, ok := told.Serves["scheme"]; ok {
|
|
|
|
|
scheme = fmt.Sprintf("%v", s)
|
|
|
|
|
}
|
|
|
|
|
port, ok := told.Serves["port"]
|
|
|
|
|
if !ok {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf("%s says nothing about which port the package registry answers on", path)
|
|
|
|
|
}
|
|
|
|
|
npmPath, ok := told.Serves["npm-path"]
|
|
|
|
|
if !ok {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf("%s says nothing about the path the package registry answers on", path)
|
|
|
|
|
}
|
|
|
|
|
registry = fmt.Sprintf("%s://%s:%v%v", scheme, told.At, port, npmPath)
|
|
|
|
|
username = told.As
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The credential the mesh sealed to this machine. The mesh authenticates the ordinary way — a
|
|
|
|
|
// generated password the provider only applies (novox/hq ADR 0048) — so with a username this is
|
|
|
|
|
// a password (basic auth); without one it is a bearer token a provider minted.
|
|
|
|
|
// A builder run by a person.
|
|
|
|
|
registry := strings.TrimSpace(os.Getenv("MESH_NPM_REGISTRY"))
|
|
|
|
|
secret := strings.TrimSpace(os.Getenv("MESH_NPM_TOKEN"))
|
|
|
|
|
if path := strings.TrimSpace(os.Getenv("MESH_NPM_TOKEN_FILE")); path != "" {
|
|
|
|
|
raw, err := os.ReadFile(path)
|
|
|
|
|
username := strings.TrimSpace(os.Getenv("MESH_NPM_USER"))
|
|
|
|
|
if registry == "" && secret == "" {
|
|
|
|
|
return none, nil
|
|
|
|
|
}
|
|
|
|
|
n := builder.Npmrc{Scope: scope, Registry: registry, From: builder.FromEnvironment, Machine: machine}
|
|
|
|
|
if username != "" {
|
|
|
|
|
n.Username, n.Password = username, secret
|
|
|
|
|
} else {
|
|
|
|
|
n.Token = secret
|
|
|
|
|
}
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fromBinding composes the registry from the npm-package-registry binding the mesh wrote and the
|
|
|
|
|
// credential it sealed beside it. The mesh authenticates the ordinary way — a generated password the
|
|
|
|
|
// provider only applies (novox/hq ADR 0048) — so with a username the secret is a password (basic
|
|
|
|
|
// auth); without one it is a bearer token a provider minted.
|
|
|
|
|
func fromBinding(path string, raw []byte, scope, machine string) (builder.Npmrc, error) {
|
|
|
|
|
none := builder.Npmrc{Machine: machine}
|
|
|
|
|
var told struct {
|
|
|
|
|
From string `json:"from"`
|
|
|
|
|
At string `json:"at"`
|
|
|
|
|
As string `json:"as"`
|
|
|
|
|
Serves map[string]any `json:"serves"`
|
|
|
|
|
}
|
|
|
|
|
if err := json.Unmarshal(raw, &told); err != nil {
|
|
|
|
|
return none, fmt.Errorf("%s is not a binding: %w", path, err)
|
|
|
|
|
}
|
|
|
|
|
if told.At == "" {
|
|
|
|
|
return none, fmt.Errorf(
|
|
|
|
|
"%s says the package registry is on %q and gives no address for it", path, told.From)
|
|
|
|
|
}
|
|
|
|
|
// Composed from what the provider serves, so nothing here knows gitea's URL shape from
|
|
|
|
|
// another registry's: it states its port, the path its registry answers on, and the scheme.
|
|
|
|
|
scheme := "https"
|
|
|
|
|
if s, ok := told.Serves["scheme"]; ok {
|
|
|
|
|
scheme = fmt.Sprintf("%v", s)
|
|
|
|
|
}
|
|
|
|
|
port, ok := told.Serves["port"]
|
|
|
|
|
if !ok {
|
|
|
|
|
return none, fmt.Errorf("%s says nothing about which port the package registry answers on", path)
|
|
|
|
|
}
|
|
|
|
|
npmPath, ok := told.Serves["npm-path"]
|
|
|
|
|
if !ok {
|
|
|
|
|
return none, fmt.Errorf("%s says nothing about the path the package registry answers on", path)
|
|
|
|
|
}
|
|
|
|
|
secret := strings.TrimSpace(os.Getenv("MESH_NPM_TOKEN"))
|
|
|
|
|
if file := strings.TrimSpace(os.Getenv("MESH_NPM_TOKEN_FILE")); file != "" {
|
|
|
|
|
raw, err := os.ReadFile(file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return builder.Npmrc{}, fmt.Errorf("cannot read this builder's package-registry credential: %w", err)
|
|
|
|
|
return none, fmt.Errorf("cannot read this builder's package-registry credential: %w", err)
|
|
|
|
|
}
|
|
|
|
|
secret = strings.TrimSpace(string(raw))
|
|
|
|
|
}
|
|
|
|
|
if u := strings.TrimSpace(os.Getenv("MESH_NPM_USER")); u != "" {
|
|
|
|
|
username = u
|
|
|
|
|
n := builder.Npmrc{Scope: scope, Registry: fmt.Sprintf("%s://%s:%v%v", scheme, told.At, port, npmPath),
|
|
|
|
|
From: builder.FromBinding, Machine: machine}
|
|
|
|
|
if told.As != "" {
|
|
|
|
|
n.Username, n.Password = told.As, secret
|
|
|
|
|
} else {
|
|
|
|
|
n.Token = secret
|
|
|
|
|
}
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if registry == "" && secret == "" {
|
|
|
|
|
return builder.Npmrc{}, nil
|
|
|
|
|
// GenesisRegistry is the genesis document (novox/hq ADR 0336 §3): what the installer sealed to the
|
|
|
|
|
// build agent on its node at genesis's packages step, as the `genesis-package-registry` own-secret —
|
|
|
|
|
// the forge it raised, the port this node gave the package registry, its npm path, and the account it
|
|
|
|
|
// made with that account's password. mesh-host writes it; the field names are the two repositories'
|
|
|
|
|
// agreement.
|
|
|
|
|
type GenesisRegistry struct {
|
|
|
|
|
Scheme string `json:"scheme"`
|
|
|
|
|
At string `json:"at"`
|
|
|
|
|
Port int `json:"port"`
|
|
|
|
|
NpmPath string `json:"npm-path"`
|
|
|
|
|
As string `json:"as"`
|
|
|
|
|
Password string `json:"password"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fromGenesis composes the registry from the genesis document.
|
|
|
|
|
func fromGenesis(path string, raw []byte, scope, machine string) (builder.Npmrc, error) {
|
|
|
|
|
none := builder.Npmrc{Machine: machine}
|
|
|
|
|
var g GenesisRegistry
|
|
|
|
|
if err := json.Unmarshal(raw, &g); err != nil {
|
|
|
|
|
return none, fmt.Errorf("%s is not the genesis document for the package registry: %w", path, err)
|
|
|
|
|
}
|
|
|
|
|
if username != "" {
|
|
|
|
|
return builder.Npmrc{Scope: scope, Registry: registry, Username: username, Password: secret}, nil
|
|
|
|
|
var missing []string
|
|
|
|
|
for name, value := range map[string]string{"at": g.At, "npm-path": g.NpmPath, "as": g.As, "password": g.Password} {
|
|
|
|
|
if strings.TrimSpace(value) == "" {
|
|
|
|
|
missing = append(missing, name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return builder.Npmrc{Scope: scope, Registry: registry, Token: secret}, nil
|
|
|
|
|
if g.Port <= 0 {
|
|
|
|
|
missing = append(missing, "port")
|
|
|
|
|
}
|
|
|
|
|
if len(missing) > 0 {
|
|
|
|
|
sort.Strings(missing)
|
|
|
|
|
return none, fmt.Errorf("%s, the genesis document for the package registry, says nothing of %s",
|
|
|
|
|
path, strings.Join(missing, ", "))
|
|
|
|
|
}
|
|
|
|
|
scheme := g.Scheme
|
|
|
|
|
if scheme == "" {
|
|
|
|
|
scheme = "http"
|
|
|
|
|
}
|
|
|
|
|
return builder.Npmrc{Scope: scope, Registry: fmt.Sprintf("%s://%s:%d%s", scheme, g.At, g.Port, g.NpmPath),
|
|
|
|
|
Username: g.As, Password: g.Password, From: builder.FromGenesis, Machine: machine}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// saysRegistry is the line each build's log carries about the package registry (novox/hq ADR 0336
|
|
|
|
|
// §4): which of the binding and the genesis document it resolved from, or that it has neither here.
|
|
|
|
|
func saysRegistry(n builder.Npmrc) string {
|
|
|
|
|
if n.Enabled() {
|
|
|
|
|
return "the package registry is resolved from " + n.From
|
|
|
|
|
}
|
|
|
|
|
machine := n.Machine
|
|
|
|
|
if machine == "" {
|
|
|
|
|
machine = "this machine"
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("no package registry: %s is unbound on %s and no genesis document is here; "+
|
|
|
|
|
"a build needing it is refused", builder.RegistryRequirement, machine)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// forgeFrom is the git credential this builder may offer a clone, composed from the same binding
|
|
|
|
|