#!/usr/bin/env bash

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

# End-to-end test for release.sh against a throwaway git repository, a
# throwaway GPG key and a fake ASF dist area served over file://. Nothing
# touches the network or the real repository. Needs git, gpg, curl, tar,
# sha512sum.

set -euo pipefail

RELEASE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/release.sh"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
cd "$WORK"

VERSION=1.2.3
NAME="apache-casbin-sqlx-adapter-$VERSION-incubating-src"
ASF="$WORK/asf"
RELEASE_DIR="$ASF/sqlx-adapter-$VERSION-incubating"

# curl wants file:///C:/... on Windows and file:///tmp/... elsewhere.
file_url() {
    local p
    p="$(cygpath -m "$1" 2>/dev/null || printf '%s' "$1")"
    case "$p" in /*) echo "file://$p" ;; *) echo "file:///$p" ;; esac
}

pass() { echo "ok   - $*"; }
expect_fail() {
    local what="$1"; shift
    if "$@" >/dev/null 2>&1; then
        echo "FAIL - $what: expected failure, but it succeeded" >&2
        exit 1
    fi
    pass "$what (rejected)"
}

commit() {
    git -c user.name=test -c user.email=test@example.invalid commit -q --allow-empty -am "$1"
}

# --- fixture repository ----------------------------------------------------
mkdir repo && cd repo
git init -q
printf '[package]\nname = "casbin-sqlx-adapter"\nversion = "0.0.0"\nedition = "2018"\n' >Cargo.toml
echo 'fixture' >LICENSE
git add . && commit 'fixture'

# --- meta --------------------------------------------------------------------
out="$WORK/meta"
GITHUB_OUTPUT="$out" "$RELEASE" meta v1.2.3
grep -qx "version=1.2.3" "$out" && grep -qx "is_rc=false" "$out" && grep -qx "basename=$NAME" "$out"
pass "meta v1.2.3"
: >"$out"
GITHUB_OUTPUT="$out" "$RELEASE" meta v1.2.3-rc7
grep -qx "version=1.2.3" "$out" && grep -qx "is_rc=true" "$out" && grep -qx "basename=$NAME" "$out"
pass "meta v1.2.3-rc7 (same artifact name as the final release)"
for tag in 1.2.3 v1.2 v1.2.3-rc0 v1.2.3-rc v1.2.3-beta1 v1.2.3rc1 master; do
    expect_fail "meta $tag" "$RELEASE" meta "$tag"
done

# --- set-version -------------------------------------------------------------
"$RELEASE" set-version "$VERSION" >/dev/null
grep -qx "version = \"$VERSION\"" Cargo.toml && pass "set-version writes $VERSION"
expect_fail "set-version twice (placeholder already gone)" "$RELEASE" set-version "$VERSION"
expect_fail "set-version with rc suffix" "$RELEASE" set-version "$VERSION-rc1"
git checkout -q -- Cargo.toml

# --- archive -----------------------------------------------------------------
"$RELEASE" archive "$VERSION" "$WORK/dist" >/dev/null
(cd "$WORK/dist" && sha512sum -c --quiet "$NAME.tar.gz.sha512")
pass "archive produces tarball and matching .sha512"
tar -tzf "$WORK/dist/$NAME.tar.gz" | grep -vq "^$NAME/" && { echo "FAIL - entries outside $NAME/" >&2; exit 1; }
tar -xzf "$WORK/dist/$NAME.tar.gz" -C "$WORK" "$NAME/Cargo.toml"
grep -qx "version = \"$VERSION\"" "$WORK/$NAME/Cargo.toml" && pass "archive carries the tag version"
grep -qx 'version = "0.0.0"' Cargo.toml && [[ -z "$(git status --porcelain)" ]] && pass "archive leaves the working tree untouched"
expect_fail "archive with rc suffix" "$RELEASE" archive "$VERSION-rc1" "$WORK/dist-rc"

# --- fake ASF dist area -------------------------------------------------------
(umask 077 && mkdir "$WORK/gnupg" "$WORK/gnupg-other")
gpg --batch --quiet --homedir "$WORK/gnupg" --passphrase '' --quick-generate-key 'Release Manager <rm@example.invalid>' default default never
gpg --batch --quiet --homedir "$WORK/gnupg-other" --passphrase '' --quick-generate-key 'Someone Else <other@example.invalid>' default default never
mkdir -p "$RELEASE_DIR"
gpg --batch --quiet --homedir "$WORK/gnupg" --armor --export >"$ASF/KEYS"
cp "$WORK/dist/$NAME.tar.gz" "$WORK/dist/$NAME.tar.gz.sha512" "$RELEASE_DIR/"
sign() { gpg --batch --quiet --homedir "$1" --armor --yes --detach-sign -o "$RELEASE_DIR/$NAME.tar.gz.asc" "$RELEASE_DIR/$NAME.tar.gz"; }
sign "$WORK/gnupg"
export ASF_DIST_URL
ASF_DIST_URL="$(file_url "$ASF")"

# --- verify: happy path -------------------------------------------------------
"$RELEASE" verify "$VERSION" "$WORK/verified"
for f in "$NAME.tar.gz" "$NAME.tar.gz.sha512" "$NAME.tar.gz.asc"; do
    cmp -s "$WORK/verified/$f" "$RELEASE_DIR/$f"
done
pass "verify accepts the promoted files and keeps them byte for byte"
expect_fail "verify into a non-empty dir" "$RELEASE" verify "$VERSION" "$WORK/verified"

# --- verify: rejections -------------------------------------------------------
expect_fail "verify with rc suffix" "$RELEASE" verify "$VERSION-rc1" "$WORK/v0"
expect_fail "verify unknown version (nothing promoted)" "$RELEASE" verify 9.9.9 "$WORK/v1"

mv "$RELEASE_DIR/$NAME.tar.gz.asc" "$WORK/asc.bak"
expect_fail "verify without .asc" "$RELEASE" verify "$VERSION" "$WORK/v2"
mv "$WORK/asc.bak" "$RELEASE_DIR/$NAME.tar.gz.asc"

sign "$WORK/gnupg-other"
expect_fail "verify with a signature from a key not in KEYS" "$RELEASE" verify "$VERSION" "$WORK/v3"
sign "$WORK/gnupg"

cp "$RELEASE_DIR/$NAME.tar.gz" "$WORK/tgz.bak"
printf 'x' >>"$RELEASE_DIR/$NAME.tar.gz"
expect_fail "verify with a tampered tarball" "$RELEASE" verify "$VERSION" "$WORK/v4"
cp "$WORK/tgz.bak" "$RELEASE_DIR/$NAME.tar.gz"

# Valid checksum and signature, but the tag no longer points at the voted
# sources: this is the case the download step alone cannot catch.
echo 'changed after the vote' >>LICENSE
commit 'post-vote change'
expect_fail "verify when the tag differs from the voted sources" "$RELEASE" verify "$VERSION" "$WORK/v5"
git reset -q --hard HEAD~1
"$RELEASE" verify "$VERSION" "$WORK/v6" >/dev/null 2>&1 && pass "verify passes again at the voted commit"

echo "all release.sh checks passed"
