@@ -17,6 +17,7 @@ package linux
1717import (
1818 "context"
1919 "fmt"
20+ "strings"
2021 "testing"
2122
2223 "github.com/stretchr/testify/require"
@@ -38,14 +39,68 @@ func installNodeVersion(ctx context.Context, t *testing.T, ctr tc.Container, ver
3839 execOK (ctx , t , ctr , activated (fmt .Sprintf ("vfox use --global nodejs@%s" , version )))
3940}
4041
41- // installLatestNode installs the newest published Node.js version. `vfox
42- // install nodejs@ latest` resolves @latest through the plugin's Available
43- // (the first, newest entry) and prints the pick ; the version is returned so
44- // the caller can drive `vfox use` against it .
45- func installLatestNode (ctx context.Context , t * testing.T , ctr tc.Container ) string {
42+ // installLatestNode installs the newest published Node.js version. If the
43+ // latest official release has no unofficial ARM64 musl build yet, that one
44+ // platform-specific probe is skipped ; the other latest probes still cover
45+ // @latest resolution and installation .
46+ func installLatestNode (ctx context.Context , t * testing.T , ctr tc.Container ) ( string , bool ) {
4647 t .Helper ()
47- out := execRetry (ctx , t , ctr , "vfox install nodejs@latest" , "vfox install nodejs@latest" )
48+ const label = "vfox install nodejs@latest"
49+ code , out := execRun (ctx , t , ctr , label )
50+ if code != 0 {
51+ version , filename , missing := missingLatestArm64MuslBuild (out )
52+ if missing {
53+ available , known := unofficialArm64MuslBuildAvailable (ctx , t , ctr , version , filename )
54+ if known && ! available {
55+ t .Logf ("SKIP @latest install: unofficial musl ARM64 build %s is not published yet" , filename )
56+ return "" , false
57+ }
58+ }
59+ out = common .Retry (ctx , t , label , func () (int , string ) {
60+ return execRun (ctx , t , ctr , label )
61+ })
62+ }
4863 latest := common .LatestUsed (out )
4964 require .NotEmptyf (t , latest , "@latest install did not report the picked version\n --- output ---\n %s" , out )
50- return latest
65+ return latest , true
66+ }
67+
68+ // missingLatestArm64MuslBuild recognizes only the specific error emitted
69+ // after the unofficial-builds checksum list also lacks the requested asset.
70+ func missingLatestArm64MuslBuild (output string ) (version , filename string , missing bool ) {
71+ output = common .StripANSI (output )
72+ version = common .LatestUsed (output )
73+ if version == "" {
74+ return "" , "" , false
75+ }
76+ filename = fmt .Sprintf ("node-v%s-linux-arm64-musl.tar.gz" , version )
77+ message := fmt .Sprintf ("Node.js package %s is not listed in " , filename )
78+ missing = strings .Contains (output , message ) && strings .Contains (output , "SHASUMS256.txt" )
79+ return version , filename , missing
80+ }
81+
82+ // unofficialArm64MuslBuildAvailable checks the exact upstream checksum list
83+ // before treating a missing artifact as an expected release-publication lag.
84+ // The second return value is false when the HTTP result is inconclusive, so
85+ // network errors still fail through the normal retry path instead of skipping.
86+ func unofficialArm64MuslBuildAvailable (ctx context.Context , t * testing.T , ctr tc.Container , version , filename string ) (available , known bool ) {
87+ t .Helper ()
88+ url := fmt .Sprintf ("https://unofficial-builds.nodejs.org/download/release/v%s/SHASUMS256.txt" , version )
89+ script := fmt .Sprintf ("curl -fsSL --max-time 20 -w '\\ n%%{http_code}' %q 2>/dev/null" , url )
90+ code , out := execRun (ctx , t , ctr , script )
91+ trimmed := strings .TrimSpace (out )
92+ status := trimmed [strings .LastIndex (trimmed , "\n " )+ 1 :]
93+ switch status {
94+ case "404" :
95+ return false , code == 22
96+ case "200" :
97+ if code != 0 {
98+ return false , false
99+ }
100+ return strings .Contains (trimmed , filename ), true
101+ default :
102+ // curl exits non-zero for HTTP errors and reports 000 for transport
103+ // failures. Do not skip a CI failure unless absence is confirmed.
104+ return false , false
105+ }
51106}
0 commit comments