diff --git a/.github/scripts/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 01ceafe9af1..8beb70c548b 100755 --- a/.github/scripts/sketch_utils.sh +++ b/.github/scripts/sketch_utils.sh @@ -244,6 +244,14 @@ function build_sketch { # build_sketch [ext fi fi + # Install libraries from ci.json if they exist + install_libs -ai "$ide_path" -s "$sketchdir" + install_result=$? + if [ $install_result -ne 0 ]; then + echo "ERROR: Library installation failed for $sketchname" >&2 + exit $install_result + fi + ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" if [ -n "$ARDUINO_BUILD_DIR" ]; then build_dir="$ARDUINO_BUILD_DIR" @@ -580,6 +588,160 @@ function build_sketches { # build_sketches &2 || printf '%s\n' "$out" >&2 + fi +} + +function install_libs { # install_libs [-v] + local ide_path="" + local sketchdir="" + local verbose=false + + while [ -n "$1" ]; do + case "$1" in + -ai ) shift; ide_path=$1 ;; + -s ) shift; sketchdir=$1 ;; + -v ) verbose=true ;; + * ) + echo "ERROR: Unknown argument: $1" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + ;; + esac + shift + done + + if [ -z "$ide_path" ]; then + echo "ERROR: IDE path not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ -z "$sketchdir" ]; then + echo "ERROR: Sketch directory not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ ! -f "$ide_path/arduino-cli" ]; then + echo "ERROR: arduino-cli not found at $ide_path/arduino-cli" >&2 + return 1 + fi + + if [ ! -f "$sketchdir/ci.json" ]; then + [ "$verbose" = true ] && echo "No ci.json found in $sketchdir, skipping library installation" + return 0 + fi + if ! jq -e . "$sketchdir/ci.json" >/dev/null 2>&1; then + echo "ERROR: $sketchdir/ci.json is not valid JSON" >&2 + return 1 + fi + + local libs_type + libs_type=$(jq -r '.libs | type' "$sketchdir/ci.json" 2>/dev/null) + if [ -z "$libs_type" ] || [ "$libs_type" = "null" ]; then + [ "$verbose" = true ] && echo "No libs field found in ci.json, skipping library installation" + return 0 + elif [ "$libs_type" != "array" ]; then + echo "ERROR: libs field in ci.json must be an array, found: $libs_type" >&2 + return 1 + fi + + local libs_count + libs_count=$(jq -r '.libs | length' "$sketchdir/ci.json" 2>/dev/null) + if [ "$libs_count" -eq 0 ]; then + [ "$verbose" = true ] && echo "libs array is empty in ci.json, skipping library installation" + return 0 + fi + + echo "Installing $libs_count libraries from $sketchdir/ci.json" + + local needs_unsafe=false + local original_unsafe_setting="" + local libs + libs=$(jq -r '.libs[]? // empty' "$sketchdir/ci.json") + + # Detect any git-like URL (GitHub/GitLab/Bitbucket/self-hosted/ssh) + for lib in $libs; do + if is_git_like_url "$lib"; then + needs_unsafe=true + break + fi + done + + if [ "$needs_unsafe" = true ]; then + [ "$verbose" = true ] && echo "Checking current unsafe install setting..." + original_unsafe_setting=$("$ide_path/arduino-cli" config get library.enable_unsafe_install 2>/dev/null || echo "false") + if [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Enabling unsafe installs for Git URLs..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install true >/dev/null 2>&1 || \ + echo "WARNING: Failed to enable unsafe installs, Git URL installs may fail" >&2 + else + [ "$verbose" = true ] && echo "Unsafe installs already enabled" + fi + fi + + local rc=0 install_status=0 output="" + for lib in $libs; do + [ "$verbose" = true ] && echo "Processing library: $lib" + + if is_git_like_url "$lib"; then + [ "$verbose" = true ] && echo "Installing library from git URL: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install --git-url "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install --git-url "$lib" 2>&1) + install_status=$? + fi + else + [ "$verbose" = true ] && echo "Installing library by name: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install "$lib" 2>&1) + install_status=$? + fi + fi + + # Treat "already installed"/"up to date" as success (idempotent) + if [ $install_status -ne 0 ] && echo "$output" | grep -qiE 'already installed|up to date'; then + install_status=0 + fi + + if [ "$verbose" != true ]; then + print_err_warnings "$install_status" "$output" + fi + + if [ $install_status -ne 0 ]; then + echo "ERROR: Failed to install library: $lib" >&2 + rc=$install_status + break + else + [ "$verbose" = true ] && echo "Successfully installed library: $lib" + fi + done + + if [ "$needs_unsafe" = true ] && [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Restoring original unsafe install setting..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install false >/dev/null 2>&1 || true + fi + + [ $rc -eq 0 ] && echo "Library installation completed" + return $rc +} + + USAGE=" USAGE: ${0} [command] [options] Available commands: @@ -587,6 +749,7 @@ Available commands: build: Build a sketch. chunk_build: Build a chunk of sketches. check_requirements: Check if target meets sketch requirements. + install_libs: Install libraries from ci.json file. " cmd=$1 @@ -606,6 +769,8 @@ case "$cmd" in ;; "check_requirements") check_requirements "$@" ;; + "install_libs") install_libs "$@" + ;; *) echo "ERROR: Unrecognized command" echo "$USAGE" diff --git a/docs/en/contributing.rst b/docs/en/contributing.rst index 7a7b99894eb..ae07bb57706 100644 --- a/docs/en/contributing.rst +++ b/docs/en/contributing.rst @@ -425,6 +425,10 @@ The ``ci.json`` file is used to specify how the test suite and sketches will han * ``fqbn``: A dictionary that specifies the FQBNs that will be used to compile the sketch. The key is the target name and the value is a list of FQBNs. The `default FQBNs `_ are used if this field is not specified. This overrides the default FQBNs and the ``fqbn_append`` field. +* ``libs``: A list of libraries that are required to run the test suite. The libraries will be installed automatically if they are not already present. + Libraries are installed using the ``arduino-cli lib install`` command, so you can specify libraries by name + version (e.g., ``AudioZero@1.0.0``) + or by URL (e.g., ``https://github.com/arduino-libraries/WiFi101.git``). + More information can be found in the `Arduino CLI documentation `_. The ``wifi`` test suite is a good example of how to use the ``ci.json`` file: