Create ZIP verified the release tag and archive digest against environment variables the step never received, so packaging failed with an empty comparison and no diagnostic output. Pass the fetch-upstream outputs to the step, refuse to run with a missing provenance value, and report the package contract code when a validator rejects the ZIP. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
494 lines
22 KiB
YAML
494 lines
22 KiB
YAML
name: Background release validation
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
shell-tests:
|
|
name: Shell integration tests
|
|
runs-on: ubuntu-latest
|
|
needs: fetch-upstream
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Validate owned source hygiene
|
|
run: |
|
|
set -euo pipefail
|
|
EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
|
git diff --check "$EMPTY_TREE" "$GITHUB_SHA" -- .
|
|
|
|
- name: Reject committed secrets and release debug flags
|
|
run: |
|
|
set -euo pipefail
|
|
bash tests/release/source-policy.sh
|
|
|
|
- name: Download verified upstream payload
|
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
with:
|
|
name: upstream-zapret2-payload
|
|
path: upstream-payload
|
|
|
|
- name: Hydrate upstream files required by package fixtures
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p zapret2/bin/arm64-v8a zapret2/bin/armeabi-v7a
|
|
install -m 0755 upstream-payload/bin/arm64-v8a/nfqws2 zapret2/bin/arm64-v8a/nfqws2
|
|
install -m 0755 upstream-payload/bin/armeabi-v7a/nfqws2 zapret2/bin/armeabi-v7a/nfqws2
|
|
while IFS= read -r upstream_lua || [ -n "$upstream_lua" ]; do
|
|
[ -z "$upstream_lua" ] && continue
|
|
install -m 0644 "upstream-payload/lua/$upstream_lua" "zapret2/lua/$upstream_lua"
|
|
done < upstream/lua-files.txt
|
|
install -m 0644 upstream-payload/upstream-zapret2.commit zapret2/upstream-zapret2.commit
|
|
install -m 0644 upstream-payload/upstream-zapret2.release zapret2/upstream-zapret2.release
|
|
install -m 0644 upstream-payload/upstream-zapret2.archive.sha256 zapret2/upstream-zapret2.archive.sha256
|
|
|
|
- name: Validate shell syntax and security contracts
|
|
run: |
|
|
set -euo pipefail
|
|
bash -n upstream/fetch-release.sh
|
|
bash tests/release/release-channel.sh
|
|
chmod +x tests/shell/run.sh
|
|
sudo sh tests/shell/run.sh
|
|
|
|
prepare-version:
|
|
name: Prepare Version
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
version_code: ${{ steps.version.outputs.version_code }}
|
|
version_tag: ${{ steps.version.outputs.version_tag }}
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Read canonical release version
|
|
id: version
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION_METADATA=$(sh tools/release-version.sh)
|
|
VERSION=$(sed -n 's/^version=//p' <<<"$VERSION_METADATA")
|
|
VERSION_CODE=$(sed -n 's/^version_code=//p' <<<"$VERSION_METADATA")
|
|
VERSION_TAG=$(sed -n 's/^version_tag=//p' <<<"$VERSION_METADATA")
|
|
[[ -n "$VERSION" && -n "$VERSION_CODE" && -n "$VERSION_TAG" ]]
|
|
|
|
printf '%s\n' "$VERSION_METADATA" >> "$GITHUB_OUTPUT"
|
|
echo "Validating ${VERSION_TAG} (versionCode=${VERSION_CODE})"
|
|
|
|
build-apk:
|
|
name: Build Android APK
|
|
runs-on: ubuntu-latest
|
|
needs: [prepare-version, shell-tests]
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Set up JDK 17
|
|
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
|
|
with:
|
|
java-version: '17'
|
|
distribution: 'temurin'
|
|
cache: 'gradle'
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@0b6dd653ba04f4f93bf581ec31e66cbd7dcb644d # v4
|
|
|
|
- name: Run Android unit tests
|
|
run: |
|
|
chmod +x gradlew
|
|
./gradlew testDebugUnitTest --dependency-verification=strict --no-daemon --stacktrace
|
|
working-directory: android-app
|
|
|
|
- name: Build Debug APK
|
|
run: ./gradlew assembleDebug --dependency-verification=strict --no-daemon --stacktrace
|
|
working-directory: android-app
|
|
|
|
- name: Run Android lint
|
|
run: ./gradlew lintRelease --dependency-verification=strict --no-daemon --stacktrace
|
|
working-directory: android-app
|
|
|
|
- name: Create keystore
|
|
env:
|
|
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
for name in KEYSTORE_BASE64 KEYSTORE_PASSWORD KEY_ALIAS KEY_PASSWORD; do
|
|
[ -n "${!name:-}" ] || { echo "Required signing secret $name is missing" >&2; exit 1; }
|
|
done
|
|
umask 077
|
|
printf '%s' "$KEYSTORE_BASE64" | tr -d '[:space:]' | base64 --decode > keystore.jks
|
|
[ -s keystore.jks ] || { echo "Decoded keystore is empty" >&2; exit 1; }
|
|
chmod 0600 keystore.jks
|
|
keytool -list -keystore keystore.jks -storepass "$KEYSTORE_PASSWORD" -alias "$KEY_ALIAS" >/dev/null
|
|
working-directory: android-app
|
|
|
|
- name: Set version from CI metadata
|
|
run: |
|
|
VERSION="${{ needs.prepare-version.outputs.version }}"
|
|
VERSION_CODE="${{ needs.prepare-version.outputs.version_code }}"
|
|
|
|
echo "Setting version to $VERSION (code: $VERSION_CODE)"
|
|
|
|
# Update build.gradle.kts only after source-policy tests have checked
|
|
# the canonical repository placeholders.
|
|
sed -i "s/versionCode = [0-9]*/versionCode = ${VERSION_CODE}/" app/build.gradle.kts
|
|
sed -i "s/versionName = \"[^\"]*\"/versionName = \"${VERSION}\"/" app/build.gradle.kts
|
|
|
|
# Verify changes
|
|
grep -E "versionCode|versionName" app/build.gradle.kts
|
|
working-directory: android-app
|
|
|
|
- name: Build Release APK
|
|
env:
|
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
run: |
|
|
# Retry build up to 3 times (Maven Central sometimes returns 403)
|
|
for i in 1 2 3; do
|
|
echo "Build attempt $i..."
|
|
if ./gradlew assembleRelease --dependency-verification=strict --no-daemon; then
|
|
echo "Build succeeded on attempt $i"
|
|
break
|
|
fi
|
|
if [ $i -lt 3 ]; then
|
|
echo "Build failed, waiting 30s before retry..."
|
|
sleep 30
|
|
else
|
|
echo "Build failed after 3 attempts"
|
|
exit 1
|
|
fi
|
|
done
|
|
working-directory: android-app
|
|
|
|
- name: Verify APK signature and certificate
|
|
env:
|
|
APK_SIGNING_CERT_SHA256: ${{ secrets.APK_SIGNING_CERT_SHA256 }}
|
|
run: |
|
|
set -euo pipefail
|
|
[ -n "${APK_SIGNING_CERT_SHA256:-}" ] || { echo "Required APK_SIGNING_CERT_SHA256 secret is missing" >&2; exit 1; }
|
|
APK='app/build/outputs/apk/release/app-release.apk'
|
|
[ -s "$APK" ]
|
|
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -mindepth 2 -maxdepth 2 -type f -name apksigner -print | sort -V | tail -n 1)"
|
|
[ -x "$APKSIGNER" ] || { echo "apksigner was not found" >&2; exit 1; }
|
|
VERIFY_OUTPUT="$("$APKSIGNER" verify --verbose --print-certs "$APK")"
|
|
printf '%s\n' "$VERIFY_OUTPUT"
|
|
ACTUAL_CERT="$(
|
|
printf '%s\n' "$VERIFY_OUTPUT" |
|
|
awk '
|
|
/certificate SHA-256 digest:/ {
|
|
digest = $0
|
|
sub(/^.*certificate SHA-256 digest:[[:space:]]*/, "", digest)
|
|
gsub(/:|[[:space:]]/, "", digest)
|
|
print toupper(digest)
|
|
}
|
|
' |
|
|
sort -u
|
|
)"
|
|
EXPECTED_CERT="$(printf '%s' "$APK_SIGNING_CERT_SHA256" | tr -d ':[:space:]' | tr '[:lower:]' '[:upper:]')"
|
|
[ "${#ACTUAL_CERT}" -eq 64 ] || { echo "Unable to read APK signer certificate digest" >&2; exit 1; }
|
|
[ "$ACTUAL_CERT" = "$EXPECTED_CERT" ] || { echo "APK signer certificate digest does not match the release identity" >&2; exit 1; }
|
|
(
|
|
cd "$(dirname "$APK")"
|
|
sha256sum "$(basename "$APK")" > "$(basename "$APK").sha256"
|
|
sha256sum -c "$(basename "$APK").sha256"
|
|
)
|
|
working-directory: android-app
|
|
|
|
- name: Remove private signing material
|
|
if: always()
|
|
run: rm -f keystore.jks
|
|
working-directory: android-app
|
|
|
|
- name: Upload APK
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: zapret2-apk
|
|
path: |
|
|
android-app/app/build/outputs/apk/release/app-release.apk
|
|
android-app/app/build/outputs/apk/release/app-release.apk.sha256
|
|
|
|
fetch-upstream:
|
|
name: Fetch latest upstream zapret2 release
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
sha: ${{ steps.upstream.outputs.sha }}
|
|
tag: ${{ steps.upstream.outputs.tag }}
|
|
archive_sha256: ${{ steps.upstream.outputs.archive_sha256 }}
|
|
|
|
steps:
|
|
- name: Checkout module
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Fetch and verify upstream payload
|
|
id: upstream
|
|
run: |
|
|
set -euo pipefail
|
|
chmod +x upstream/fetch-release.sh
|
|
upstream/fetch-release.sh "$GITHUB_WORKSPACE/upstream-payload"
|
|
|
|
- name: Upload verified upstream payload
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: upstream-zapret2-payload
|
|
path: upstream-payload
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
package-module:
|
|
name: Package Magisk Module
|
|
runs-on: ubuntu-latest
|
|
needs: [prepare-version, fetch-upstream, build-apk]
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Download verified upstream payload
|
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
with:
|
|
name: upstream-zapret2-payload
|
|
path: upstream-payload
|
|
|
|
- name: Download APK
|
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
with:
|
|
name: zapret2-apk
|
|
path: apk-artifact
|
|
|
|
- name: Prepare module
|
|
env:
|
|
UPSTREAM_ZAPRET2_SHA: ${{ needs.fetch-upstream.outputs.sha }}
|
|
UPSTREAM_ZAPRET2_TAG: ${{ needs.fetch-upstream.outputs.tag }}
|
|
UPSTREAM_ZAPRET2_ARCHIVE_SHA256: ${{ needs.fetch-upstream.outputs.archive_sha256 }}
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ needs.prepare-version.outputs.version }}"
|
|
VERSION_CODE="${{ needs.prepare-version.outputs.version_code }}"
|
|
[[ "$UPSTREAM_ZAPRET2_SHA" =~ ^[0-9a-f]{40}$ ]]
|
|
[[ "$UPSTREAM_ZAPRET2_TAG" =~ ^v[0-9] ]]
|
|
grep -Fxq "$UPSTREAM_ZAPRET2_SHA" upstream-payload/upstream-zapret2.commit
|
|
grep -Fxq "$UPSTREAM_ZAPRET2_TAG" upstream-payload/upstream-zapret2.release
|
|
grep -Fxq "$UPSTREAM_ZAPRET2_ARCHIVE_SHA256" upstream-payload/upstream-zapret2.archive.sha256
|
|
|
|
# Create bin directories (ARM only, no x86 emulator support)
|
|
mkdir -p zapret2/bin/arm64-v8a
|
|
mkdir -p zapret2/bin/armeabi-v7a
|
|
|
|
# Copy the verified upstream release binaries.
|
|
install -m 0755 upstream-payload/bin/arm64-v8a/nfqws2 zapret2/bin/arm64-v8a/nfqws2
|
|
install -m 0755 upstream-payload/bin/armeabi-v7a/nfqws2 zapret2/bin/armeabi-v7a/nfqws2
|
|
test -s zapret2/bin/arm64-v8a/nfqws2
|
|
test -s zapret2/bin/armeabi-v7a/nfqws2
|
|
|
|
# Sync all official Lua files from the same release as the binaries.
|
|
# Custom module Lua files (custom_funcs.lua, zapret-multishake.lua,
|
|
# zapret-16kb.lua, zapret-wgobfs.lua) are kept from the module repo.
|
|
UPSTREAM_LUA="upstream-payload/lua"
|
|
test -d "$UPSTREAM_LUA"
|
|
while IFS= read -r upstream_lua || [ -n "$upstream_lua" ]; do
|
|
[ -z "$upstream_lua" ] && continue
|
|
test -f "$UPSTREAM_LUA/$upstream_lua"
|
|
install -m 0644 "$UPSTREAM_LUA/$upstream_lua" "zapret2/lua/$upstream_lua"
|
|
test -s "zapret2/lua/$upstream_lua"
|
|
echo "Synced upstream $upstream_lua"
|
|
done < upstream/lua-files.txt
|
|
install -m 0644 upstream-payload/upstream-zapret2.commit zapret2/upstream-zapret2.commit
|
|
install -m 0644 upstream-payload/upstream-zapret2.release zapret2/upstream-zapret2.release
|
|
install -m 0644 upstream-payload/upstream-zapret2.archive.sha256 zapret2/upstream-zapret2.archive.sha256
|
|
grep 'NFQWS2_COMPAT_VER' "zapret2/lua/zapret-lib.lua" | head -2
|
|
|
|
# Set module version from CI metadata
|
|
sed -i "s/^version=.*/version=v${VERSION}/" module.prop
|
|
sed -i "s/^versionCode=.*/versionCode=${VERSION_CODE}/" module.prop
|
|
sed -i "s|^updateJson=.*|updateJson=https://github.com/youtubediscord/magisk-zapret2/releases/latest/download/update.json|" module.prop
|
|
|
|
# Validate custom Lua files against upstream API
|
|
echo "Validating custom Lua compatibility with upstream..."
|
|
UPSTREAM_CORE="zapret2/lua/zapret-lib.lua zapret2/lua/zapret-antidpi.lua zapret2/lua/zapret-auto.lua"
|
|
CUSTOM_LUA="zapret2/lua/custom_funcs.lua zapret2/lua/zapret-multishake.lua zapret2/lua/zapret-16kb.lua zapret2/lua/zapret-wgobfs.lua"
|
|
WARN_COUNT=0
|
|
|
|
# Extract all "function name(" definitions from upstream core files
|
|
UPSTREAM_FUNCS=$(grep -h '^function [a-zA-Z_][a-zA-Z0-9_]*(' $UPSTREAM_CORE 2>/dev/null | sed 's/function \([a-zA-Z_][a-zA-Z0-9_]*\)(.*/\1/' | sort -u)
|
|
|
|
# Include globals/constants and nfqws2 C bindings used by custom Lua.
|
|
UPSTREAM_GLOBALS="DLOG b_debug VERDICT_PASS VERDICT_DROP HEXDUMP_DLOG_MAX resolve_range instance_cutoff"
|
|
ALL_UPSTREAM="$UPSTREAM_FUNCS $UPSTREAM_GLOBALS"
|
|
|
|
# Check every explicit custom-Lua required-functions declaration.
|
|
# Files without this optional declaration intentionally produce an
|
|
# empty set; that is not a grep/pipeline failure under pipefail.
|
|
for lua_file in $CUSTOM_LUA; do
|
|
[ -f "$lua_file" ] || continue
|
|
BASENAME=$(basename "$lua_file")
|
|
REQUIRED=$(awk '
|
|
/local required[[:space:]]*=[[:space:]]*{/ { capture=1 }
|
|
capture {
|
|
line=$0
|
|
while (match(line, /"[a-zA-Z_][a-zA-Z0-9_]*"/)) {
|
|
print substr(line, RSTART + 1, RLENGTH - 2)
|
|
line=substr(line, RSTART + RLENGTH)
|
|
}
|
|
if ($0 ~ /^[[:space:]]*}[,]?[[:space:]]*$/) exit
|
|
}
|
|
' "$lua_file" | sort -u)
|
|
for func in $REQUIRED; do
|
|
if ! echo "$ALL_UPSTREAM" | tr ' ' '\n' | grep -qx "$func"; then
|
|
echo "::error file=$lua_file::Function '$func' required by $BASENAME not found in upstream core Lua files"
|
|
WARN_COUNT=$((WARN_COUNT + 1))
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ "$WARN_COUNT" -gt 0 ]; then
|
|
echo "::error::$WARN_COUNT upstream API compatibility errors found"
|
|
exit 1
|
|
else
|
|
echo "All custom Lua files compatible with upstream API"
|
|
fi
|
|
|
|
# Make scripts executable
|
|
chmod +x customize.sh service.sh uninstall.sh action.sh
|
|
chmod +x zapret2/scripts/*.sh
|
|
chmod +x zapret2/scripts/lifecycle/*.sh
|
|
chmod +x system/bin/zapret2-start system/bin/zapret2-stop system/bin/zapret2-status system/bin/zapret2-restart system/bin/zapret2-full-rollback
|
|
test "$(sed -n '2p' zapret2/runtime-manifest.tsv | tr -d '\r')" = 'owner_protocol|7|zapret2-firewall'
|
|
. ./zapret2/scripts/package-contract.sh
|
|
package_contract_apply_modes "$PWD" package
|
|
# Runtime validators intentionally require root-owned installed files.
|
|
# Exercise them against a private root-owned package copy, not the
|
|
# developer-owned Actions checkout.
|
|
PACKAGE_AUDIT_ROOT="$(mktemp -d)"
|
|
package_contract_assemble_package "$PWD" "$PACKAGE_AUDIT_ROOT"
|
|
sudo chown -R 0:0 "$PACKAGE_AUDIT_ROOT"
|
|
sudo find "$PACKAGE_AUDIT_ROOT" -type d -exec chmod 0755 {} +
|
|
package_contract_validate_all "$PACKAGE_AUDIT_ROOT" package
|
|
package_contract_validate_modes "$PACKAGE_AUDIT_ROOT" package
|
|
sudo rm -rf "$PACKAGE_AUDIT_ROOT"
|
|
|
|
- name: Create ZIP
|
|
env:
|
|
UPSTREAM_ZAPRET2_SHA: ${{ needs.fetch-upstream.outputs.sha }}
|
|
UPSTREAM_ZAPRET2_TAG: ${{ needs.fetch-upstream.outputs.tag }}
|
|
UPSTREAM_ZAPRET2_ARCHIVE_SHA256: ${{ needs.fetch-upstream.outputs.archive_sha256 }}
|
|
run: |
|
|
set -eo pipefail
|
|
for provenance in UPSTREAM_ZAPRET2_SHA UPSTREAM_ZAPRET2_TAG UPSTREAM_ZAPRET2_ARCHIVE_SHA256; do
|
|
if [ -z "${!provenance:-}" ]; then
|
|
echo "Required upstream provenance value $provenance is missing" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
package_contract_step() {
|
|
local action="$1"
|
|
shift
|
|
PACKAGE_CONTRACT_CODE=""
|
|
PACKAGE_CONTRACT_DETAIL=""
|
|
"$action" "$@" && return 0
|
|
echo "Package contract check $action failed: ${PACKAGE_CONTRACT_CODE:-UNKNOWN} ${PACKAGE_CONTRACT_DETAIL:-}" >&2
|
|
return 1
|
|
}
|
|
VERSION="${{ needs.prepare-version.outputs.version }}"
|
|
VERSION_TAG="${{ needs.prepare-version.outputs.version_tag }}"
|
|
VERSION_CODE="${{ needs.prepare-version.outputs.version_code }}"
|
|
ZIP_NAME="zapret2-magisk-${VERSION_TAG}.zip"
|
|
|
|
# Update update.json with current version and direct asset URL
|
|
sed -i "s/\"version\": \"v[^\"]*\"/\"version\": \"${VERSION_TAG}\"/" update.json
|
|
sed -i "s/\"versionCode\": [0-9]*/\"versionCode\": ${VERSION_CODE}/" update.json
|
|
sed -i "s|\"zipUrl\": \"[^\"]*\"|\"zipUrl\": \"https://github.com/youtubediscord/magisk-zapret2/releases/download/${VERSION_TAG}/${ZIP_NAME}\"|" update.json
|
|
|
|
. ./zapret2/scripts/package-contract.sh
|
|
PACKAGE_ASSEMBLY_ROOT="$(mktemp -d)"
|
|
package_contract_step package_contract_assemble_package "$PWD" "$PACKAGE_ASSEMBLY_ROOT"
|
|
rm -f "$ZIP_NAME"
|
|
(
|
|
cd "$PACKAGE_ASSEMBLY_ROOT"
|
|
zip -r "$GITHUB_WORKSPACE/$ZIP_NAME" \
|
|
module.prop customize.sh service.sh uninstall.sh action.sh system zapret2
|
|
)
|
|
|
|
# Print and assert the distributable layout, file type, mode, and
|
|
# exact wrapper bytes so packaging cannot silently omit system/bin.
|
|
ZIP_LIST="$(mktemp)"
|
|
zipinfo -1 "$ZIP_NAME" | tee "$ZIP_LIST"
|
|
PACKAGE_VALIDATE_ROOT="$(mktemp -d)"
|
|
package_contract_step package_contract_validate_zip_names "$PWD" "$ZIP_LIST"
|
|
unzip -q "$ZIP_NAME" -d "$PACKAGE_VALIDATE_ROOT"
|
|
sudo chown -R 0:0 "$PACKAGE_VALIDATE_ROOT"
|
|
sudo find "$PACKAGE_VALIDATE_ROOT" -type d -exec chmod 0755 {} +
|
|
package_contract_step package_contract_validate_all "$PACKAGE_VALIDATE_ROOT" package
|
|
package_contract_step package_contract_validate_exact_tree "$PACKAGE_VALIDATE_ROOT" package
|
|
package_contract_step package_contract_validate_modes "$PACKAGE_VALIDATE_ROOT" package
|
|
if grep -Eq '^META-INF(/|$)' "$ZIP_LIST"; then
|
|
echo "Recovery flashing metadata must not be published" >&2
|
|
exit 1
|
|
fi
|
|
WRAPPER_EXPECTED="$(mktemp)"
|
|
WRAPPER_ACTUAL="$(mktemp)"
|
|
verify_provenance_entry() {
|
|
local entry="$1" expected="$2"
|
|
if [ "$(grep -Fxc "$entry" "$ZIP_LIST")" -ne 1 ]; then
|
|
echo "Provenance entry $entry is missing from the published ZIP" >&2
|
|
return 1
|
|
fi
|
|
printf '%s\n' "$expected" > "$WRAPPER_EXPECTED"
|
|
unzip -p "$ZIP_NAME" "$entry" > "$WRAPPER_ACTUAL"
|
|
if ! cmp -s "$WRAPPER_EXPECTED" "$WRAPPER_ACTUAL"; then
|
|
echo "Provenance entry $entry does not match the verified upstream value" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
verify_provenance_entry 'zapret2/upstream-zapret2.commit' "$UPSTREAM_ZAPRET2_SHA"
|
|
verify_provenance_entry 'zapret2/upstream-zapret2.release' "$UPSTREAM_ZAPRET2_TAG"
|
|
verify_provenance_entry 'zapret2/upstream-zapret2.archive.sha256' "$UPSTREAM_ZAPRET2_ARCHIVE_SHA256"
|
|
rm -f "$ZIP_LIST" "$WRAPPER_EXPECTED" "$WRAPPER_ACTUAL"
|
|
rm -rf "$PACKAGE_ASSEMBLY_ROOT"
|
|
sudo rm -rf "$PACKAGE_VALIDATE_ROOT"
|
|
|
|
sha256sum "$ZIP_NAME" > "$ZIP_NAME.sha256"
|
|
sha256sum -c "$ZIP_NAME.sha256"
|
|
|
|
# Rename APK with version
|
|
(
|
|
cd apk-artifact
|
|
sha256sum -c app-release.apk.sha256
|
|
)
|
|
cp apk-artifact/app-release.apk zapret2-control-${VERSION_TAG}.apk
|
|
sha256sum zapret2-control-${VERSION_TAG}.apk > zapret2-control-${VERSION_TAG}.apk.sha256
|
|
sha256sum -c zapret2-control-${VERSION_TAG}.apk.sha256
|
|
|
|
- name: Upload module
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: zapret2-magisk-module
|
|
path: |
|
|
zapret2-magisk-*.zip
|
|
zapret2-magisk-*.zip.sha256
|
|
|
|
- name: Upload APK artifact
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: zapret2-control-apk
|
|
path: |
|
|
zapret2-control-*.apk
|
|
zapret2-control-*.apk.sha256
|
|
|
|
- name: Upload release metadata
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: zapret2-release-metadata
|
|
path: update.json
|