Blob Blame History Raw
#!/bin/bash
# vim: sw=4:sts=4:et

fetch_cache()
{
    local CACHE_URL=$1
    local CACHE_FILE=$2
    local EXPIRE=$3
    local REFRESH=$4

    [ -n "$REFRESH" ] && rm "$CACHE_FILE" 2>/dev/null
    if [[ $(find "$CACHE_FILE" -mtime -${EXPIRE:-7} -print 2>/dev/null) \
            && -s "$CACHE_FILE" ]]; then
        echo $CACHE_FILE
        return
    fi
    curl -L "$CACHE_URL" -o "$CACHE_FILE" >/dev/null 2>&1 && echo $CACHE_FILE
}

fetch_branches()
{
    local CACHED_BRANCHES="/tmp/$USER-branches.conf"
    local URL="https://kerncvs.suse.de/branches.conf"
    local REFRESH=$1
    branches=$CACHED_BRANCHES
    fetch_cache $URL $CACHED_BRANCHES 7 $REFRESH
}

fetch_cve2bugzilla()
{
    local CACHED_CVE2BSC="/tmp/$USER-cve2bugzilla"
    local URL="https://gitlab.suse.de/security/cve-database/-/raw/master/data/cve2bugzilla"
    local REFRESH=$1
    fetch_cache $URL $CACHED_CVE2BSC 1 $REFRESH
}

cve2bugzilla()
{
    local CVE=$1
    local CVE2BUGZILLA=$(fetch_cve2bugzilla $2)
    local NR_TO_REPORT=1
    # The first bsc should be the actual report others are product specific (e.g. LP)
    for bsc in $(grep -w $CVE $CVE2BUGZILLA | cut -d: -f2 | head -n $NR_TO_REPORT)
    do
        echo -n "bsc#$bsc"
    done
}

fetch_cve2cvss()
{
    local CACHED_CVE2CVSS="/tmp/$USER-cve2cvss"
    local URL="http://ftp.suse.com/pub/projects/security/yaml/suse-cvss-scores.yaml"
    local REFRESH=$1
    fetch_cache $URL $CACHED_CVE2CVSS 1 $REFRESH
}

