Give a group's TypeScript members the mesh-sdk member's package when its merge changes the package's paths, and pin the SDK's R4e head (ADR 0500 §4-§5, issue 1309's checker half)
A TypeScript member of a group that changes mesh-sdk's package resolved the old version from the registry. Its Node fetch now names the mesh-sdk member's head, which the Node build module packs and serves, publishing nothing. The paths come from the indexer's modules value (lead decision L8), so a change of Go files or of the manifest alone resolves the registry as before. A test holds that no step of a check carries publish.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -649,3 +650,76 @@ func TestADigestThatEchoesOtherSiblingsKeepsNothing(t *testing.T) { // 1287 with
|
||||
t.Errorf("kept %v under a digest that does not cover every sibling", keys)
|
||||
}
|
||||
}
|
||||
|
||||
type pkgPaths map[string][]string
|
||||
|
||||
func (p pkgPaths) PackagePaths(repository string) []string { return p[repository] }
|
||||
|
||||
// groupPackageBench is a group of a TypeScript member (mesh-delivery) and the mesh-sdk member, whose merge changes sdkChanged.
|
||||
func groupPackageBench(t *testing.T, sdkChanged []string) *bench {
|
||||
b := newBench(t)
|
||||
b.ex.Modules = langs{repoA: {buildrun.GoBuildModule, buildrun.NodeBuildModule}, repoSDK: {buildrun.GoBuildModule, buildrun.NodeBuildModule}}
|
||||
b.ex.Packages = pkgPaths{repoSDK: {"src", "test", "package.json", "package-lock.json", "tsconfig.json"}}
|
||||
fetch := func(c stepCall) (any, error) {
|
||||
f := b.fetch
|
||||
if c.args["repository"] == repoSDK {
|
||||
f.Changed = sdkChanged
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
b.steps.handlers["build_go_fetch"], b.steps.handlers["build_node_fetch"] = fetch, fetch
|
||||
return b
|
||||
}
|
||||
|
||||
func nodeFetchOf(b *bench, repo string) stepCall {
|
||||
for _, c := range b.steps.tool("fetch") {
|
||||
if c.module == buildrun.NodeBuildModule && c.args["repository"] == repo {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return stepCall{}
|
||||
}
|
||||
|
||||
func TestATypeScriptMemberTakesTheGroupsPackageWhenTheSDKMemberChangesItsPaths(t *testing.T) { // ADR 0500 §5, C11b
|
||||
b := groupPackageBench(t, []string{"go/delivery/x.go", "src/state.ts"})
|
||||
b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2)))
|
||||
want := map[string]any{"repository": repoSDK, "commit": head2, "ref": "refs/pull/12/head"}
|
||||
if got := nodeFetchOf(b, repoA).args["package"]; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("mesh-delivery's Node fetch was given the package %v, want %v", got, want)
|
||||
}
|
||||
if got := nodeFetchOf(b, repoSDK).args["package"]; got != nil {
|
||||
t.Errorf("the mesh-sdk member's own fetch was given %v: it builds its package itself", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestATypeScriptMemberResolvesTheRegistryWhenTheSDKMemberLeavesItsPaths(t *testing.T) { // ADR 0500 §5: the manifest alone, or Go alone
|
||||
for _, changed := range [][]string{{"go/delivery/x.go"}, {"module.json"}, {"srcx/a.ts"}} {
|
||||
b := groupPackageBench(t, changed)
|
||||
b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2)))
|
||||
if got := nodeFetchOf(b, repoA).args["package"]; got != nil {
|
||||
t.Errorf("%v: the Node fetch was given %v, want the registry", changed, got)
|
||||
}
|
||||
}
|
||||
// A lone TypeScript pull request: no group package either.
|
||||
b := groupPackageBench(t, []string{"src/state.ts"})
|
||||
b.judge(t, caseOf(t, pullOf(repoA, 7, head1)))
|
||||
if got := nodeFetchOf(b, repoA).args["package"]; got != nil {
|
||||
t.Errorf("a lone pull request's Node fetch was given %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoStepOfACheckCarriesPublish(t *testing.T) { // ADR 0500 §4, 1309 (the checker's half): only the builder's trunk build publishes
|
||||
b := groupPackageBench(t, []string{"src/state.ts"})
|
||||
b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2)))
|
||||
if len(b.steps.calls) == 0 {
|
||||
t.Fatal("no step ran")
|
||||
}
|
||||
for _, c := range b.steps.calls {
|
||||
if _, ok := c.args["publish"]; ok {
|
||||
t.Errorf("%s was given publish: %v", c.tool, c.args)
|
||||
}
|
||||
if strings.HasSuffix(c.tool, "_build") || strings.HasSuffix(c.tool, "_pack") {
|
||||
t.Errorf("the checker asked %s: a check builds no artifact of its own", c.tool)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ type Examiner struct {
|
||||
Builds BuildStates
|
||||
// Announce says a preview was written under the key (EventPreviewWritten); nil says nothing.
|
||||
Announce Announcer
|
||||
// Packages names, of a repository, the paths (repository-relative) its TypeScript package artifacts are built from, from
|
||||
// the indexer's modules (delivery.Module.ArtifactPaths, lead decision L8); nil serves no group package.
|
||||
Packages interface{ PackagePaths(repository string) []string }
|
||||
// Trunks are the trunk heads of every repository, for the siblings a repository's tests read.
|
||||
Trunks interface{ Heads() map[string]string }
|
||||
Now func() time.Time
|
||||
@@ -269,7 +272,20 @@ func (st *state) choose(bm string) (node, toolchain string, err error) {
|
||||
func (st *state) fetchAll(ctx context.Context) *delivery.Verdict {
|
||||
began := st.j.now()
|
||||
defer func() { st.clone = st.j.now().Sub(began) }()
|
||||
for _, p := range st.c.Pulls {
|
||||
// The mesh-sdk member first: whether its merge changes the TypeScript package's paths decides whether the other members'
|
||||
// Node fetches take the group's package (ADR 0500 §5).
|
||||
pulls := slices.Clone(st.c.Pulls)
|
||||
slices.SortStableFunc(pulls, func(a, b Pull) int {
|
||||
if (a.Repository == SDKRepository) != (b.Repository == SDKRepository) {
|
||||
if a.Repository == SDKRepository {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
var groupPackage map[string]any
|
||||
for _, p := range pulls {
|
||||
var merged []Fetched
|
||||
for _, bm := range st.j.Modules.BuildModules(p.Repository) {
|
||||
node, tc, err := st.choose(bm)
|
||||
@@ -280,6 +296,9 @@ func (st *state) fetchAll(ctx context.Context) *delivery.Verdict {
|
||||
st.nodes[node] = true
|
||||
args := map[string]any{"repository": p.Repository, "commit": p.Head, "ref": refOf(p.Number), "base": p.Trunk}
|
||||
t.fetchArgs = args
|
||||
if bm == buildrun.NodeBuildModule && groupPackage != nil && p.Repository != SDKRepository {
|
||||
args["package"] = groupPackage
|
||||
}
|
||||
if bm == buildrun.GoBuildModule {
|
||||
beside, err := st.j.besideFor(ctx, st.c, p)
|
||||
if err != nil {
|
||||
@@ -311,10 +330,33 @@ func (st *state) fetchAll(ctx context.Context) *delivery.Verdict {
|
||||
if st.c.Try == nil { // a try owes the indexer no preview: no pull request holds its head
|
||||
st.j.writePreview(ctx, p, merged)
|
||||
}
|
||||
if p.Repository == SDKRepository && len(st.c.Pulls) > 1 && st.j.changesPackage(p, merged) {
|
||||
groupPackage = map[string]any{"repository": p.Repository, "commit": p.Head, "ref": refOf(p.Number)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// changesPackage says whether the merge of a pull request changes a file inside the named paths of one of its repository's
|
||||
// TypeScript packages: a change of the manifest alone, or of files outside those paths, does not (ADR 0500 §5).
|
||||
func (j *Examiner) changesPackage(p Pull, merged []Fetched) bool {
|
||||
if j.Packages == nil {
|
||||
return false
|
||||
}
|
||||
paths := j.Packages.PackagePaths(p.Repository)
|
||||
for _, f := range merged {
|
||||
for _, changed := range f.Changed {
|
||||
for _, named := range paths {
|
||||
named = strings.TrimSuffix(strings.TrimPrefix(named, "./"), "/")
|
||||
if changed == named || strings.HasPrefix(changed, named+"/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isTestFile says whether a repository-relative path is a test file of the languages the checker judges: Go's _test.go and
|
||||
// the TypeScript module's .test. and .spec. files and test directories.
|
||||
func isTestFile(p string) bool {
|
||||
|
||||
Reference in New Issue
Block a user