@@ -902,8 +902,14 @@ exit 0`,
902902 } ) ;
903903
904904 expect ( result . status ) . toBe ( 0 ) ;
905- // git should NOT have been called at all in the source-checkout path
906- expect ( fs . existsSync ( gitLog ) ) . toBe ( false ) ;
905+ // git clone / git fetch should NOT have been called in the source-checkout path.
906+ // git may be called for version resolution (git describe), so we check
907+ // that no clone or fetch was attempted rather than no git calls at all.
908+ if ( fs . existsSync ( gitLog ) ) {
909+ const gitCalls = fs . readFileSync ( gitLog , "utf-8" ) ;
910+ expect ( gitCalls ) . not . toMatch ( / c l o n e / ) ;
911+ expect ( gitCalls ) . not . toMatch ( / f e t c h / ) ;
912+ }
907913 // And curl for the releases API should NOT have been called
908914 expect ( `${ result . stdout } ${ result . stderr } ` ) . not . toMatch ( / c u r l s h o u l d n o t b e c a l l e d / ) ;
909915 } ) ;
@@ -1043,18 +1049,43 @@ describe("installer pure helpers", () => {
10431049
10441050 // -- resolve_installer_version --
10451051
1046- it ( "resolve_installer_version: reads version from package.json" , ( ) => {
1052+ it ( "resolve_installer_version: reads version from git or package.json" , ( ) => {
10471053 const r = callInstallerFn ( "resolve_installer_version" ) ;
1048- // Should read from the repo's actual package.json
1049- expect ( r . stdout . trim ( ) ) . toMatch ( / ^ \d + \. \d + \. \d + $ / ) ;
1054+ // May return clean semver ("0.0.2") or git describe format ("0.0.2-3-gabcdef1")
1055+ expect ( r . stdout . trim ( ) ) . toMatch ( / ^ \d + \. \d + \. \d + ( - .+ ) ? $ / ) ;
1056+ } ) ;
1057+
1058+ it ( "resolve_installer_version: falls back to package.json when git tags are unavailable" , ( ) => {
1059+ const tmp = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "nemoclaw-resolve-ver-pkg-" ) ) ;
1060+ fs . mkdirSync ( path . join ( tmp , ".git" ) ) ;
1061+ fs . writeFileSync (
1062+ path . join ( tmp , "package.json" ) ,
1063+ `${ JSON . stringify ( { version : "0.5.0" } , null , 2 ) } \n` ,
1064+ ) ;
1065+ // source overwrites SCRIPT_DIR, so we re-set it after sourcing.
1066+ // The temp dir advertises git metadata but has no usable tags,
1067+ // so the function should fall back to package.json instead of exiting.
1068+ const r = spawnSync (
1069+ "bash" ,
1070+ [ "-c" , `source "${ INSTALLER } " 2>/dev/null; SCRIPT_DIR="${ tmp } "; resolve_installer_version` ] ,
1071+ {
1072+ cwd : tmp ,
1073+ encoding : "utf-8" ,
1074+ env : { HOME : tmp , PATH : TEST_SYSTEM_PATH } ,
1075+ } ,
1076+ ) ;
1077+ expect ( r . status ) . toBe ( 0 ) ;
1078+ expect ( r . stdout . trim ( ) ) . toBe ( "0.5.0" ) ;
10501079 } ) ;
10511080
10521081 it ( "resolve_installer_version: falls back to DEFAULT when no package.json" , ( ) => {
10531082 const tmp = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "nemoclaw-resolve-ver-" ) ) ;
1054- // source from a directory with no package.json — SCRIPT_DIR will be wrong
1083+ // source overwrites SCRIPT_DIR, so we re-set it after sourcing.
1084+ // The temp dir has no .git, no .version, and no package.json,
1085+ // so the function should fall back to DEFAULT_NEMOCLAW_VERSION.
10551086 const r = spawnSync (
10561087 "bash" ,
1057- [ "-c" , `SCRIPT_DIR=" ${ tmp } "; source "${ INSTALLER } " 2>/dev/null; resolve_installer_version` ] ,
1088+ [ "-c" , `source "${ INSTALLER } " 2>/dev/null; SCRIPT_DIR=" ${ tmp } " ; resolve_installer_version` ] ,
10581089 {
10591090 cwd : tmp ,
10601091 encoding : "utf-8" ,
0 commit comments