#!/usr/bin/env bash
# SPDX-License-Identifier: MIT-0
# Copyright (C) 2024 by Gero Schwäricke <gero.schwaericke@grandcentrix.net>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

# Check for updates to Linux stable releases. The script uses the versions found in
# linux/*/linux.hash. For each of the versions it downloads the related hash list and tries
# to find an updated stable release. If found it updates all related files and hashes.

set -euo pipefail

latest_version=$(sed -rn 's|^\tdefault "(.+)" if BR2_LINUX_KERNEL_LATEST_VERSION$|\1|p' linux/Config.in)
latest_release_version=$(echo "${latest_version}" | cut -f 1-2 -d .)
echo "${latest_version} -> ${latest_release_version}"

updated_files_list=()
updated_releases_list=''
updated_versions='Update the latest kernel releases to:'

update_linux_hash() {
    HASHFILE="$1"
    hash_list=
    while read -r line; do
        # e.g.: # From https://www.kernel.org/pub/linux/kernel/v6.x/sha256sums.asc
        url=$(echo "${line}" | sed -n 's|^# From \(https://www\.kernel\.org/pub/linux/kernel/.*/sha256sums.asc\)|\1|p')
        # e.g.: sha256  f905f1238ea7a8e85314bacf283302e8097006010d25fcea726d0de0ea5bc9b6  linux-6.8.9.tar.xz
        old_hash=$(echo "${line}" | sed -rn 's|^sha256 +([0-9a-f]+) +linux-.*$|\1|p')
        old_version=$(echo "${line}" | sed -rn 's|^sha256 +[0-9a-f]+ +linux-(.+)\.tar\.xz$|\1|p')
        if [ -n "${url}" ]; then
            # Line contained a URL: Download hash list and use it.
            if [ -e "${hash_list}" ]; then
                rm "${hash_list}"
            fi

            hash_list=$(basename "${url}")
            echo ">>> Downloading hash list from ${url}"
            curl -q "${url}" > "${hash_list}"
            gpg --verify "${hash_list}"

            echo "${line}" >> "$HASHFILE.new"
        elif [ -n "${old_version}" ]; then
            # Line contained a hash entry: Search for newer versions.
            release_version=$(echo "${old_version}" | cut -f 1-2 -d .) # e.g.: 6.8 instead of 6.8.9
            # e.g.: 19b31956d229b5b9ca5671fa1c74320179682a3d8d00fc86794114b21da86039  linux-6.8.12.tar.xz
            new_hash_entry=$(grep "^[0-9a-f]\+ \+linux-${release_version//./\\.}\.[0-9]\+\.tar\.xz$" "${hash_list}" | sort -V --key '2' | tail -1)
            new_hash=$(echo "${new_hash_entry}" | cut -f 1 -d ' ')
            new_version=$(echo "${new_hash_entry}" | cut -f 3 -d ' ' | sed -rn 's|linux-(.+)\.tar\.xz|\1|p')

            if [ "$new_version" == "$old_version" ]; then
                # Same version as before
                echo ">>> Release ${release_version}.x already on newest version ${new_version}."
                echo "${line}" >> "$HASHFILE.new"

                if [ "${old_hash}" != "${new_hash}" ]; then
                    # Hash changed! This should never happen!
                    echo ">>> Version hash changed for ${release_version}.x!"
                    echo ">>> It was: ${old_hash}"
                    echo ">>> It is:  ${new_hash}"
                    exit 1
                fi
            else
                # Different version: update hash list, default headers, and possibly latest
                # version.
                echo ">>> Updating hash for ${release_version}.x in $HASHFILE"
                echo "sha256  ${new_hash}  linux-${new_version}.tar.xz" >> "$HASHFILE.new"

                echo ">>> Updating version from ${old_version} to ${new_version} in Config.in files"
                sed -Ei "s/\tdefault \"${old_version}\"([\t ]+)if /\tdefault \"${new_version}\"\1if /" \
                    package/linux-headers/Config.in.host linux/Config.in
                git mv package/linux-headers/"${old_version}" package/linux-headers/"${new_version}"
                if [ -d linux/"${old_version}" ]; then
                    git mv linux/"${old_version}" linux/"${new_version}"
                fi

                updated_releases_list="${updated_releases_list} ${release_version}.x"
                updated_versions="${updated_versions}\n - ${old_version} -> ${new_version}"
            fi
        else
            # Different line: Reset hash list and just copy this line over as is.
            if [ -e "${hash_list}" ]; then
                rm "${hash_list}"
                hash_list=
            fi
            echo "${line}" >> "$HASHFILE.new"
        fi
    done < "$HASHFILE"

    # We were iterating over this file so we could not edit in-place.
    mv "$HASHFILE.new" "$HASHFILE"
    updated_files_list+=("$HASHFILE")
}

while IFS= read -r -d '' hashfile; do
    update_linux_hash "$hashfile"
done < <(find linux/ -name '*.hash' -type f -print0)

if [ -n "${updated_releases_list}" ]; then
    git add "${updated_files_list[@]}" linux/Config.in package/linux-headers/Config.in.host
    git commit --verbose --edit --signoff --file=<( {
        echo "{linux, linux-headers}: bump${updated_releases_list//.x /.x, } series"
        echo
        echo "${updated_versions}"
    } )
else
    echo '>>> Nothing happened.'
fi
