Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions .github/scripts/sketch_utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ function build_sketch { # build_sketch <ide_path> <user_path> <path-to-ino> [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"
Expand Down Expand Up @@ -580,13 +588,168 @@ function build_sketches { # build_sketches <ide_path> <user_path> <target> <path
return 0
}

# Return 0 if the string looks like a git URL we should pass to --git-url
is_git_like_url() {
local u=$1
[[ "$u" =~ ^https?://.+ ]] || [[ "$u" =~ ^git@[^:]+:.+ ]]
}

# If status!=0, print errors/warnings from captured output (fallback to full output)
print_err_warnings() {
local status=$1; shift
local out=$*
if [ "$status" -ne 0 ]; then
printf '%s\n' "$out" | grep -Ei "error|warning|warn" >&2 || printf '%s\n' "$out" >&2
fi
}

function install_libs { # install_libs <ide_path> <sketchdir> [-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 <ide_path> -s <sketchdir> [-v]" >&2
return 1
;;
esac
shift
done

if [ -z "$ide_path" ]; then
echo "ERROR: IDE path not provided" >&2
echo "USAGE: install_libs -ai <ide_path> -s <sketchdir> [-v]" >&2
return 1
fi
if [ -z "$sketchdir" ]; then
echo "ERROR: Sketch directory not provided" >&2
echo "USAGE: install_libs -ai <ide_path> -s <sketchdir> [-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:
count: Count sketches.
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
Expand All @@ -606,6 +769,8 @@ case "$cmd" in
;;
"check_requirements") check_requirements "$@"
;;
"install_libs") install_libs "$@"
;;
*)
echo "ERROR: Unrecognized command"
echo "$USAGE"
Expand Down
4 changes: 4 additions & 0 deletions docs/en/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/espressif/arduino-esp32/blob/a31a5fca1739993173caba995f7785b8eed6b30e/.github/scripts/sketch_utils.sh#L86-L91>`_
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 <https://arduino.github.io/arduino-cli/1.3/commands/arduino-cli_lib_install/>`_.

The ``wifi`` test suite is a good example of how to use the ``ci.json`` file:

Expand Down
Loading