cve2cvss()
{
    local CVE=$1
    local REFRESH=$2
    local CVE2CVSS=$(fetch_cve2cvss $REFRESH)
    local cvss="$(grep -w $CVE -A3 $CVE2CVSS | grep score:)"

    echo ${cvss##*:}
}

cve2sha()
{
	local arg=$1
	local REFRESH=$2
	sha="$(cd $VULNS_GIT; [ -n "$REFRESH" ] && git pull >/dev/null 2>&1; scripts/cve_search $arg 2>/dev/null | head -n1 | cut -d" " -f7)"
	
	if [ $(echo $sha | wc -c) -eq 41 ]
	then
		echo $sha
	fi
}

sha2cve()
{
	local arg=$1
	local REFRESH=$2
	cve_sha="$(cd $VULNS_GIT; [ -n "$REFRESH" ] && git pull >/dev/null 2>&1; scripts/cve_search $arg 2>/dev/null | cut -d" " -f1,7)"
	
	if [ $(echo ${cve_sha##* } | wc -c) -eq 41 ]
	then
		echo ${cve_sha%% *}
	fi
}

current_branch()
{
    git branch --show-current
}

print_merge_branches()
{
    local branches_conf="$1"
    local branch="$2"
    local merge_branch=

    [ -z "$branches_conf" ] && fail "megre_branches: No branches_conf provided"
    [ -z "$branch" ] && fail "merge_branches: No branch provided"

    for word in $(grep -w "^$branch:" "$branches_conf") ; do
	if [ "${word#merge:}" != "$word" ] ; then
	    merge_branch="${word#merge:}"
	    merge_branch="${merge_branch#-}"
	    [ -z "$merge_branch" ] && fail "print_merge_branges: non supported syntax"
	    echo "$merge_branch"
	fi
    done
}

for_each_build_branch()
{
    local branches_conf="$1"
    local fn="$2"
    shift 2
    local args="$@"

    grep -w build "$branches_conf" | grep -v -E "^(master|vanilla|linux-next|stable|slowroll)" | \
	while read line ; do
	    line=${line%%\#*}
	    branch=${line%%:*}

	    # empty line or comment
	    if [ -z "$branch" ] ; then
	       continue
	    fi

	    $fn $branch $args || break
	done
}

fail()
{
	echo $* >&2
	exit 1
}

branch_base_ver()
{
    local branch="origin/$1"
    git show-ref --verify --quiet "refs/remotes/${branch}" || fail "$branch invalid branch. Please git fetch origin."

    local base_ver="v$(git grep SRCVERSION $branch -- rpm/config.sh | sed 's@.*=@@')"

    echo $base_ver
}

sha_get_upstream_git_fixes()
{
    local sha=$1
    local upstream_git=${2:-$LINUX_GIT}

    [ -z "$sha" ] && fail "No commit provided"
    [ -z "$upstream_git" ] && fail "No upstream git tree"

    git --git-dir="$upstream_git/.git" show $sha | grep -i "^[[:space:]]*fixes:" | awk '{print $2}'
}

print_upstream_sha_info()
{
    local sha=$1
    local upstream_git=${2:-$LINUX_GIT}

    echo -n "$(git --git-dir="$upstream_git/.git" show  -s --pretty='format:%h ("%s")' $sha) merged "
    git --git-dir="$upstream_git/.git" describe --contains --abbrev=0 --match="v*" $sha
}

print_upstream_sha_summary()
{
    local sha=$1
    local expl_fixes=$2
    local upstream_git=${3:-$LINUX_GIT}
    local has_fixes=0

    # FIXME ugh
    [ "$expl_fixes" = "none" ] && expl_fixes=""

    print_upstream_sha_info $sha $upstream_git
    for fix in $(sha_get_upstream_git_fixes $1 $upstream_git) $expl_fixes
    do
	    echo -n "Fixes: "
	    print_upstream_sha_info $fix $upstream_git
	    has_fixes=1
    done

    if [ $has_fixes -eq 0 ]
    then
	    echo "No Fixes tag. Requires manual review for affected branches."
    fi
}

sha_merged_in_upstream_tag()
{
    local sha=$1
    local base=$2
    local upstream_git=${3:-$LINUX_GIT}

    [ -z "$sha" ] && fail "sha_merged_in_upstream_tag: No sha provided"
    [ -z "$base" ] && fail "sha_merged_in_upstream_tag: No base provided"
    [ -z "$upstream_git" ] && fail "sha_merged_in_upstream_tag: No upstream git tree"

    git --git-dir="$LINUX_GIT/.git" merge-base --is-ancestor "$sha" "$base" 2>/dev/null
}

sha_in_upstream()
{
    local sha=$1
    local upstream_git=${2:-$LINUX_GIT}

    [ -z "$sha" ] && fail "sha_in_upstream: No sha provided"
    [ -z "$upstream_git" ] && fail "sha_in_upstream: No upstream git tree. LINUX_GIT should point to Linus git tree clone."

    sha_merged_in_upstream_tag $sha origin/master $upstream_git
}


sha_has_git_fixes()
{
    local sha="$1"
    local base="$2"
    local upstream_git=${3:-$LINUX_GIT}

    [ -z "$sha" ] && fail "sha_affected_by_git_fixes: No sha provided"
    [ -z "$base" ] && fail "sha_affected_by_git_fixes: No tag provided"
    [ -z "$upstream_git" ] && fail "sha_affected_by_git_fixes: No upstream_git provided"

    # Check git fixes when the bug was introduced
    local git_fixes="$(sha_get_upstream_git_fixes $sha)"

    test -n "$git_fixes"
}


affected_by_git_fixes()
{
    local branch="$1"
    local base="$2"
    shift 2
    local git_fixes="$@"

    [ -z "$branch" ] && fail "affected_by_git_fixes: No branch provided"
    [ -z "$base" ] && fail "affected_by_git_fixes: No tag provided"
    [ -z "$git_fixes" ] && fail "affected_by_git_fixes: No git fixes provided"

    # Check git fixes when the bug was introduced
    local git_fix=
    local affected_by=

    for git_fix in $git_fixes ; do
	local needs_fix=

	# Is it merged in the upstream base kernel?
	if sha_merged_in_upstream_tag "$git_fix" "$base" ; then
	    needs_fix=1
	fi

	# Do we have it backported?
	if sha_merged_in_suse_tree "$git_fix" "$branch" ; then
	    needs_fix=1
	fi

	if [ -n "$needs_fix" ] ; then
	    if [ -z "$affected_by" ] ; then
		affected_by="$git_fix"
	    else
		affected_by="$affected_by $git_fix"
	    fi
	fi
    done

    if [ -n "$affected_by" ] ; then
	echo "Fixes: $affected_by"
    fi
}

sha_to_patch_in_branch()
{
    local sha="$1"
    local branch="$2"

    [ -z "$sha" ] && fail "sha_to_patch_in_branch: No sha provided"
    [ -z "$branch" ] && fail "sha_to_patch_in_branch: No branch provided"

    branch_file=$(git --no-pager grep -l -i "^git-commit[[:space:]]*:[[:space:]]*$sha" "origin/$branch" 2>/dev/null )

    echo "${branch_file#origin/$branch:}"
}

sha_to_patch()
{
    local sha="$1"

    [ -z "$sha" ] && fail "sha_to_patch: No sha provided"

    git --no-pager grep -l -i "^git-commit[[:space:]]*:[[:space:]]*$sha"
}

sha_merged_in_suse_tree()
{
    local sha="$1"
    local branch="$2"

    [ -z "$sha" ] && fail "sha_merged_in_suse_tree: No sha provided"
    [ -z "$branch" ] && fail "sha_merged_in_suse_tree: No branch provided"

    local patch=$(sha_to_patch_in_branch "$sha" "$branch")

    test -n "$patch"
}

references_to_patches_in_branch()
{
    local branch="$1"
    shift
    local references="$@"

    [ -z "$branch" ] && fail "references_to_patches_in_branch: No branch provided"
    [ -z "$references" ] && fail "references_to_patches_in_branch: No references provided"

    local pattern_prefix="^references[[:space:]]*:[[:space:]]*"
    local pattern=

    for ref in $references ; do
	[ -n "$pattern" ] && pattern="$pattern|"
	pattern="$pattern$pattern_prefix$ref"
    done

    branch_files=$(git --no-pager grep -l -E -i "$pattern" "origin/$branch")

    for branch_file in $branch_files ; do
	echo "${branch_file#origin/$branch:}"
    done
}

patch_has_reference()
{
    local ref="$1"
    local patch="$2"

    [ -z "$patch" ] && fail "No patch provided"
    [ -z "$ref" ] && fail "No reference provided"

    grep -q -i "^references:.*$ref" "$patch"
}

patch_has_reference_in_branch()
{
    local patch="$1"
    local ref="$2"
    local branch="$3"

    [ -z "$patch" ] && fail "patch_has_reference_in_branch: No patch provided"
    [ -z "$ref" ] && fail "patch_has_reference_in_branch: No reference provided"
    [ -z "$branch" ] && fail "patch_has_reference_in_branch: No branch provided"

    git --no-pager grep -w -q -i "^references:.*$ref" "origin/$branch" -- "$patch"
}

sha_has_reference_in_branch()
{
    local sha="$1"
    local ref="$2"
    local branch="$3"
    local patch=

    [ -z "$sha" ] && fail "sha_has_reference_in_branch: No sha provided"
    [ -z "$ref" ] && fail "sha_has_reference_in_branch: No reference provided"
    [ -z "$branch" ] && fail "sha_has_reference_in_branch: No branch provided"

    patch=$(sha_to_patch_in_branch "$sha" "$branch")

    if [ -n "$patch" ] ; then
	patch_has_reference_in_branch "$patch" "$ref" "$branch"
    else
	# no patch, no refence needed
	true
    fi
}

patch_add_reference()
{
    local ref=$1
    local patch=$2

    [ -z "$patch" ] && fail "No patch provided"
    [ -z "$ref" ] && fail "No reference provided"

    if ! patch_has_reference "$ref" "$patch" ; then
	local references=$(grep -i "^references:" $patch | sed -e 's/^[Rr]eferences:[[:space:]]//')

	references="$references $ref"
	patch-tag --delete "references" "$patch"
	patch-tag --add "References=$references" "$patch"

	change_to_commit=1
    fi

    if ! patch_has_reference "$ref" "$patch" ; then
	fail "Failed to add reference '$ref' into $patch"
    fi
}

current_branch_state()
{
    local state_line

    status_line=$(git status | grep "Your branch")

    [ -z "$status_line" ] && fail "Can't get status of the current branch"

    if (echo "$status_line" | grep -q "up to date") ; then
	echo "up to date"
    elif (echo "$status_line" | grep -q "ahead") ; then
	echo "ahead"
    elif (echo "$status_line" | grep -q "behind.*fast-forwarded") ; then
	echo "behind-ff"
    elif (echo "$status_line" | grep -q "have diverged") ; then
	echo "diverged"
    else
	echo "unknown"
    fi
}

push_list_name()
{
    local type=$1

    [ -z "$type" ] && fail "push_list_name: called with no type"

    echo "push-list.$type"
}

push_list_has_branch()
{
    local type=$1
    local branch=$2
    local file=

    [ -z "$type" ] && fail "push_list_has_branch: called with no type"
    [ -z "$branch" ] && fail "push_list_has_branch: called with no branch"

    file=$(push_list_name $type)

    if [ -e "$file" ] ; then
	grep -q "^branch\$" "$file"
    else
	false
    fi
}

push_list_add_branch()
{
    local type="$1"
    local branch="$2"
    local file=

    [ -z "$type" ] && fail "push_list_update: called with no type"
    [ -z "$branch" ] && fail "push_list_has_branch: called with no branch"

    if ! $(push_list_has_branch $type $branch) ; then
	file=$(push_list_name $type)
	echo "$branch" >> "$file"
    fi
}

queue_push()
{
    local branch_state=

    branch_state=$(current_branch_state)

    case "$branch_state" in
	"up to date")
	    // nope
	    ;;
	ahead)
	    push_list_add_branch "ready" $(current_branch)
	    ;;
	*)
	    push_list_add_branch "manual" $(current_branch)
	    ;;
      esac
}

push_list_msg()
{
    local type="$1"
    local msg="$2"
    local file=

    [ -z "$type" ] && fail "push_list_msg: called with no type"
    [ -z "$msg" ] && fail "push_list_msg: called with no message"

    file=$(push_list_name "$type")

    if ! grep -q "^$msg$" "$file" ; then
	echo "$msg" >> "$file"
    fi
}

log_fail()
{
    local msg="$1"

    [ -z "$msg" ] && fail "log_failure: called with no message"

    push_list_msg "failure" "$msg"
    fail "$msg"
}