|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Run socket-facts.plugin.scala on the relocation fixture and assert a Maven relocation's target keeps |
| 3 | +# the parent that declared the relocating module: the project itself (direct) or a build project it |
| 4 | +# depends on, never a library that declares it with a scope it does not pass on. Coursier reports the |
| 5 | +# target with a caller it leaves out of the report, and gives that caller its declared revision, here |
| 6 | +# a version range; an override can move the target off the version the relocation names. |
| 7 | +# |
| 8 | +# Usage: smoke-test-relocation.sh <sbt-version> <scala-version> [coursier|ivy] (ivy: sbt 1.x only) |
| 9 | +set -euo pipefail |
| 10 | +HERE="$(cd "$(dirname "$0")" && pwd)" |
| 11 | +SBT_VERSION="${1:?usage: smoke-test-relocation.sh <sbt-version> <scala-version>}" |
| 12 | +SCALA_VERSION="${2:?usage: smoke-test-relocation.sh <sbt-version> <scala-version>}" |
| 13 | +RESOLVER="${3:-coursier}" |
| 14 | +PLUGIN="$HERE/../../socket-facts.plugin.scala" |
| 15 | +PROJECT="$HERE/relocation" |
| 16 | +RECORDS="$PROJECT/records.tsv" |
| 17 | +# shellcheck source=SCRIPTDIR/../compat-cache.sh |
| 18 | +. "$HERE/../compat-cache.sh" |
| 19 | +IVY="$SOCKET_COMPAT_CACHE/ivy2" |
| 20 | +export COURSIER_CACHE="$SOCKET_COMPAT_CACHE/coursier" |
| 21 | + |
| 22 | +bash "$HERE/../make-stub-repo.sh" "$PROJECT/localrepo" \ |
| 23 | + 'demo.old:moved:1.0>demo.ext:moved:1.0' \ |
| 24 | + 'demo.ext:moved:1.0' \ |
| 25 | + 'demo.ext:moved:1.1' \ |
| 26 | + 'demo.ext:wrapper:1.0+demo.old:moved:1.0@test' |
| 27 | +rm -rf "$IVY/cache/demo.ext" "$IVY/cache/demo.old" |
| 28 | + |
| 29 | +GB="$(mktemp -d)/global-base" |
| 30 | +mkdir -p "$GB/plugins" |
| 31 | +cp "$PLUGIN" "$GB/plugins/SocketFactsPlugin.scala" |
| 32 | +mkdir -p "$PROJECT/project" |
| 33 | +echo "sbt.version=$SBT_VERSION" > "$PROJECT/project/build.properties" |
| 34 | +echo "scalaVersion in ThisBuild := \"$SCALA_VERSION\"" > "$PROJECT/scala-version.sbt" |
| 35 | +if [ "$RESOLVER" = ivy ]; then |
| 36 | + echo 'useCoursier in ThisBuild := false' >> "$PROJECT/scala-version.sbt" |
| 37 | +fi |
| 38 | +rm -rf "$RECORDS" "$PROJECT/target" "$PROJECT/project/target" "$PROJECT"/*/target |
| 39 | + |
| 40 | +echo "+ sbt $SBT_VERSION (scala $SCALA_VERSION) (relocation, $RESOLVER)" |
| 41 | +( cd "$PROJECT" && sbt -Dsbt.global.base="$GB" -Dsbt.server.autostart=false \ |
| 42 | + -Dsbt.ivy.home="$IVY" -Dsocket.recordsFile="$RECORDS" --batch socketFacts ) |
| 43 | + |
| 44 | +python3 - "$RECORDS" <<'PY' |
| 45 | +import sys |
| 46 | +rows = [l.rstrip('\n').split('\t') for l in open(sys.argv[1]) if l.strip()] |
| 47 | +root_of = {r[1]: (r[2], r[3]) for r in rows if r[0] == 'root'} |
| 48 | +# Coursier leaves the relocating module out, so the target hangs under its declarer; Ivy keeps it as |
| 49 | +# an artifact-less node between the two, which counts as the declarer's edge. |
| 50 | +moved = {'core': 'demo.ext:moved:jar:1.0', 'app': 'demo.ext:moved:jar:1.1'} |
| 51 | +relocating, wrapper = 'demo.old:moved:1.0', 'demo.ext:wrapper:jar:1.0' |
| 52 | +errors = [] |
| 53 | +for rid, (project, config) in root_of.items(): |
| 54 | + nodes = {r[2]: r for r in rows if r[0] == 'node' and r[1] == rid} |
| 55 | + target = moved.get(project) |
| 56 | + if target not in nodes: |
| 57 | + continue |
| 58 | + parents_of = lambda c: {r[2] for r in rows if r[0] == 'edge' and r[1] == rid and r[3] == c} |
| 59 | + parents, direct = parents_of(target), nodes[target][8] == '1' |
| 60 | + if relocating in parents: |
| 61 | + parents = (parents - {relocating}) | parents_of(relocating) |
| 62 | + direct = direct or nodes[relocating][8] == '1' |
| 63 | + if project == 'core' and not direct: |
| 64 | + errors.append(f"core/{config}: {target} not direct") |
| 65 | + if project == 'app' and parents != {'core'}: |
| 66 | + errors.append(f"app/{config}: {target} parents {sorted(parents)} != ['core']") |
| 67 | + if wrapper in parents: |
| 68 | + errors.append(f"{project}/{config}: {wrapper} made a parent of {target}") |
| 69 | +for project, target in moved.items(): |
| 70 | + if not any(r[0] == 'node' and r[2] == target and root_of[r[1]][0] == project for r in rows): |
| 71 | + errors.append(f"{target} missing from {project}") |
| 72 | +if errors: |
| 73 | + print("FAIL:") |
| 74 | + for e in errors: print(" -", e) |
| 75 | + sys.exit(1) |
| 76 | +print(f"PASS (relocation): demo.ext:moved direct in core, under core in app (overridden), never under {wrapper}") |
| 77 | +PY |
| 78 | + |
| 79 | +python3 "$HERE/../assert-reachability.py" "$RECORDS" |
0 commit comments