#!/usr/bin/env bash export VSCODE_QUALITY="stable" while getopts ":i" opt; do case "$opt" in i) export VSCODE_QUALITY="insider" ;; *) ;; esac done generate_rejects() { local PATCH_FILE="$1" if ! command -v python3 >/dev/null 2>&1; then echo "python3 not found; cannot create reject files for ${PATCH_FILE}" return 1 fi PATCH_FOR_REJECT="${PATCH_FILE}" python3 <<'PY' import os import pathlib import re import subprocess def chunk_needs_reject(raw_chunk: str) -> bool: """Return True when a patch chunk cannot be applied nor reversed.""" chunk = raw_chunk if raw_chunk.endswith("\n") else raw_chunk + "\n" def _run_git(extra_args): return subprocess.run( ["git", "apply", "--check", "--ignore-whitespace", *extra_args], input=chunk, text=True, capture_output=True, ) forward = _run_git([]) if forward.returncode == 0: return False reverse = _run_git(["--reverse"]) if reverse.returncode == 0: return False return True patch_path = os.environ["PATCH_FOR_REJECT"] with open(patch_path, "r", encoding="utf-8", errors="ignore") as src: content = src.read() chunks = re.split(r'(?m)^diff --git ', content) for chunk in chunks: chunk = chunk.strip() if not chunk: continue chunk = "diff --git " + chunk match = re.search(r'^diff --git a/(.*?) b/(.*?)$', chunk, re.MULTILINE) if not match: continue a_path, b_path = match.groups() candidate = b_path if b_path != "/dev/null" else a_path if candidate in ("/dev/null", ""): continue if candidate.startswith("../") or candidate.startswith("..\\") or candidate.startswith("/"): continue dest = pathlib.Path(candidate + ".rej") if not chunk_needs_reject(chunk): continue dest.parent.mkdir(parents=True, exist_ok=True) with dest.open("w", encoding="utf-8") as fh: fh.write(chunk) if not chunk.endswith("\n"): fh.write("\n") print(f"generated reject: {dest}") PY } check_file() { while [ $# -gt 1 ]; do git apply --reject "${1}" shift done if [[ -f "${1}.bak" ]]; then mv -f $1{.bak,} fi if [[ -f "${1}" ]]; then git apply --reject "../patches/helper/settings.patch" git add . git commit --no-verify -q -m "VSCODIUM HELPER" echo applying patch: "${1}" if ! git apply --ignore-whitespace "${1}"; then echo failed to apply patch "${1}" git apply --reject --verbose "${1}" if [[ -z "$( find . -name '*.rej' -print )" ]]; then echo "no .rej generated by git; creating fallback rejects for ${1}" if ! generate_rejects "${1}"; then echo "failed to generate reject files for ${1}" exit 1 fi if [[ -z "$( find . -name '*.rej' -print )" ]]; then echo "still no .rej after attempting to create them for ${1}" exit 1 fi fi while [[ -n "$( find . -name '*.rej' -print )" ]]; do echo "patch: ${1}" find . -name '*.rej' -print read -rp "Press any key when the conflict have been resolved..." -n1 -s echo done git restore .vscode/settings.json git add . git diff --staged -U1 > "${1}" fi git add . git reset -q --hard HEAD~ fi } cd vscode || { echo "'vscode' dir not found"; exit 1; } git add . git reset -q --hard HEAD while [[ -n "$( git log -1 | grep "VSCODIUM HELPER" )" ]]; do git reset -q --hard HEAD~ done for FILE in ../patches/*.patch; do ADDITIONAL_FILES=() BASENAME=$(basename "${FILE}") DIRNAME=$(dirname "${FILE}") if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then GROUP_ID="${BASH_REMATCH[1]}" INDEX="${BASH_REMATCH[2]}" ENDNAME="${BASH_REMATCH[3]}" for ((I = 0; I < INDEX; I++)); do for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then ADDITIONAL_FILES+=("$CANDIDATE") fi done done fi if [[ ${#ADDITIONAL_FILES[@]} -gt 0 ]]; then check_file ${ADDITIONAL_FILES[@]} "${FILE}" else check_file "${FILE}" fi done if [[ "${VSCODE_QUALITY}" == "insider" ]]; then for FILE in ../patches/insider/*.patch; do check_file "${FILE}" done fi for ARCH in alpine linux osx windows; do for FILE in "../patches/${ARCH}/"*.patch; do ADDITIONAL_FILES=() BASENAME=$(basename "${FILE}") DIRNAME=$(dirname "${FILE}") if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then GROUP_ID="${BASH_REMATCH[1]}" INDEX="${BASH_REMATCH[2]}" ENDNAME="${BASH_REMATCH[3]}" for ((I = 0; I < INDEX; I++)); do NOT_FOUND=1 for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then ADDITIONAL_FILES+=("$CANDIDATE") NOT_FOUND=0 fi done if (( $NOT_FOUND )); then for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then ADDITIONAL_FILES+=("$CANDIDATE") fi done fi done if [[ ${#ADDITIONAL_FILES[@]} -gt 0 ]]; then check_file ${ADDITIONAL_FILES[@]} "${FILE}" else check_file "${FILE}" fi else check_file "${FILE}" fi done for TARGET in client reh; do for FILE in "../patches/${ARCH}/${TARGET}/"*.patch; do check_file "${FILE}" done for FILE in "../patches/${ARCH}/${TARGET}/"*/*.patch; do check_file "${FILE}" done done done