blob: 21c07300c1545d7fdcc7d1ba3cc18ed67315426b [file]
#!/bin/bash
# Copyright 2018 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#### CATEGORY=Software delivery
### perform a system OTA on a connected device
## usage: fx ota [--help] [--build|--no-build] [--serve] [--packageless|--no-packageless]
##
## --build | --no-build Build (or not) the 'updates' target before initiating OTA.
## --serve Spawn 'fx serve' for the duration of the OTA if not already running.
## --packageless | --no-packageless Use (or not) a packageless update (direct blob fetching).
## Defaults to '--packageless'.
## --help Print out this message.
##
## Ask the connected Fuchsia device to do an OTA.
## Depending on the flags, this command will attempt to build the necessary
## dependencies ('fx build updates') before requesting the OTA.
##
## The default for --build is defined by the "incremental" feature:
## 'fx --enable=incremental ota' defaults to '--build'
## 'fx --disable=incremental ota' defaults to '--no-build'
##
## A package server needs to be running and this command will fail if it is not.
set -e
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/updates.sh || exit $?
fx-config-read
fx-fail-if-main-pb-is-not-set
function needs-update {
local targets_path="${FUCHSIA_BUILD_DIR}/amber-files/repository/targets.json"
local base_package="system_image/0"
local jq_filter=".signed.targets[\"${base_package}\"].custom.merkle"
local base_package_merkle
if [ ! -e "${targets_path}" ]; then
fx-error "TUF targets metadata '${targets_path}' does not exist."
fx-error "You may need to run 'fx build' to create it."
return 1
fi
if ! base_package_merkle=$(fx-command-run jq -e -r "$jq_filter" "$targets_path"); then
fx-error "TUF targets metadata does not contain the package '${base_package}'".
fx-error "You may need to run 'fx build' to create it."
return 1
fi
if [[ "${base_package_merkle}" == "" || "${base_package_merkle}" == "null" ]]; then
fx-error "Invalid merkle for TUF targets entry for 'system_image/0'."
fx-error "Merkle must be a non-empty string, not '${base_package_merkle}'."
return 1
fi
[[ $(fx-command-run shell 'read ver < /system/meta;echo $ver') != "${base_package_merkle}" ]]
}
function main {
local build=false
local serve=false
local packageless=true
local build_targets=("//build/images/updates")
if is_feature_enabled "incremental"; then
# In incremental workflows, these defaults have changed.
# Keep old behavior if incremental is not enabled.
build=true
fi
while (( $# )); do
case "$1" in
--help)
fx-command-help
exit 0
;;
--no-build)
build=false
;;
--build)
build=true
;;
--serve)
serve=true
;;
--packageless)
packageless=true
;;
--no-packageless)
packageless=false
;;
*)
fx-error 'Invalid syntax'
fx-command-help
exit 1
esac
shift
done
local started_serve=false
if $packageless; then
if $serve; then
fx-info "--serve is not needed in packageless OTAs, a temporary server is always started by ffx."
fi
elif $serve; then
# TODO(https://fxbug.dev/504722186): Implement temporary server in `ffx target update`.
if ! check-for-package-server >/dev/null 2>&1; then
fx-info "Package server not running. Starting 'fx serve'..."
local server_name="fx-ota-serve-$(date +%Y-%m-%dt%H-%M-%S)"
fx-command-run serve --background --name "${server_name}" || return 1
started_serve=true
trap "fx-info \"Stopping package server...\"; fx-command-run ffx repository server stop ${server_name} || true" EXIT
# Wait for it to be ready
local tries=10
while ! check-for-package-server >/dev/null 2>&1; do
if ((tries-- == 0)); then
fx-error "Package server failed to start in time."
return 1
fi
sleep 1
done
fi
else
check-for-package-server || return 1
fi
local total_start_time=$(date +%s)
if $build; then
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Starting build/refresh of ${build_targets[@]}..."
local build_start_time=$(date +%s)
fx-command-run build "${build_targets[@]}" || return 1
local build_end_time=$(date +%s)
local build_duration=$((build_end_time - build_start_time))
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Build completed in ${build_duration} seconds."
fi
local ota_args=("--reboot" "false")
if $packageless; then
ota_args+=("--product-bundle" "--packageless")
fi
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Starting OTA..."
local ota_start_time=$(date +%s)
fx-command-run ffx target update force-install "${ota_args[@]}"
local ota_end_time=$(date +%s)
local ota_duration=$((ota_end_time - ota_start_time))
# Assuming ffx prints its own byte counts in its progress bar, this measures the wall clock time.
fx-info "$(date '+%Y-%m-%d %H:%M:%S') OTA sequence completed in ${ota_duration} seconds."
fx-command-run reboot
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Waiting for device to reboot..."
local wait_start_time=$(date +%s)
fx-command-run wait
local wait_end_time=$(date +%s)
local wait_duration=$((wait_end_time - wait_start_time))
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Device rebooted in ${wait_duration} seconds."
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Waiting for update to be committed..."
local commit_start_time=$(date +%s)
fx-command-run shell update wait-for-commit
local commit_end_time=$(date +%s)
local commit_duration=$((commit_end_time - commit_start_time))
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Committed in ${commit_duration} seconds."
if needs-update; then
fx-error "After update, system appears still out of date. OTA may have failed. Run 'fx log' for details."
fx-error "Also ensure your firewall rules are up to date (e.g. fx setup-ufw)"
return 1
fi
local total_end_time=$(date +%s)
local total_duration=$((total_end_time - total_start_time))
fx-info "$(date '+%Y-%m-%d %H:%M:%S') Total OTA process took ${total_duration} seconds."
}
main "$@"