diff --git a/linux-stable.sh b/linux-stable.sh new file mode 100755 index 000000000..1c79c2d8e --- /dev/null +++ b/linux-stable.sh @@ -0,0 +1,262 @@ +#!/usr/bin/env bash +# +# Pull in linux-stable updates to a kernel tree +# +# Copyright (C) 2017-2018 Nathan Chancellor +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see + + +# Colors for script +BOLD="\033[1m" +GRN="\033[01;32m" +RED="\033[01;31m" +RST="\033[0m" +YLW="\033[01;33m" + + +# Alias for echo to handle escape codes like colors +function echo() { + command echo -e "$@" +} + + +# Prints a formatted header to point out what is being done to the user +function header() { + if [[ -n ${2} ]]; then + COLOR=${2} + else + COLOR=${RED} + fi + echo "${COLOR}" + # shellcheck disable=SC2034 + echo "====$(for i in $(seq ${#1}); do echo "=\c"; done)====" + echo "== ${1} ==" + # shellcheck disable=SC2034 + echo "====$(for i in $(seq ${#1}); do echo "=\c"; done)====" + echo "${RST}" +} + + +# Prints an error in bold red +function die() { + echo + echo "${RED}${1}${RST}" + [[ ${2} = "-h" ]] && ${0} -h + exit 1 +} + + +# Prints a statement in bold green +function success() { + echo + echo "${GRN}${1}${RST}" + [[ -z ${2} ]] && echo +} + + +# Prints a warning in bold yellow +function warn() { + echo + echo "${YLW}${1}${RST}" + [[ -z ${2} ]] && echo +} + + +# Parse the provided parameters +function parse_parameters() { + while [[ $# -ge 1 ]]; do + case ${1} in + # Use git cherry-pick + "-c"|"--cherry-pick") + UPDATE_METHOD=cherry-pick ;; + + # Only update the linux-stable remote + "-f"|"--fetch-only") + FETCH_REMOTE_ONLY=true ;; + + # Help menu + "-h"|"--help") + echo + echo "${BOLD}Command:${RST} ./$(basename "${0}") " + echo + echo "${BOLD}Script description:${RST} Merges/cherry-picks Linux upstream into a kernel tree" + echo + echo "${BOLD}Required parameters:${RST}" + echo " -c | --cherry-pick" + echo " -m | --merge" + echo " Call either git cherry-pick or git merge when updating from upstream" + echo + echo "${BOLD}Optional parameters:${RST}" + echo " -f | --fetch-only" + echo " Simply fetches the tags from linux-stable then exits" + echo + echo " -k | --kernel-folder" + echo " The device's kernel source's location; this can either be a full path or relative to where the script is being executed." + echo + echo " -l | --latest" + echo " Updates to the latest version available for the current kernel tree" + echo + echo " -p | --print-latest" + echo " Prints the latest version available for the current kernel tree then exits" + echo + echo " -v | --version" + echo " Updates to the specified version (e.g. -v 3.18.78)" + echo + echo "${BOLD}Defaults:${RST}" + echo " If -l or -v are not specified, ONE version is picked at a time (e.g. 3.18.31 to 3.18.32)" + echo + echo " If -k is not specified, the script assumes it is in the kernel source folder already" + echo + exit 1 ;; + + # Kernel source location + "-k"|"--kernel-folder") + shift + [[ $# -lt 1 ]] && die "Please specify a kernel source location!" + + KERNEL_FOLDER=${1} ;; + + # Update to the latest version upstream unconditionally + "-l"|"--latest") + UPDATE_MODE=1 ;; + + # Use git merge + "-m"|"--merge") + UPDATE_METHOD=merge ;; + + # Print the latest version from kernel.org + "-p"|"--print-latest") + PRINT_LATEST=true ;; + + # Update to the specified version + "-v"|"--version") + shift + [[ $# -lt 1 ]] && die "Please specify a version to update!" + + TARGET_VERSION=${1} ;; + + *) + die "Invalid parameter!" ;; + esac + + shift + done + + # If kernel source isn't specified, assume we're there + [[ -z ${KERNEL_FOLDER} ]] && KERNEL_FOLDER=$(pwd) + + # Sanity checks + [[ ! ${UPDATE_METHOD} ]] && die "Neither cherry-pick nor merge were specified, please supply one!" -h + [[ ! -d ${KERNEL_FOLDER} ]] && die "Invalid kernel source location specified! Folder does not exist" -h + [[ ! -f ${KERNEL_FOLDER}/Makefile ]] && die "Invalid kernel source location specified! No Makefile present" -h + + # Default update mode is one version at a time + [[ -z ${UPDATE_MODE} && -z ${TARGET_VERSION} ]] && UPDATE_MODE=0 +} + + +# Update the linux-stable remote (and add it if it doesn't exist) +function update_remote() { + header "Updating linux-stable" + + # Add remote if it isn't already present + cd "${KERNEL_FOLDER}" || die "Could not change into ${KERNEL_FOLDER}!" + + if git fetch --tags https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/; then + success "linux-stable updated successfully!" + else + die "linux-stable update failed!" + fi + + [[ ${FETCH_REMOTE_ONLY} ]] && exit 0 +} + + +# Generate versions +function generate_versions() { + header "Calculating versions" + + # Full kernel version + CURRENT_VERSION=$(make -s CC=gcc CROSS_COMPILE="" kernelversion) + # First two numbers (3.4 | 3.10 | 3.18 | 4.4) + CURRENT_MAJOR_VERSION=$(echo "${CURRENT_VERSION}" | cut -f 1,2 -d .) + # Last number + CURRENT_SUBLEVEL=$(echo "${CURRENT_VERSION}" | cut -d . -f 3) + + # Get latest update from upstream + LATEST_VERSION=$(git tag --sort=-taggerdate -l "v${CURRENT_MAJOR_VERSION}"* | head -n 1 | sed s/v//) + LATEST_SUBLEVEL=$(echo "${LATEST_VERSION}" | cut -d . -f 3) + + # Print the current/latest version and exit if requested + echo "${BOLD}Current kernel version:${RST} ${CURRENT_VERSION}" + echo + echo "${BOLD}Latest kernel version:${RST} ${LATEST_VERSION}" + if [[ ${PRINT_LATEST} ]]; then + echo + exit 0 + fi + + # UPDATE_MODES: + # 0. Update one version + # 1. Update to the latest version + case ${UPDATE_MODE} in + 0) + TARGET_SUBLEVEL=$((CURRENT_SUBLEVEL + 1)) + TARGET_VERSION=${CURRENT_MAJOR_VERSION}.${TARGET_SUBLEVEL} ;; + 1) + TARGET_VERSION=${LATEST_VERSION} ;; + esac + + # Make sure target version is between current version and latest version + TARGET_SUBLEVEL=$(echo "${TARGET_VERSION}" | cut -d . -f 3) + [[ ${TARGET_SUBLEVEL} -le ${CURRENT_SUBLEVEL} ]] && die "${TARGET_VERSION} is already present in ${CURRENT_VERSION}!" + [[ ${TARGET_SUBLEVEL} -gt ${LATEST_SUBLEVEL} ]] && die "${CURRENT_VERSION} is the latest!" + [[ ${CURRENT_SUBLEVEL} -eq 0 ]] && CURRENT_VERSION=${CURRENT_MAJOR_VERSION} + + RANGE=v${CURRENT_VERSION}..v${TARGET_VERSION} + + echo + echo "${BOLD}Target kernel version:${RST} ${TARGET_VERSION}" + echo +} + + +function update_to_target_version() { + case ${UPDATE_METHOD} in + "cherry-pick") + if ! git cherry-pick "${RANGE}"; then + die "Cherry-pick needs manual intervention! Resolve conflicts then run: + +git add . && git cherry-pick --continue" + else + header "${TARGET_VERSION} PICKED CLEANLY!" "${GRN}" + fi ;; + + "merge") + if ! GIT_MERGE_VERBOSITY=1 git merge --no-edit "v${TARGET_VERSION}"; then + die "Merge needs manual intervention! + +Resolve conflicts then run git commit!" + else + header "${TARGET_VERSION} MERGED CLEANLY!" "${GRN}" + fi ;; + esac +} + + +parse_parameters "$@" +update_remote +generate_versions +update_to_target_version