@@ -244,6 +244,14 @@ function build_sketch { # build_sketch <ide_path> <user_path> <path-to-ino> [ext
244244        fi 
245245    fi 
246246
247+     #  Install libraries from ci.json if they exist
248+     install_libs -ai " $ide_path " " $sketchdir " 
249+     install_result=$? 
250+     if  [ $install_result  -ne  0 ];  then 
251+         echo  " ERROR: Library installation failed for $sketchname " >&2 
252+         exit  $install_result 
253+     fi 
254+ 
247255    ARDUINO_CACHE_DIR=" $HOME /.arduino/cache.tmp" 
248256    if  [ -n  " $ARDUINO_BUILD_DIR " ;  then 
249257        build_dir=" $ARDUINO_BUILD_DIR " 
@@ -580,13 +588,168 @@ function build_sketches { # build_sketches <ide_path> <user_path> <target> <path
580588    return  0
581589}
582590
591+ #  Return 0 if the string looks like a git URL we should pass to --git-url
592+ is_git_like_url () {
593+     local  u=$1 
594+     [[ " $u " =~  ^https? ://.+ ]] ||  [[ " $u " =~  ^git@[^:]+:.+ ]]
595+ }
596+ 
597+ #  If status!=0, print errors/warnings from captured output (fallback to full output)
598+ print_err_warnings () {
599+     local  status=$1 ;  shift 
600+     local  out=$* 
601+     if  [ " $status " -ne  0 ];  then 
602+         printf  ' %s\n' " $out " |  grep -Ei " error|warning|warn" >&2  ||  printf  ' %s\n' " $out " >&2 
603+     fi 
604+ }
605+ 
606+ function  install_libs  { #  install_libs <ide_path> <sketchdir> [-v]
607+     local  ide_path=" " 
608+     local  sketchdir=" " 
609+     local  verbose=false
610+ 
611+     while  [ -n  " $1 " ;  do 
612+         case  " $1 " in 
613+         -ai ) shift ;  ide_path=$1  ;;
614+         -s  ) shift ;  sketchdir=$1  ;;
615+         -v  ) verbose=true ;;
616+         *  )
617+             echo  " ERROR: Unknown argument: $1 " >&2 
618+             echo  " USAGE: install_libs -ai <ide_path> -s <sketchdir> [-v]" >&2 
619+             return  1
620+             ;;
621+         esac 
622+         shift 
623+     done 
624+ 
625+     if  [ -z  " $ide_path " ;  then 
626+         echo  " ERROR: IDE path not provided" >&2 
627+         echo  " USAGE: install_libs -ai <ide_path> -s <sketchdir> [-v]" >&2 
628+         return  1
629+     fi 
630+     if  [ -z  " $sketchdir " ;  then 
631+         echo  " ERROR: Sketch directory not provided" >&2 
632+         echo  " USAGE: install_libs -ai <ide_path> -s <sketchdir> [-v]" >&2 
633+         return  1
634+     fi 
635+     if  [ !  -f  " $ide_path /arduino-cli" ;  then 
636+         echo  " ERROR: arduino-cli not found at $ide_path /arduino-cli" >&2 
637+         return  1
638+     fi 
639+ 
640+     if  [ !  -f  " $sketchdir /ci.json" ;  then 
641+         [ " $verbose " =  true  ] &&  echo  " No ci.json found in $sketchdir , skipping library installation" 
642+         return  0
643+     fi 
644+     if  !  jq -e .  " $sketchdir /ci.json" > /dev/null 2>&1 ;  then 
645+         echo  " ERROR: $sketchdir /ci.json is not valid JSON" >&2 
646+         return  1
647+     fi 
648+ 
649+     local  libs_type
650+     libs_type=$( jq -r ' .libs | type' " $sketchdir /ci.json" 2> /dev/null) 
651+     if  [ -z  " $libs_type " ||  [ " $libs_type " =  " null" ;  then 
652+         [ " $verbose " =  true  ] &&  echo  " No libs field found in ci.json, skipping library installation" 
653+         return  0
654+     elif  [ " $libs_type " !=  " array" ;  then 
655+         echo  " ERROR: libs field in ci.json must be an array, found: $libs_type " >&2 
656+         return  1
657+     fi 
658+ 
659+     local  libs_count
660+     libs_count=$( jq -r ' .libs | length' " $sketchdir /ci.json" 2> /dev/null) 
661+     if  [ " $libs_count " -eq  0 ];  then 
662+         [ " $verbose " =  true  ] &&  echo  " libs array is empty in ci.json, skipping library installation" 
663+         return  0
664+     fi 
665+ 
666+     echo  " Installing $libs_count  libraries from $sketchdir /ci.json" 
667+ 
668+     local  needs_unsafe=false
669+     local  original_unsafe_setting=" " 
670+     local  libs
671+     libs=$( jq -r ' .libs[]? // empty' " $sketchdir /ci.json" ) 
672+ 
673+     #  Detect any git-like URL (GitHub/GitLab/Bitbucket/self-hosted/ssh)
674+     for  lib  in  $libs ;  do 
675+         if  is_git_like_url " $lib " ;  then 
676+             needs_unsafe=true
677+             break 
678+         fi 
679+     done 
680+ 
681+     if  [ " $needs_unsafe " =  true  ];  then 
682+         [ " $verbose " =  true  ] &&  echo  " Checking current unsafe install setting..." 
683+         original_unsafe_setting=$( " $ide_path /arduino-cli" 2> /dev/null ||  echo  " false" ) 
684+         if  [ " $original_unsafe_setting " =  " false" ;  then 
685+             [ " $verbose " =  true  ] &&  echo  " Enabling unsafe installs for Git URLs..." 
686+             " $ide_path /arduino-cli" set  library.enable_unsafe_install true  > /dev/null 2>&1  ||  \
687+                 echo  " WARNING: Failed to enable unsafe installs, Git URL installs may fail" >&2 
688+         else 
689+             [ " $verbose " =  true  ] &&  echo  " Unsafe installs already enabled" 
690+         fi 
691+     fi 
692+ 
693+     local  rc=0 install_status=0 output=" " 
694+     for  lib  in  $libs ;  do 
695+         [ " $verbose " =  true  ] &&  echo  " Processing library: $lib " 
696+ 
697+         if  is_git_like_url " $lib " ;  then 
698+             [ " $verbose " =  true  ] &&  echo  " Installing library from git URL: $lib " 
699+             if  [ " $verbose " =  true  ];  then 
700+                 " $ide_path /arduino-cli" " $lib " 
701+                 install_status=$? 
702+             else 
703+                 output=$( " $ide_path /arduino-cli" " $lib " 2>&1 ) 
704+                 install_status=$? 
705+             fi 
706+         else 
707+             [ " $verbose " =  true  ] &&  echo  " Installing library by name: $lib " 
708+             if  [ " $verbose " =  true  ];  then 
709+                 " $ide_path /arduino-cli" " $lib " 
710+                 install_status=$? 
711+             else 
712+                 output=$( " $ide_path /arduino-cli" " $lib " 2>&1 ) 
713+                 install_status=$? 
714+             fi 
715+         fi 
716+ 
717+         #  Treat "already installed"/"up to date" as success (idempotent)
718+         if  [ $install_status  -ne  0 ] &&  echo  " $output " |  grep -qiE ' already installed|up to date' ;  then 
719+             install_status=0
720+         fi 
721+ 
722+         if  [ " $verbose " !=  true  ];  then 
723+             print_err_warnings " $install_status " " $output " 
724+         fi 
725+ 
726+         if  [ $install_status  -ne  0 ];  then 
727+             echo  " ERROR: Failed to install library: $lib " >&2 
728+             rc=$install_status 
729+             break 
730+         else 
731+             [ " $verbose " =  true  ] &&  echo  " Successfully installed library: $lib " 
732+         fi 
733+     done 
734+ 
735+     if  [ " $needs_unsafe " =  true  ] &&  [ " $original_unsafe_setting " =  " false" ;  then 
736+         [ " $verbose " =  true  ] &&  echo  " Restoring original unsafe install setting..." 
737+         " $ide_path /arduino-cli" set  library.enable_unsafe_install false  > /dev/null 2>&1  ||  true 
738+     fi 
739+ 
740+     [ $rc  -eq  0 ] &&  echo  " Library installation completed" 
741+     return  $rc 
742+ }
743+ 
744+ 
583745USAGE=" 
584746USAGE: ${0}  [command] [options] 
585747Available commands: 
586748    count: Count sketches. 
587749    build: Build a sketch. 
588750    chunk_build: Build a chunk of sketches. 
589751    check_requirements: Check if target meets sketch requirements. 
752+     install_libs: Install libraries from ci.json file. 
590753" 
591754
592755cmd=$1 
@@ -606,6 +769,8 @@ case "$cmd" in
606769    ;;
607770    " check_requirements" " $@ " 
608771    ;;
772+     " install_libs" " $@ " 
773+     ;;
609774    * )
610775        echo  " ERROR: Unrecognized command" 
611776        echo  " $USAGE " 
0 commit comments