mirror of
https://github.com/Prowlarr/Indexers.git
synced 2025-12-19 18:48:24 +00:00
1095 lines
44 KiB
Bash
Executable File
1095 lines
44 KiB
Bash
Executable File
#!/bin/bash
|
|
# shellcheck disable=SC2162
|
|
|
|
## Script to keep Prowlarr/Indexers up to date with Jackett/Jackett
|
|
## Created by Bakerboy448
|
|
##
|
|
## Features:
|
|
### - Controlled logging: DEBUG/VERBOSE modes for troubleshooting
|
|
### - Automated conflict resolution for indexer syncing
|
|
##
|
|
## Requirements
|
|
### Prowlarr/Indexers local git repo exists
|
|
### Set variables as needed
|
|
### Typically only prowlarr_git_path would be needed to be set
|
|
## Using the Script
|
|
### Suggested to run from the current directory being Prowlarr/Indexers local Repo using Git Bash `./scripts/indexer-sync-v2.sh`
|
|
### Use -d for DEBUG logging, -v for VERBOSE logging
|
|
# Default values
|
|
DEBUG=${DEBUG:-false}
|
|
VERBOSE=${VERBOSE:-false}
|
|
prowlarr_remote_name="origin"
|
|
prowlarr_target_branch="master"
|
|
mode_choice="normal"
|
|
push_mode=false
|
|
push_mode_force=false
|
|
prowlarr_push_remote="origin"
|
|
PROWLARR_COMMIT_TEMPLATE="jackett indexers as of"
|
|
PROWLARR_COMMIT_TEMPLATE_APPEND=""
|
|
PROWLARR_REPO_URL="https://github.com/Prowlarr/Indexers.git"
|
|
JACKETT_REPO_URL="https://github.com/Jackett/Jackett.git"
|
|
PROWLARR_RELEASE_BRANCH="master"
|
|
JACKETT_BRANCH="master"
|
|
JACKETT_REMOTE_NAME="z_Jackett"
|
|
SKIP_BACKPORT=false
|
|
is_dev_exec=false
|
|
is_jackett_dev=false
|
|
pulls_exists=false
|
|
local_exist=false
|
|
automation_mode=false
|
|
MAX_COMMITS_TO_PICK=50
|
|
MAX_COMMITS_TO_SEARCH=100
|
|
VALIDATION_SCRIPT="scripts/validate.py"
|
|
|
|
BLOCKLIST=("uniongang.yml" "uniongangcookie.yml" "sharewood.yml" "ygg-api.yml" "yggtorrent.yml" "yggcookie.yml" "anirena.yml" "torrentgalaxy.yml" "torrent-heaven.yml" "scenelinks.yml")
|
|
CONFLICTS_NONYML_EXTENSIONS='\.(cs|js|iss|html|ico|png|csproj)$'
|
|
# Initialize Defaults
|
|
removed_indexers=""
|
|
added_indexers=""
|
|
modified_indexers=""
|
|
newschema_indexers=""
|
|
both_added_new_indexers=""
|
|
BACKPORT_SKIPPED=false
|
|
GIT_DIFF_CMD="git diff --cached --name-only"
|
|
declare -A blocklist_map
|
|
for blocked in "${BLOCKLIST[@]}"; do
|
|
blocklist_map["$blocked"]=1
|
|
done
|
|
|
|
|
|
# Prowlarr Schema Versions
|
|
## v1 frozen 2021-10-13
|
|
## v2 frozen 2022-04-18
|
|
## v1 and v2 purged and moved to v3 2022-06-24
|
|
## v3 purged and frozen 2022-07-22
|
|
## v4 purged and frozen 2022-08-18
|
|
## v5 purged and frozen 2022-10-14
|
|
## v6 purged and frozen 2022-10-14
|
|
## v7 purged and frozen 2024-04-27
|
|
## v8 purged and frozen 2024-04-27
|
|
## v9 purged and frozen 2024-10-13
|
|
# Load schema versions from VERSIONS file
|
|
load_versions() {
|
|
MIN_SCHEMA=10
|
|
MAX_SCHEMA=11
|
|
CURRENT_SCHEMA=11
|
|
|
|
if [ -f "VERSIONS" ]; then
|
|
# shellcheck disable=SC2034
|
|
while IFS='=' read -r key value; do
|
|
case "$key" in
|
|
MIN_VERSION) MIN_SCHEMA="$value" ;;
|
|
MAX_VERSION) MAX_SCHEMA="$value" ;;
|
|
CURRENT_VERSION) CURRENT_SCHEMA="$value" ;;
|
|
esac
|
|
done < <(grep -v '^#' VERSIONS | grep '=')
|
|
fi
|
|
|
|
NEW_SCHEMA=$((MAX_SCHEMA + 1))
|
|
}
|
|
|
|
load_versions
|
|
NEW_VERS_DIR="definitions/v$NEW_SCHEMA"
|
|
mkdir -p "$NEW_VERS_DIR"
|
|
|
|
log() {
|
|
local level="$1"
|
|
local message="$2"
|
|
local color_reset="\033[0m"
|
|
local color_success="\033[0;32m" # Green
|
|
local color_info="\033[0;36m" # Cyan
|
|
local color_warn="\033[0;33m" # Yellow
|
|
local color_debug="\033[0;34m" # Blue
|
|
local color_trace="\033[0;35m" # Magenta
|
|
local color_error="\033[0;31m" # Red
|
|
|
|
# Check if logging level should be output
|
|
case "$level" in
|
|
DEBUG)
|
|
[[ "$DEBUG" != "true" ]] && return
|
|
;;
|
|
VERBOSE)
|
|
[[ "$VERBOSE" != "true" && "$DEBUG" != "true" ]] && return
|
|
;;
|
|
esac
|
|
|
|
local color
|
|
case "$level" in
|
|
SUCCESS)
|
|
level="INFO"
|
|
message="SUCCESS|$message"
|
|
color=$color_success
|
|
;;
|
|
INFO)
|
|
color=$color_info
|
|
;;
|
|
WARN)
|
|
color=$color_warn
|
|
;;
|
|
WARNING)
|
|
color=$color_warn
|
|
level="WARN"
|
|
;;
|
|
DEBUG)
|
|
color=$color_debug
|
|
;;
|
|
VERBOSE)
|
|
color=$color_trace
|
|
;;
|
|
ERROR)
|
|
color=$color_error
|
|
;;
|
|
*)
|
|
color=$color_reset
|
|
;;
|
|
esac
|
|
|
|
echo -e "${color}$(date +'%Y-%m-%dT%H:%M:%S%z')|$level|$message${color_reset}"
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo "Sync Prowlarr indexers with Jackett"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -d Enable DEBUG logging"
|
|
echo " -v Enable VERBOSE logging"
|
|
echo " -f Force push with lease"
|
|
echo " -r REMOTE Prowlarr remote name (default: origin)"
|
|
echo " -b BRANCH Target branch (default: master)"
|
|
echo " -o REMOTE Push remote (default: origin)"
|
|
echo " -m MODE Mode: normal, development (default: normal)"
|
|
echo " -p Enable push mode"
|
|
echo " -z Skip backport"
|
|
echo " -a Automation mode (skip interactive prompts)"
|
|
echo " -c TEMPLATE Commit template"
|
|
echo " -u URL Prowlarr repo URL"
|
|
echo " -j URL Jackett repo URL"
|
|
echo " -R BRANCH Prowlarr release branch"
|
|
echo " -J BRANCH Jackett branch"
|
|
echo " -n NAME Jackett remote name"
|
|
echo ""
|
|
echo "Environment variables:"
|
|
echo " DEBUG=true Enable debug logging"
|
|
echo " VERBOSE=true Enable verbose logging"
|
|
exit 1
|
|
}
|
|
|
|
|
|
determine_schema_version() {
|
|
local def_file="$1"
|
|
log "DEBUG" "Testing schema version of [$def_file]"
|
|
log "VERBOSE" "Extracting version from file path: $def_file"
|
|
|
|
check_version=$(echo "$def_file" | cut -d'/' -f2)
|
|
log "VERBOSE" "Extracted version: $check_version"
|
|
dir="definitions/$check_version"
|
|
schema="$dir/schema.json"
|
|
|
|
log "DEBUG" "Checking file against schema [$schema]"
|
|
local test_output
|
|
$PYTHON_CMD "$VALIDATION_SCRIPT" --single "$def_file" "$schema"
|
|
test_output=$?
|
|
|
|
if [ "$test_output" = 0 ]; then
|
|
log "INFO" "Definition [$def_file] matches schema [$schema]"
|
|
else
|
|
check_version="v0"
|
|
fi
|
|
export check_version=$check_version
|
|
}
|
|
|
|
determine_best_schema_version() {
|
|
local def_file="$1"
|
|
log "INFO" "Determining best schema version for [$def_file]"
|
|
|
|
# Use Python function to find best schema version
|
|
local best_version
|
|
best_version=$($PYTHON_CMD "$VALIDATION_SCRIPT" --find-best-version "$def_file")
|
|
|
|
if [[ "$best_version" =~ ^v([0-9]+)$ ]]; then
|
|
matched_version="${BASH_REMATCH[1]}"
|
|
log "INFO" "Definition [$def_file] best matches schema [$best_version]"
|
|
else
|
|
matched_version=0
|
|
log "WARN" "Definition [$def_file] does not match any schema"
|
|
log "ERROR" "Cardigann update likely needed. Version [v$NEW_SCHEMA] required. Review definition."
|
|
fi
|
|
|
|
export matched_version=$matched_version
|
|
}
|
|
|
|
initialize_script() {
|
|
# Check for Python and virtual environment
|
|
# Check for Python and determine command to use
|
|
PYTHON_CMD=""
|
|
if command -v python3 &> /dev/null; then
|
|
PYTHON_CMD="python3"
|
|
elif command -v python &> /dev/null; then
|
|
PYTHON_CMD="python"
|
|
else
|
|
log "ERROR" "Python could not be found. Check your Python installation"
|
|
exit 1
|
|
fi
|
|
|
|
log "DEBUG" "Using Python command: $PYTHON_CMD"
|
|
|
|
# Check if we have a virtual environment and activate it
|
|
if [ -d ".venv" ]; then
|
|
log "INFO" "Activating virtual environment"
|
|
if [ -f ".venv/bin/activate" ]; then
|
|
# Linux/Mac
|
|
# shellcheck disable=SC1091
|
|
source .venv/bin/activate
|
|
elif [ -f ".venv/Scripts/activate" ]; then
|
|
# Windows
|
|
# shellcheck disable=SC1091
|
|
source .venv/Scripts/activate
|
|
fi
|
|
fi
|
|
|
|
# Check if required Python packages are available
|
|
if ! $PYTHON_CMD -c "import jsonschema, yaml" &>/dev/null; then
|
|
log "ERROR" "required python packages are missing. Install with: pip install -r requirements.txt"
|
|
exit 2
|
|
fi
|
|
|
|
log "INFO" "Using Python validation"
|
|
}
|
|
|
|
while getopts "frpzab:m:c:u:j:R:J:n:o:dv" opt; do
|
|
case ${opt} in
|
|
f)
|
|
# No Arg
|
|
push_mode_force=true
|
|
log "DEBUG" "push_mode_force is $push_mode_force"
|
|
;;
|
|
r)
|
|
prowlarr_remote_name=$OPTARG
|
|
;;
|
|
b)
|
|
prowlarr_target_branch=$OPTARG
|
|
;;
|
|
o)
|
|
prowlarr_push_remote=$OPTARG
|
|
;;
|
|
m)
|
|
mode_choice=$OPTARG
|
|
log "DEBUG" "mode_choice using argument $mode_choice"
|
|
case "$mode_choice" in
|
|
normal | n | N)
|
|
is_dev_exec=false
|
|
;;
|
|
development | dev | d | D)
|
|
is_dev_exec=true
|
|
log "WARN" "Mode: Development"
|
|
log "WARN" "Skipping upstream reset to local. Skip checking out the local Prowlarr branch and output the details."
|
|
log "INFO" "This will not reset Prowlarr branch from upstream/master and will ONLY checkout the selected branch to use."
|
|
log "INFO" "This will pause at various debugging points for human review"
|
|
;;
|
|
jackett | j | J)
|
|
log "WARN" "Mode: Jackett"
|
|
is_dev_exec=true
|
|
is_jackett_dev=true
|
|
log "WARN" "Skipping upstream reset to local. Skip checking out the local Prowlarr branch and output the details."
|
|
log "INFO" "This will not reset Prowlarr branch from upstream/master and will ONLY checkout [$prowlarr_target_branch] branch to use."
|
|
log "INFO" "This will not reset Jackett branch and will use what it currently locally is $JACKETT_REMOTE_NAME$JACKETT_BRANCH"
|
|
log "INFO" "This will pause at various debugging points for human review"
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|
|
;;
|
|
p)
|
|
# No Arg
|
|
push_mode=true
|
|
log "DEBUG" "push_mode is $push_mode"
|
|
;;
|
|
c)
|
|
PROWLARR_COMMIT_TEMPLATE=$OPTARG
|
|
;;
|
|
u)
|
|
PROWLARR_REPO_URL=$OPTARG
|
|
;;
|
|
j)
|
|
JACKETT_REPO_URL=$OPTARG
|
|
;;
|
|
R)
|
|
PROWLARR_RELEASE_BRANCH=$OPTARG
|
|
;;
|
|
J)
|
|
JACKETT_BRANCH=$OPTARG
|
|
;;
|
|
n)
|
|
JACKETT_REMOTE_NAME=$OPTARG
|
|
;;
|
|
z)
|
|
# No Arg
|
|
SKIP_BACKPORT=true
|
|
PROWLARR_COMMIT_TEMPLATE_APPEND="[backports skipped - TODO]"
|
|
log "DEBUG" "SKIP_BACKPORT is $SKIP_BACKPORT. Commit Template will be appended with '$PROWLARR_COMMIT_TEMPLATE_APPEND' if applicable'"
|
|
;;
|
|
a)
|
|
# No Arg
|
|
automation_mode=true
|
|
log "DEBUG" "automation_mode is $automation_mode - interactive prompts will be skipped"
|
|
;;
|
|
d)
|
|
DEBUG=true
|
|
log "INFO" "DEBUG logging enabled"
|
|
;;
|
|
v)
|
|
VERBOSE=true
|
|
log "INFO" "VERBOSE logging enabled"
|
|
;;
|
|
\?)
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
configure_git() {
|
|
git config advice.statusHints false
|
|
git_remotes=$(git remote -v)
|
|
prowlarr_remote_exists=$(echo "$git_remotes" | grep "$prowlarr_remote_name")
|
|
prowlarr_push_remote_exists=$(echo "$git_remotes" | grep "$prowlarr_push_remote")
|
|
jackett_remote_exists=$(echo "$git_remotes" | grep "$JACKETT_REMOTE_NAME")
|
|
|
|
if [ -z "$prowlarr_remote_exists" ]; then
|
|
git remote add "$prowlarr_remote_name" "$PROWLARR_REPO_URL"
|
|
fi
|
|
|
|
if [ -z "$jackett_remote_exists" ]; then
|
|
git remote add "$JACKETT_REMOTE_NAME" "$JACKETT_REPO_URL"
|
|
fi
|
|
|
|
if [ "$prowlarr_push_remote" != "$prowlarr_remote_name" ] && [ -z "$prowlarr_push_remote_exists" ]; then
|
|
log "ERROR" "Push remote [$prowlarr_push_remote] does not exist. Please add it manually or use an existing remote."
|
|
exit 1
|
|
fi
|
|
|
|
log "INFO" "Configured Git"
|
|
if [ "$is_jackett_dev" = true ]; then
|
|
log "DEBUG" "Skipping fetch for jackett development mode"
|
|
else
|
|
git fetch --all --prune --progress
|
|
fi
|
|
# Set default target branch based on push remote URL (only if using default)
|
|
push_remote_url=$(git remote get-url "$prowlarr_push_remote" 2>/dev/null || echo "")
|
|
if [ "$prowlarr_target_branch" = "master" ]; then
|
|
if [[ "$push_remote_url" == *"Prowlarr/Indexers"* ]]; then
|
|
log "DEBUG" "Hello Servarr - Using target [master] branch for Prowlarr repo"
|
|
else
|
|
prowlarr_target_branch="jackett-pulls"
|
|
log "DEBUG" "Hello User - Unable to target [master]. Using target [jackett-pulls] branch for Fork repo"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_branches() {
|
|
local remote_pulls_check local_pulls_check
|
|
|
|
remote_pulls_check=$(git ls-remote --heads "$prowlarr_remote_name" "$prowlarr_target_branch")
|
|
local_pulls_check=$(git branch --list "$prowlarr_target_branch")
|
|
|
|
if [ -z "$local_pulls_check" ]; then
|
|
local_exist=false
|
|
log "WARN" "local branch [$prowlarr_target_branch] does not exist"
|
|
else
|
|
local_exist=true
|
|
log "INFO" "local branch [$prowlarr_target_branch] does exist"
|
|
fi
|
|
|
|
if [ -z "$remote_pulls_check" ]; then
|
|
pulls_exists=false
|
|
log "WARN" "remote repo/branch [$prowlarr_remote_name/$prowlarr_target_branch] does not exist"
|
|
else
|
|
pulls_exists=true
|
|
log "INFO" "remote repo/branch [$prowlarr_remote_name/$prowlarr_target_branch] does exist"
|
|
fi
|
|
}
|
|
|
|
git_branch_reset() {
|
|
if [ "$pulls_exists" = false ]; then
|
|
if [ "$local_exist" = true ]; then
|
|
git checkout -B "$prowlarr_target_branch"
|
|
log "INFO" "Checked out out local branch [$prowlarr_target_branch]"
|
|
if [ "$is_dev_exec" = true ] || [ "$is_jackett_dev" = true ]; then
|
|
log "DEBUG" "[$is_dev_exec] skipping reset to [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
else
|
|
git reset --hard "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH"
|
|
log "WARN" "local branch [$prowlarr_target_branch] hard reset based on remote/branch [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
fi
|
|
else
|
|
git checkout -B "$prowlarr_target_branch" "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH" --no-track
|
|
log "INFO" "local branch [$prowlarr_target_branch] created from remote/branch [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
fi
|
|
else
|
|
if [ "$local_exist" = true ]; then
|
|
git checkout -B "$prowlarr_target_branch"
|
|
log "INFO" "Checked out out local branch [$prowlarr_target_branch]"
|
|
if [ "$is_dev_exec" = true ] || [ "$is_jackett_dev" = true ]; then
|
|
log "DEBUG" "Development Mode - Skipping reset to [$prowlarr_remote_name/$prowlarr_target_branch]"
|
|
else
|
|
git reset --hard "$prowlarr_remote_name"/"$prowlarr_target_branch"
|
|
# Try to rebase on master
|
|
if ! git rebase "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH"; then
|
|
if [ "$automation_mode" = true ]; then
|
|
log "WARN" "Rebase failed in automation mode, starting fresh from master instead"
|
|
git rebase --abort
|
|
git reset --hard "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH"
|
|
log "INFO" "local [$prowlarr_target_branch] reset to master [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
else
|
|
log "ERROR" "Rebase failed due to conflicts with [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
git rebase --abort
|
|
exit 9
|
|
fi
|
|
else
|
|
log "INFO" "local [$prowlarr_target_branch] reset and rebased on [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
fi
|
|
fi
|
|
else
|
|
git checkout -B "$prowlarr_target_branch" "$prowlarr_remote_name"/"$prowlarr_target_branch"
|
|
log "INFO" "local [$prowlarr_target_branch] created from [$prowlarr_remote_name/$prowlarr_target_branch]"
|
|
# Try to rebase on master
|
|
if ! git rebase "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH"; then
|
|
if [ "$automation_mode" = true ]; then
|
|
log "WARN" "Rebase failed in automation mode, starting fresh from master instead"
|
|
git rebase --abort
|
|
git checkout -B "$prowlarr_target_branch" "$prowlarr_remote_name"/"$PROWLARR_RELEASE_BRANCH" --no-track
|
|
log "INFO" "local [$prowlarr_target_branch] created fresh from master [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
else
|
|
log "ERROR" "Rebase failed due to conflicts with [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
git rebase --abort
|
|
exit 9
|
|
fi
|
|
else
|
|
log "INFO" "rebased [$prowlarr_target_branch] on [$prowlarr_remote_name/$PROWLARR_RELEASE_BRANCH]"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
pull_cherry_and_merge() {
|
|
log "INFO" "Reviewing Commits"
|
|
existing_message=$(git log --format=%B -n1)
|
|
existing_message_ln1=$(echo "$existing_message" | awk 'NR==1')
|
|
|
|
log "DEBUG" "Searching for commits with template: '$PROWLARR_COMMIT_TEMPLATE'"
|
|
log "DEBUG" "Searching in last $MAX_COMMITS_TO_SEARCH commits"
|
|
|
|
prowlarr_commits=$(git log --format=%B -n "$MAX_COMMITS_TO_SEARCH" | grep "^$PROWLARR_COMMIT_TEMPLATE")
|
|
log "DEBUG" "Found prowlarr commits count: $(echo "$prowlarr_commits" | wc -l)"
|
|
log "DEBUG" "First prowlarr commit found: $(echo "$prowlarr_commits" | head -1)"
|
|
|
|
prowlarr_jackett_commit_message=$(echo "$prowlarr_commits" | awk 'NR==1')
|
|
log "DEBUG" "Prowlarr jackett commit message: '$prowlarr_jackett_commit_message'"
|
|
|
|
if [ "$is_jackett_dev" = true ]; then
|
|
# Use only local Jackett branch (no remote)
|
|
jackett_ref="$JACKETT_REMOTE_NAME$JACKETT_BRANCH"
|
|
else
|
|
# Normal usage: remote reference
|
|
jackett_ref="$JACKETT_REMOTE_NAME/$JACKETT_BRANCH"
|
|
fi
|
|
if [ "$is_dev_exec" = true ] || [ "$is_jackett_dev" = true ]; then
|
|
log "DEBUG" "Jackett Remote is [$jackett_ref]"
|
|
# read -r -p "Pausing to review commits. Press any key to continue." -n1 -s
|
|
fi
|
|
|
|
jackett_recent_commit=$(git rev-parse "$jackett_ref")
|
|
log "DEBUG" "Jackett recent commit: '$jackett_recent_commit'"
|
|
|
|
# Get and log truncated Jackett commit message
|
|
jackett_commit_message=$(git log --format=%s -n1 "$jackett_recent_commit")
|
|
jackett_commit_message_truncated=$(echo "$jackett_commit_message" | cut -c1-80)
|
|
log "INFO" "Jackett commit message: '$jackett_commit_message_truncated'"
|
|
|
|
recent_pulled_commit=$(echo "$prowlarr_commits" | awk 'NR==1{print $5}')
|
|
log "DEBUG" "Recent pulled commit (field 5): '$recent_pulled_commit'"
|
|
log "DEBUG" "Full first commit line: '$(echo "$prowlarr_commits" | awk 'NR==1')'"
|
|
|
|
if [ -z "$recent_pulled_commit" ]; then
|
|
log "ERROR" "Recent Pulled Commit is empty. Failing."
|
|
log "ERROR" "Debug info:"
|
|
log "ERROR" " - prowlarr_commits found: '$(echo "$prowlarr_commits" | head -3)'"
|
|
log "ERROR" " - Template used: '$PROWLARR_COMMIT_TEMPLATE'"
|
|
log "ERROR" " - Trying to extract field 5 from first line"
|
|
exit 3
|
|
fi
|
|
if [ "$jackett_recent_commit" = "$recent_pulled_commit" ]; then
|
|
log "SUCCESS" "--- we are current with jackett; nothing to do ---"
|
|
exit 0
|
|
fi
|
|
log "INFO" "[$jackett_recent_commit] is the most recent Jackett commit as per branch [$jackett_ref]"
|
|
log "INFO" "[$recent_pulled_commit] is the most recent Prowlarr/Indexer commit pulled from Jackett as per branch [$prowlarr_remote_name/$prowlarr_target_branch]"
|
|
# Define the command to get the commit range
|
|
commit_range_cmd="git log --reverse --pretty='%n%H' $recent_pulled_commit..$jackett_recent_commit"
|
|
# Execute the command and capture the output
|
|
commit_range=$(eval "$commit_range_cmd")
|
|
commit_count=$(git rev-list --count "$recent_pulled_commit".."$jackett_recent_commit")
|
|
log "INFO" "There are [$commit_count] commits to cherry-pick"
|
|
if [ "$is_dev_exec" = true ] || [ "$is_jackett_dev" = true ]; then
|
|
log "DEBUG" "Get Range Command is [$commit_range_cmd]"
|
|
# read -r -p "Pausing to review commits. Press any key to continue." -n1 -s
|
|
fi
|
|
# Enforce maximum commits threshold
|
|
if [ "$commit_count" -gt "$MAX_COMMITS_TO_PICK" ]; then
|
|
log "ERROR" "Commit count [$commit_count] is greater than [$MAX_COMMITS_TO_PICK]. Exiting."
|
|
exit 4
|
|
fi
|
|
log "INFO" "Commit Range is: [$commit_range]"
|
|
log "INFO" "-- Beginning Cherrypicking ---"
|
|
git config merge.directoryRenames true
|
|
git config merge.verbosity 0
|
|
sleep 2
|
|
|
|
for pick_commit in ${commit_range}; do
|
|
has_conflicts=$(git ls-files --unmerged; git status --porcelain | grep "^UU\|^AA\|^DD\|^AU\|^UA\|^DU\|^UD" || true)
|
|
if [ -n "$has_conflicts" ]; then
|
|
resolve_conflicts
|
|
fi
|
|
has_conflicts=$(git ls-files --unmerged; git status --porcelain | grep "^UU\|^AA\|^DD\|^AU\|^UA\|^DU\|^UD" || true)
|
|
if [ -n "$has_conflicts" ]; then
|
|
log "ERROR" "Conflicts Exist [$has_conflicts] - Cannot Cherrypick"
|
|
git status
|
|
if [ "$automation_mode" = true ]; then
|
|
log "ERROR" "Automation mode: Cannot continue with unresolved conflicts"
|
|
exit 5
|
|
else
|
|
read -r -p "Pausing due to conflicts. Press any key to continue when resolved." -n1 -s
|
|
log "INFO" "Continuing Cherrypicking"
|
|
fi
|
|
fi
|
|
log "INFO" "cherrypicking Jackett commit [$pick_commit]"
|
|
|
|
# Get and log the commit message for this specific commit
|
|
pick_commit_message=$(git log --format=%s -n1 "$pick_commit")
|
|
pick_commit_message_truncated=$(echo "$pick_commit_message" | cut -c1-80)
|
|
log "INFO" "Commit message: '$pick_commit_message_truncated'"
|
|
|
|
git cherry-pick --no-commit --rerere-autoupdate --allow-empty --keep-redundant-commits "$pick_commit"
|
|
|
|
# Detect new indexers from this commit before conflict resolution
|
|
new_jackett_indexers=$(git diff --cached --diff-filter=A --name-only | grep "src/Jackett.Common/Definitions/.*\.yml$" || true)
|
|
if [ -n "$new_jackett_indexers" ]; then
|
|
log "INFO" "New indexers from Jackett: [$new_jackett_indexers]"
|
|
fi
|
|
has_conflicts=$(git ls-files --unmerged; git status --porcelain | grep "^UU\|^AA\|^DD\|^AU\|^UA\|^DU\|^UD" || true)
|
|
if [ -n "$has_conflicts" ]; then
|
|
resolve_conflicts
|
|
fi
|
|
git config merge.directoryRenames conflict
|
|
git config merge.verbosity 2
|
|
done
|
|
|
|
log "SUCCESS" "--- Completed cherry picking ---"
|
|
log "INFO" "Evaluating and Reviewing Changes"
|
|
|
|
# Checkout schema files if they exist - expand the glob pattern first
|
|
schema_files=$(find definitions -type f -name "schema.json" -path "*/v[0-9]*/schema.json" 2>/dev/null)
|
|
if [ -n "$schema_files" ]; then
|
|
for schema_file in $schema_files; do
|
|
git checkout HEAD -- "$schema_file" 2>/dev/null || true
|
|
done
|
|
else
|
|
log "DEBUG" "No schema.json files found to checkout"
|
|
fi
|
|
|
|
handle_new_indexers
|
|
handle_modified_indexers
|
|
handle_backporting_indexers
|
|
}
|
|
|
|
resolve_unmerged_files() {
|
|
# Check for any remaining unmerged files and resolve them
|
|
unmerged_files=$(git diff --name-only --diff-filter=U 2>/dev/null || true)
|
|
|
|
if [ -n "$unmerged_files" ]; then
|
|
log "WARN" "Unmerged files detected: [$unmerged_files]"
|
|
echo "$unmerged_files" | while IFS= read -r file; do
|
|
if [ -n "$file" ]; then
|
|
if [[ "$file" == .github/* ]] || [[ "$file" == src/* ]] || [[ "$file" == *.md ]] || [[ "$file" == package*.json ]] || [[ "$file" == .editorconfig ]]; then
|
|
# For non-definition files, prefer our version or remove
|
|
log "DEBUG" "Resolving unmerged non-definition file: [$file]"
|
|
if git ls-files | grep -q "^$file$"; then
|
|
git checkout --ours "$file" 2>/dev/null || git rm --force "$file" 2>/dev/null || true
|
|
else
|
|
git rm --force "$file" 2>/dev/null || true
|
|
fi
|
|
git add "$file" 2>/dev/null || true
|
|
elif [[ "$file" == definitions/* ]]; then
|
|
# For definition files, prefer their version
|
|
log "DEBUG" "Resolving unmerged definition file: [$file]"
|
|
git checkout --theirs "$file" 2>/dev/null || true
|
|
git add "$file" 2>/dev/null || true
|
|
else
|
|
# Default: prefer our version
|
|
log "DEBUG" "Resolving unmerged file (default): [$file]"
|
|
git checkout --ours "$file" 2>/dev/null || git rm --force "$file" 2>/dev/null || true
|
|
git add "$file" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
resolve_conflicts() {
|
|
readme_conflicts=$($GIT_DIFF_CMD | grep -E '^README\.md$')
|
|
nonyml_conflicts=$($GIT_DIFF_CMD | grep -E "$CONFLICTS_NONYML_EXTENSIONS")
|
|
yml_conflicts=$($GIT_DIFF_CMD | grep -E '\.ya?ml$')
|
|
schema_conflicts=$($GIT_DIFF_CMD | grep -E '\.schema\.json$')
|
|
|
|
log "WARN" "conflicts exist"
|
|
if [ -n "$readme_conflicts" ]; then
|
|
log "DEBUG" "README conflict exists; using Prowlarr README"
|
|
git checkout --ours README.md
|
|
git add --force README.md
|
|
fi
|
|
|
|
if [ -n "$schema_conflicts" ]; then
|
|
log "DEBUG" "Schema conflict exists; using Prowlarr schema"
|
|
for file in $schema_conflicts; do
|
|
if git ls-files | grep -q "^$file$"; then
|
|
git checkout --ours "$file"
|
|
git add --force "$file"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Handle "both added" definition files (when Git auto-moves files from Jackett paths)
|
|
both_added_defs=$(git status --porcelain | grep "^AA" | grep "definitions/v[0-9].*\.yml$" | awk '{print $2}')
|
|
if [ -n "$both_added_defs" ]; then
|
|
log "DEBUG" "Both added definition conflicts exist; using Jackett's version: [$both_added_defs]"
|
|
for file in $both_added_defs; do
|
|
log "INFO" "NEW INDEXER: Resolving both-added conflict for [$file]"
|
|
both_added_new_indexers="$both_added_new_indexers $file"
|
|
git checkout --theirs "$file"
|
|
git add --force "$file"
|
|
done
|
|
fi
|
|
|
|
if [ -n "$nonyml_conflicts" ]; then
|
|
log "DEBUG" "Non-YML conflicts exist; removing [\n$nonyml_conflicts\n] files and restoring [package.json package-lock.json .editorconfig]"
|
|
while IFS= read -r file; do
|
|
git rm --force --quiet --ignore-unmatch "$file"
|
|
done <<<"$nonyml_conflicts"
|
|
for file in package.json package-lock.json .editorconfig; do
|
|
if git ls-files | grep -q "^$file$"; then
|
|
git checkout --ours "$file"
|
|
git add --force "$file"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -n "$yml_conflicts" ]; then
|
|
log "DEBUG" "YML conflict exists; [$yml_conflicts]"
|
|
handle_general_yml_conflicts
|
|
handle_definition_conflicts
|
|
fi
|
|
|
|
# Final check and resolution of any remaining unmerged files
|
|
resolve_unmerged_files
|
|
}
|
|
|
|
handle_general_yml_conflicts() {
|
|
# Remove non-definition YAML files (config files, workflows, etc.)
|
|
yml_remove=$(git status --porcelain | grep yml | grep -vi "definitions/" | grep -vi "Definitions/" | grep -v "definitions-update" | awk -F '[ADUMRC]{1,2} ' '{print $2}' | awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }')
|
|
|
|
if [ -n "$yml_remove" ]; then
|
|
log "DEBUG" "Removing non-definition yml files: [$yml_remove]"
|
|
echo "$yml_remove" | while IFS= read -r file; do
|
|
if [ -n "$file" ]; then
|
|
log "DEBUG" "Removing non-definition yml file: [$file]"
|
|
git rm --force --ignore-unmatch "$file" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
handle_definition_conflicts() {
|
|
# Handle indexer definition conflicts with special logic
|
|
yml_conflicts=$($GIT_DIFF_CMD | grep "\.yml" || true)
|
|
if [ -n "$yml_conflicts" ]; then
|
|
yml_defs=$(git status --porcelain | grep yml | grep -i "definitions/")
|
|
yml_add=$(echo "$yml_defs" | grep -v "UD\|D|DU" | awk -F '[ADUMRC]{1,2} ' '{print $2}' | awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }')
|
|
yml_delete=$(echo "$yml_defs" | grep "UD" | awk -F '[ADUMRC]{1,2} ' '{print $2}' | awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }')
|
|
log "DEBUG" "YML Definitions Process: [$yml_defs]"
|
|
log "DEBUG" "YML files to add/process: [$yml_add]"
|
|
log "DEBUG" "YML files to delete: [$yml_delete]"
|
|
for def in $yml_add; do
|
|
log "DEBUG" "Using & Adding Jackett's definition yml; [$def]"
|
|
new_def="${def/src\/Jackett.Common\/Definitions\//definitions/v$MIN_SCHEMA/}"
|
|
if [ "$new_def" != "$def" ]; then
|
|
mkdir -p "$(dirname "$new_def")"
|
|
log "INFO" "NEW INDEXER: Moving [$def] to [$new_def]"
|
|
mv "$def" "$new_def"
|
|
git checkout --theirs "$new_def"
|
|
git add --force "$new_def"
|
|
git rm --f --ignore-unmatch "$def"
|
|
else
|
|
git checkout --theirs "$def"
|
|
git add --force "$def"
|
|
fi
|
|
done
|
|
for def in $yml_delete; do
|
|
log "DEBUG" "Removing definitions Jackett deleted; [$def]"
|
|
git rm --f --ignore-unmatch "$def"
|
|
done
|
|
fi
|
|
}
|
|
|
|
handle_new_indexers() {
|
|
# Debug: Show all staged files first
|
|
all_staged=$(git diff --cached --name-only)
|
|
log "DEBUG" "All staged files: [$all_staged]"
|
|
|
|
# Debug: Show yml files specifically
|
|
yml_staged=$(git diff --cached --name-only | grep ".yml" || true)
|
|
log "DEBUG" "All staged yml files: [$yml_staged]"
|
|
|
|
# Git's automatic directory rename may classify new files as renames (R) instead of additions (A)
|
|
added_indexers=$(git diff --cached --diff-filter=AR --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
log "DEBUG" "New indexers detected (AR filter + v[digit]+): [$added_indexers]"
|
|
|
|
if [ -n "$added_indexers" ]; then
|
|
log "INFO" "New Indexers detected: [$added_indexers]"
|
|
for indexer in ${added_indexers}; do
|
|
base_indexer=$(basename "$indexer")
|
|
log "DEBUG" "Evaluating [$indexer] against BLOCKLIST with name [$base_indexer]"
|
|
# Check if the indexer is in the BLOCKLIST
|
|
if [[ -n "${blocklist_map[$base_indexer]}" ]]; then
|
|
log "INFO" "[$base_indexer] is in the BLOCKLIST. Removing [${indexer}]..."
|
|
git rm --f --ignore-unmatch "$indexer"
|
|
continue
|
|
fi
|
|
log "DEBUG" "Evaluating [$indexer] Cardigann Version"
|
|
if [ -f "$indexer" ]; then
|
|
# Check if this indexer replaces any existing indexers
|
|
if command -v yq &> /dev/null; then
|
|
replaced_indexers=$(yq eval '.replaces[]?' "$indexer" 2>/dev/null || true)
|
|
elif $PYTHON_CMD -c "import yaml" &>/dev/null; then
|
|
replaced_indexers=$($PYTHON_CMD -c "
|
|
import yaml, sys
|
|
try:
|
|
with open('$indexer', 'r') as f:
|
|
data = yaml.safe_load(f)
|
|
replaces = data.get('replaces', [])
|
|
if replaces:
|
|
for item in replaces:
|
|
print(item)
|
|
except:
|
|
pass
|
|
" 2>/dev/null || true)
|
|
fi
|
|
|
|
if [ -n "$replaced_indexers" ]; then
|
|
log "INFO" "Indexer [$indexer] replaces: [$replaced_indexers]"
|
|
for replaced in $replaced_indexers; do
|
|
# Find and remove all versions of the replaced indexer
|
|
for ((i = MAX_SCHEMA; i >= MIN_SCHEMA; i--)); do
|
|
replaced_file="definitions/v$i/${replaced}.yml"
|
|
if [ -f "$replaced_file" ]; then
|
|
log "INFO" "Removing replaced indexer: [$replaced_file]"
|
|
git rm --f --ignore-unmatch "$replaced_file"
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
|
|
determine_schema_version "$indexer"
|
|
log "DEBUG" "Checked Version Output is $check_version"
|
|
if [ "$check_version" != "v0" ]; then
|
|
log "DEBUG" "Schema Test passed."
|
|
updated_indexer=$indexer
|
|
else
|
|
determine_best_schema_version "$indexer"
|
|
if [ "$matched_version" -eq 0 ]; then
|
|
log "WARN" "Version [$NEW_SCHEMA] required. Review definition [$indexer]"
|
|
v_matched="v$NEW_SCHEMA"
|
|
else
|
|
v_matched="v$matched_version"
|
|
fi
|
|
updated_indexer=${indexer/v[0-9]*/$v_matched}
|
|
if [ "$indexer" != "$updated_indexer" ]; then
|
|
log "INFO" "Moving indexer old [$indexer] to new [$updated_indexer]"
|
|
mv "$indexer" "$updated_indexer"
|
|
git rm -f "$indexer"
|
|
git add -f "$updated_indexer"
|
|
else
|
|
log "DEBUG" "Doing nothing; [$indexer] already is [$updated_indexer]"
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
unset indexer
|
|
unset test
|
|
log "INFO" "completed new indexers"
|
|
else
|
|
log "INFO" "No new indexers"
|
|
fi
|
|
|
|
}
|
|
|
|
handle_modified_indexers() {
|
|
modified_indexers=$(git diff --cached --diff-filter=M --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
if [ -n "$modified_indexers" ]; then
|
|
log "INFO" "Reviewing Modified Indexers..."
|
|
for indexer in ${modified_indexers}; do
|
|
log "INFO" "Evaluating [$indexer] Cardigann Version"
|
|
if [ -f "$indexer" ]; then
|
|
determine_schema_version "$indexer"
|
|
log "INFO" "Checked Version Output is $check_version"
|
|
if [ "$check_version" != "v0" ]; then
|
|
log "DEBUG" "Schema Test passed."
|
|
updated_indexer=$indexer
|
|
else
|
|
determine_best_schema_version "$indexer"
|
|
if [ "$matched_version" -eq 0 ]; then
|
|
log "WARN" "Version [$NEW_SCHEMA] required. Review definition [$indexer]"
|
|
v_matched="v$NEW_SCHEMA"
|
|
else
|
|
v_matched="v$matched_version"
|
|
fi
|
|
updated_indexer=${indexer/v[0-9]*/$v_matched}
|
|
if [ "$indexer" != "$updated_indexer" ]; then
|
|
log "INFO" "Version bumped indexer old [$indexer] to new [$updated_indexer]"
|
|
mv "$indexer" "$updated_indexer"
|
|
git checkout HEAD -- "$indexer"
|
|
git add -f "$updated_indexer"
|
|
else
|
|
log "INFO" "Doing nothing; [$indexer] already is [$updated_indexer]"
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
unset indexer
|
|
unset test
|
|
fi
|
|
log "SUCCESS" "--- completed changed indexers ---"
|
|
}
|
|
|
|
handle_backporting_indexers() {
|
|
modified_indexers_vcheck=$(git diff --cached --diff-filter=AM --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
if [ -n "$modified_indexers_vcheck" ]; then
|
|
for indexer in ${modified_indexers_vcheck}; do
|
|
# SC2004: $/${} is unnecessary on arithmetic variables.
|
|
for ((i = MAX_SCHEMA; i >= MIN_SCHEMA; i--)); do
|
|
version="v$i"
|
|
log "DEBUG" "looking for [$version] indexer of [$indexer]"
|
|
indexer_check=$(echo "$indexer" | sed -E "s/v[0-9]+/$version/")
|
|
if [ "$indexer_check" != "$indexer" ] && [ -f "$indexer_check" ]; then
|
|
if [ "$SKIP_BACKPORT" = true ]; then
|
|
log "INFO" "Found [v$i] indexer for [$indexer] - Skipping backporting changes"
|
|
log "DEBUG" "Skipping backporting changes. Skipping further backport checks."
|
|
BACKPORT_SKIPPED=true # Sets the wider variable
|
|
return 0 # Exits the function early
|
|
else
|
|
log "INFO" "Found [v$i] indexer for [$indexer] - comparing to [$indexer_check]"
|
|
log "WARN" "HUMAN! Review this change and ensure no incompatible updates are backported."
|
|
git difftool --no-index "$indexer" "$indexer_check"
|
|
git add "$indexer_check"
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
unset indexer
|
|
unset indexer_check
|
|
fi
|
|
|
|
newschema_indexers=$(git diff --cached --diff-filter=A --name-only | grep ".yml" | grep "v$NEW_SCHEMA")
|
|
if [ -n "$newschema_indexers" ]; then
|
|
for indexer in ${newschema_indexers}; do
|
|
# SC2004: $/${} is unnecessary on arithmetic variables.
|
|
for ((i = MAX_SCHEMA; i >= MIN_SCHEMA; i--)); do
|
|
version="v$i"
|
|
log "DEBUG" "looking for [$version] indexer of [$indexer]"
|
|
indexer_check=$(echo "$indexer" | sed -E "s/v[0-9]+/$version/")
|
|
if [ "$indexer_check" != "$indexer" ] && [ -f "$indexer_check" ]; then
|
|
log "INFO" "Found [v$i] indexer for [$indexer] - comparing to [$indexer_check]"
|
|
log "ERROR" "THIS IS A NEW CARDIGANN VERSION THAT IS REQUIRED"
|
|
log "WARN" "HUMAN! Review this change and ensure no incompatible updates are backported."
|
|
git difftool --no-index "$indexer" "$indexer_check"
|
|
git add "$indexer_check"
|
|
fi
|
|
done
|
|
done
|
|
unset indexer
|
|
unset indexer_check
|
|
fi
|
|
log "SUCCESS" "--- completed backporting indexers ---"
|
|
}
|
|
|
|
cleanup_and_commit() {
|
|
if [ -n "$removed_indexers" ]; then
|
|
for indexer in ${removed_indexers}; do
|
|
log "DEBUG" "looking for previous versions of removed indexer [$indexer]"
|
|
# SC2004: $/${} is unnecessary on arithmetic variables.
|
|
for ((i = MAX_SCHEMA; i >= MIN_SCHEMA; i--)); do
|
|
indexer_remove=$(echo "$indexer" | sed -E "s/v[0-9]+/$version/")
|
|
if [ "$indexer_remove" != "$indexer" ] && [ -f "$indexer_remove" ]; then
|
|
log "INFO" "Found [v$i] indexer for [$indexer] - removing [$indexer_remove]"
|
|
rm -f "$indexer_remove"
|
|
git rm --f --ignore-unmatch "$indexer_remove"
|
|
fi
|
|
done
|
|
done
|
|
unset indexer
|
|
unset indexer_remove
|
|
fi
|
|
|
|
# Recalculated Added / Modified / Removed - include renames (R) for directory rename detection
|
|
staged_added=$(git diff --cached --diff-filter=AR --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
# Combine with both-added indexers resolved during conflicts
|
|
added_indexers=$(echo "$staged_added $both_added_new_indexers" | xargs -n1 | sort -u | xargs)
|
|
modified_indexers=$(git diff --cached --diff-filter=M --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
removed_indexers=$(git diff --cached --diff-filter=D --name-only | grep ".yml" | grep -E "v[[:digit:]]+")
|
|
newschema_indexers=$(git diff --cached --diff-filter=A --name-only | grep ".yml" | grep -E "v$NEW_SCHEMA")
|
|
|
|
# Check if there are added indexers and log if present
|
|
if [ -n "$added_indexers" ]; then
|
|
log "SUCCESS" "Added Indexers are [$added_indexers]"
|
|
fi
|
|
|
|
# Check if there are modified indexers and log if present
|
|
if [ -n "$modified_indexers" ]; then
|
|
log "SUCCESS" "Modified Indexers are [$modified_indexers]"
|
|
fi
|
|
|
|
# Check if there are removed indexers and log if present
|
|
if [ -n "$removed_indexers" ]; then
|
|
log "SUCCESS" "Removed Indexers are [$removed_indexers]"
|
|
fi
|
|
|
|
# Check if there are new schema indexers and log if present
|
|
if [ -n "$newschema_indexers" ]; then
|
|
log "WARN" "New Schema Indexers are [$newschema_indexers]"
|
|
fi
|
|
|
|
if [ -d "$NEW_VERS_DIR" ]; then
|
|
if [ "$(ls -A $NEW_VERS_DIR)" ]; then
|
|
log "ERROR" "THIS IS A NEW CARDIGANN VERSION THAT IS REQUIRED: Version [v$NEW_SCHEMA] is needed."
|
|
log "WARNING" "Review the following definitions for new Cardigann Version: $newschema_indexers"
|
|
else
|
|
rmdir "$NEW_VERS_DIR"
|
|
fi
|
|
fi
|
|
|
|
git rm -r -f -q --ignore-unmatch --cached node_modules
|
|
|
|
if [ "$automation_mode" = true ]; then
|
|
log "INFO" "Automation mode: Proceeding with commit and push automatically"
|
|
else
|
|
log "WARNING" "After review; the script will commit the changes and push as/if specified."
|
|
read -r -p "Press any key to continue or [Ctrl-C] to abort. Waiting for human review..." -n1 -s
|
|
fi
|
|
new_commit_msg="$PROWLARR_COMMIT_TEMPLATE $jackett_recent_commit [$(date -u +'%Y-%m-%dT%H:%M:%SZ')]"
|
|
if [ "$BACKPORT_SKIPPED" = true ]; then
|
|
new_commit_msg+=" $PROWLARR_COMMIT_TEMPLATE_APPEND"
|
|
fi
|
|
# Append to the commit the list of all added, removed, and modified indexers
|
|
if [ -n "$added_indexers" ]; then
|
|
new_commit_msg+=$'\n\n'"Added Indexers: $added_indexers"
|
|
fi
|
|
if [ -n "$removed_indexers" ]; then
|
|
new_commit_msg+=$'\n\n'"Removed Indexers: $removed_indexers"
|
|
fi
|
|
if [ -n "$modified_indexers" ]; then
|
|
new_commit_msg+=$'\n\n'"Modified Indexers: $modified_indexers"
|
|
fi
|
|
if [ -n "$newschema_indexers" ]; then
|
|
new_commit_msg+=$'\n\n'"New Schema Indexers: $newschema_indexers"
|
|
fi
|
|
|
|
# Check for unresolved conflicts before committing
|
|
unresolved_conflicts=$(git status --porcelain | grep "^UU\|^AA\|^DD\|^AU\|^UA\|^DU\|^UD" || true)
|
|
if [ -n "$unresolved_conflicts" ]; then
|
|
log "ERROR" "Cannot commit: Unresolved conflicts detected:"
|
|
echo "$unresolved_conflicts"
|
|
git status
|
|
exit 6
|
|
fi
|
|
|
|
if [ "$pulls_exists" = true ] && [ "$prowlarr_target_branch" != "$PROWLARR_RELEASE_BRANCH" ]; then
|
|
if [ "$existing_message_ln1" = "$prowlarr_jackett_commit_message" ]; then
|
|
if ! git commit --amend -m "$new_commit_msg" -m "$existing_message"; then
|
|
log "ERROR" "Failed to amend commit"
|
|
exit 7
|
|
fi
|
|
log "INFO" "Commit Appended - [$new_commit_msg]"
|
|
push_mode_force=true # Auto-enable force push after amend
|
|
else
|
|
if ! git commit -m "$new_commit_msg"; then
|
|
log "ERROR" "Failed to create new commit"
|
|
exit 7
|
|
fi
|
|
log "INFO" "New Commit made - [$new_commit_msg]"
|
|
fi
|
|
else
|
|
if ! git commit -m "$new_commit_msg"; then
|
|
log "ERROR" "Failed to create new commit"
|
|
exit 7
|
|
fi
|
|
log "INFO" "New Commit made - [$new_commit_msg]"
|
|
fi
|
|
}
|
|
|
|
push_changes() {
|
|
push_branch="$prowlarr_target_branch"
|
|
log "INFO" "Evaluating for Push to Remote"
|
|
log "DEBUG" " Push Modes for Branch: $push_branch"
|
|
log "DEBUG" "Push To Remote: $push_mode with Force Push With Lease: $push_mode_force"
|
|
|
|
# Safety check: NEVER force push to master
|
|
if [ "$push_mode_force" = true ] && [ "$push_branch" = "master" ]; then
|
|
log "ERROR" "Force push to master branch is forbidden for safety"
|
|
push_mode_force=false
|
|
log "WARN" "Disabled force push - will attempt regular push instead"
|
|
fi
|
|
|
|
if [ "$push_mode" = true ] && [ "$push_mode_force" = true ]; then
|
|
if git push "$prowlarr_push_remote" "$push_branch" --force-if-includes --force-with-lease; then
|
|
log "WARN" "[$prowlarr_push_remote $push_branch] Branch Force Pushed"
|
|
else
|
|
log "ERROR" "Failed to force push to [$prowlarr_push_remote $push_branch]"
|
|
exit 8
|
|
fi
|
|
elif [ "$push_mode" = true ]; then
|
|
if git push "$prowlarr_push_remote" "$push_branch" --force-if-includes; then
|
|
log "SUCCESS" "[$prowlarr_push_remote $push_branch] Branch Pushed"
|
|
else
|
|
log "ERROR" "Failed to push to [$prowlarr_push_remote $push_branch]"
|
|
exit 8
|
|
fi
|
|
else
|
|
log "SUCCESS" "Skipping Push to [$prowlarr_push_remote/$push_branch] you should consider pushing manually and/or submitting a pull-request."
|
|
fi
|
|
|
|
# Output pull request URL if push was successful
|
|
if [ "$push_mode" = true ]; then
|
|
fork_url=$(git remote get-url "$prowlarr_push_remote" 2>/dev/null | sed 's/\.git$//' | sed 's/git@github\.com:/https:\/\/github.com\//')
|
|
if [[ "$fork_url" == *"github.com"* ]]; then
|
|
fork_owner=$(echo "$fork_url" | sed 's/.*github\.com[\/:]*//' | cut -d'/' -f1)
|
|
# Only show PR URL if not pushing to Prowlarr repo itself
|
|
if [ "$fork_owner" != "Prowlarr" ]; then
|
|
pr_url="https://github.com/Prowlarr/Indexers/compare/master...$fork_owner:$push_branch"
|
|
log "SUCCESS" "Create pull request: $pr_url"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
initialize_script
|
|
configure_git
|
|
check_branches
|
|
git_branch_reset
|
|
pull_cherry_and_merge
|
|
cleanup_and_commit
|
|
push_changes
|
|
}
|
|
|
|
main "$@"
|