#!/bin/sh
# Full checks: exact source pins, shell syntax, Go analysis, integration tests,
# vulnerability scanning, Android JVM tests, and Android lint.
set -eu

root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
# shellcheck source=versions
. "$root/versions"

for command in cmp git govulncheck gradle go gofmt rsync sha256sum shellcheck; do
	command -v "$command" >/dev/null 2>&1 || {
		echo "ci: $command is required" >&2
		exit 1
	}
done

if [ -f "$root/rsync/rsync-${RSYNC_VERSION}.tar.gz" ]; then
	echo "ci: verifying pinned rsync checksum"
	( cd "$root/rsync" && sha256sum -c "rsync-${RSYNC_VERSION}.tar.gz.sha256" )
fi

# These files repeat values from ./versions because Gradle and Go cannot source
# a POSIX shell file. Exact source lines keep comments from satisfying a pin.
line() {
	grep -Fqx -e "$2" "$root/$1" || {
		echo "ci: $1 does not contain exact pin: $2" >&2
		exit 1
	}
}

echo "ci: version pins"
line build.gradle "    id 'com.android.application' version '${AGP_VERSION}' apply false"
line build.gradle "    id 'org.jetbrains.kotlin.android' version '${KOTLIN_VERSION}' apply false"
line gradle/wrapper/gradle-wrapper.properties "distributionUrl=https\\://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip"
line gradle/wrapper/gradle-wrapper.properties "distributionSha256Sum=${GRADLE_SHA256}"
line app/build.gradle "    compileSdk ${ANDROID_PLATFORM}"
line app/build.gradle "    buildToolsVersion '${ANDROID_BUILD_TOOLS}'"
line app/build.gradle "        minSdk ${ANDROID_MIN_SDK}"
line app/build.gradle "        targetSdk ${ANDROID_TARGET_SDK}"
line app/build.gradle "        versionCode ${APP_VERSION_CODE}"
line app/build.gradle "        versionName '${APP_VERSION}'"
line app/build.gradle "    implementation 'androidx.core:core-ktx:${ANDROIDX_CORE_VERSION}'"
line app/build.gradle "    implementation 'androidx.appcompat:appcompat:${ANDROIDX_APPCOMPAT_VERSION}'"
line app/build.gradle "    implementation 'androidx.work:work-runtime:${ANDROIDX_WORK_VERSION}'"
line app/build.gradle "    testImplementation 'junit:junit:${JUNIT_VERSION}'"
line app/build.gradle "    testImplementation 'org.json:json:${JSON_VERSION}'"
line rsh/go.mod "go ${GO_VERSION}"
line rsh/go.mod "require golang.org/x/crypto v${X_CRYPTO_VERSION}"
line rsh/go.mod "require golang.org/x/sys v${X_SYS_VERSION} // indirect"
line THIRD_PARTY "- rsync ${RSYNC_VERSION}, GPLv3. Source: https://download.samba.org/pub/rsync/src/."
line THIRD_PARTY "- The Go standard library from Go ${GO_VERSION}, BSD 3-clause."
line THIRD_PARTY "- golang.org/x/crypto ${X_CRYPTO_VERSION}, BSD 3-clause."
line THIRD_PARTY "APK. golang.org/x/sys ${X_SYS_VERSION} is an indirect module-graph dependency but no"
line THIRD_PARTY "- AndroidX Core ${ANDROIDX_CORE_VERSION}, AppCompat ${ANDROIDX_APPCOMPAT_VERSION}, and WorkManager ${ANDROIDX_WORK_VERSION},"
cmp "$root/APACHE-2.0" "$root/app/src/main/assets/APACHE-2.0" || {
	echo "ci: packaged APACHE-2.0 differs from repository APACHE-2.0" >&2
	exit 1
}
cmp "$root/LICENSE" "$root/app/src/main/assets/LICENSE" || {
	echo "ci: packaged LICENSE differs from repository LICENSE" >&2
	exit 1
}
cmp "$root/THIRD_PARTY" "$root/app/src/main/assets/THIRD_PARTY" || {
	echo "ci: packaged THIRD_PARTY differs from repository THIRD_PARTY" >&2
	exit 1
}
[ -s "$root/gradle/verification-metadata.xml" ] || {
	echo "ci: Gradle dependency verification metadata is missing" >&2
	exit 1
}
tracked_files=$(git -C "$root" ls-files)
tracked_secrets=
if tracked_secrets=$(printf '%s\n' "$tracked_files" |
	grep -E '(^|/)(keystore\.properties|[^/]*\.(jks|keystore))$'); then
	:
else
	status=$?
	[ "$status" -eq 1 ] || exit "$status"
fi
[ -z "$tracked_secrets" ] || {
	echo "ci: signing secret is tracked:" >&2
	printf '%s\n' "$tracked_secrets" >&2
	exit 1
}
abi_filters=
for abi in $ABIS; do
	if [ -z "$abi_filters" ]; then
		abi_filters="'$abi'"
	else
		abi_filters="$abi_filters, '$abi'"
	fi
done
line app/build.gradle "        ndk { abiFilters $abi_filters }"

checksum="$root/rsync/rsync-${RSYNC_VERSION}.tar.gz.sha256"
[ -f "$checksum" ] || { echo "ci: missing $checksum" >&2; exit 1; }
[ "$(awk 'NF == 2 { print $2 }' "$checksum")" = "rsync-${RSYNC_VERSION}.tar.gz" ] || {
	echo "ci: checksum filename does not match rsync pin" >&2
	exit 1
}

changelog="fastlane/metadata/android/en-US/changelogs/${APP_VERSION_CODE}.txt"
[ -s "$root/$changelog" ] || { echo "ci: missing $changelog for version ${APP_VERSION}" >&2; exit 1; }
chars=$(wc -c < "$root/$changelog")
[ "$chars" -le 500 ] || { echo "ci: $changelog is $chars bytes, over F-Droid's 500 limit" >&2; exit 1; }

echo "ci: shell syntax"
shell_scripts=$(git -C "$root" ls-files --cached --others --exclude-standard '*.sh')
printf '%s\n' "$shell_scripts" |
while IFS= read -r script; do
	sh -n "$root/$script"
done
( cd "$root" && shellcheck -x ci/*.sh rsync/build.sh rsh/build.sh )
em_dash=$(printf '\342\200\224')
if git -C "$root" grep --untracked -n -I "$em_dash"; then
	echo "ci: em dash is not allowed" >&2
	exit 1
else
	status=$?
	[ "$status" -eq 1 ] || exit "$status"
fi

echo "ci: gofmt"
unformatted=$(gofmt -l "$root/rsh")
[ -z "$unformatted" ] || { echo "ci: gofmt needed for:$unformatted" >&2; exit 1; }

echo "ci: go module checksums"
( cd "$root/rsh" && go mod verify )

echo "ci: go vet"
( cd "$root/rsh" && go vet ./... )

echo "ci: go test (race detector)"
( cd "$root/rsh" && go test -race ./... )

# govulncheck queries vuln.go.dev and sends module paths, not source code.
echo "ci: govulncheck"
( cd "$root/rsh" && govulncheck ./... )

echo "ci: Android unit tests"
( cd "$root" && gradle --no-daemon :app:testReleaseUnitTest )

echo "ci: Android lint"
( cd "$root" && gradle --no-daemon :app:lintRelease )

echo "ci: ok"
