mirror of
https://github.com/angr/angr
synced 2026-08-17 12:23:11 -04:00
* create snapshot files at a different base dir. * Fix branch name in "Push snapshots to snapshot repo". * sanitize snapshot index file path * snapshot_diff.sh expects GITHUB_TOKEN instead of GH_TOKEN. * snapsnot_diff.sh needs repo specified. * Fix PR number in snapshot_diff summary. * Use ANGR_PR_PAT instead when updating angr PRs.
259 lines
6.8 KiB
Bash
Executable file
259 lines
6.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -o pipefail
|
|
|
|
help() {
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
cat <<ANGR
|
|
Usage: $SCRIPT_NAME (<remote_path>) | (<local_snapshot1> <local_snapshot2>) [OPTIONS]
|
|
|
|
Compares 1-to-1 decompilation snapshots for a particular file.
|
|
|
|
Examples:
|
|
$SCRIPT_NAME -H pr-404 -t "\$(gh auth token)"
|
|
|
|
$SCRIPT_NAME -H pr-404 -p stable/cgc-challenges/ -t "\$(gh auth token)"
|
|
|
|
Options:
|
|
-d, --snapshot-directory
|
|
|
|
Specify the directory to cache the decompilation snapshots.
|
|
|
|
-h, --help
|
|
|
|
Show this help message and exit
|
|
|
|
-p <path>, --path <path>
|
|
|
|
The file path of the snapshot to compare.
|
|
|
|
-v, --verbose
|
|
|
|
Enable more verbose output.
|
|
|
|
GitHub Options:
|
|
|
|
-b <ref>, --base-ref <ref>
|
|
|
|
The name of the base repository ref to compare snapshots from. This
|
|
defaults to HEAD.
|
|
|
|
-H <ref>, --head-ref <ref>
|
|
|
|
The name of the head repository ref to compare snapshots to.
|
|
|
|
-R <repo>, --repo <repo>
|
|
|
|
The GitHub repository to enumerate using the OWNER/REPO format.
|
|
This defaults to project-purcellville/snapshots-0000.
|
|
|
|
-t <token>, --token <token>
|
|
|
|
A GitHub token with access permissions. This can also be specified via the
|
|
GITHUB_TOKEN environment variable.
|
|
ANGR
|
|
exit 1
|
|
}
|
|
|
|
declare -a FILEPATH
|
|
REF_BASE="HEAD"
|
|
REPO="project-purcellville/snapshots-0000"
|
|
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
|
SNAPSHOT_DIR=""
|
|
VERBOSE=""
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-b|--base-ref)
|
|
REF_BASE="${2}"
|
|
shift 2
|
|
;;
|
|
-d|--snapshot-dir)
|
|
SNAPSHOT_DIR="${2}"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
help
|
|
;;
|
|
-H|--head-ref)
|
|
REF_HEAD="$2"
|
|
shift 2
|
|
;;
|
|
-p|--path)
|
|
FILEPATH+=("$2")
|
|
shift 2
|
|
;;
|
|
-R|--repo)
|
|
REPO="$2"
|
|
shift 2
|
|
;;
|
|
-t|--token)
|
|
GITHUB_TOKEN="$2"
|
|
shift 2
|
|
;;
|
|
-v|--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
help
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
parse_args "$@"
|
|
|
|
if [[ -z "${REPO}" ]]; then
|
|
echo "ERROR: <repo> not provided"
|
|
echo
|
|
help
|
|
fi
|
|
|
|
if [[ -z "${REF_HEAD}" ]]; then
|
|
echo "ERROR: head <ref> not provided"
|
|
echo
|
|
help
|
|
fi
|
|
|
|
if [[ -z "${SNAPSHOT_DIR}" ]]; then
|
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
|
SNAPSHOT_DIR=$(mktemp -d)
|
|
else
|
|
mkdir -p "${SNAPSHOT_DIR}"
|
|
fi
|
|
|
|
# create the file path for the index file; sanitize REF_HEAD to replace "/" with "_"
|
|
SANITIZED_REF_HEAD=$(echo -n "${REF_HEAD}" | tr '/' '_')
|
|
SNAPSHOT_INDEX_FILE="${SNAPSHOT_DIR}/index_${SANITIZED_REF_HEAD}.txt"
|
|
|
|
# Retrieve a list of all of the available snapshot file paths.
|
|
if ! [[ -f "${SNAPSHOT_INDEX_FILE}" ]]; then
|
|
printf "Indexing remote: %s\n" "${REF_HEAD}" >&2
|
|
"${SCRIPT_DIR}/gh_ls.sh" \
|
|
-R "${REPO}" \
|
|
--branch "${REF_HEAD}" \
|
|
--path snapshots/ \
|
|
--token "${GITHUB_TOKEN}" \
|
|
>> "${SNAPSHOT_INDEX_FILE}"
|
|
fi
|
|
|
|
# If FILEPATHs were provided, call `gh_ls.sh` and retrieve all available
|
|
# valid file paths in case they were file patterns.
|
|
if ! [[ "${#FILEPATH[@]}" -eq 0 ]]; then
|
|
declare -a FILEPATH_NEW
|
|
for index in "${!FILEPATH[@]}"; do
|
|
printf "\rHydrating remote path: %s" "${FILEPATH[$index]}" >&2
|
|
if [[ "${FILEPATH[$index]}" != snapshots/* ]]; then
|
|
FILEPATH[index]="snapshots/${FILEPATH[$index]}"
|
|
fi
|
|
declare -a FILEPATH_TMP
|
|
mapfile -t FILEPATH_TMP < <(
|
|
"${SCRIPT_DIR}/gh_ls.sh" \
|
|
-R "${REPO}" \
|
|
--branch "${REF_HEAD}" \
|
|
--path "${FILEPATH[$index]}" \
|
|
--token "${GITHUB_TOKEN}"
|
|
)
|
|
FILEPATH_NEW=( "${FILEPATH_NEW[@]}" "${FILEPATH_TMP[@]}" )
|
|
done
|
|
FILEPATH=("${FILEPATH_NEW[@]}")
|
|
printf "\33[2K\rHydrated %s snapshot paths, done.\n" "${#FILEPATH_NEW[@]}" >&2
|
|
fi
|
|
|
|
# Try and find a PR merging from the `REF_HEAD` to the `REF_BASE` and get a list
|
|
# of only the files with changes.
|
|
if [[ "${#FILEPATH[@]}" -eq 0 ]]; then
|
|
mapfile -t FILEPATH < <(
|
|
gh pr list \
|
|
-R "${REPO}" \
|
|
--json headRefName \
|
|
--json files \
|
|
--jq ".[] | select(.headRefName == \"$REF_HEAD\")
|
|
| .files[].path"
|
|
)
|
|
printf "Selected changed files from PR: %s\n" "${#FILEPATH[@]}" >&2
|
|
fi
|
|
|
|
# If there are still no file paths, get the list of paths from the head ref.
|
|
if [[ "${#FILEPATH[@]}" -eq 0 ]]; then
|
|
mapfile -t FILEPATH < <(sort "${SNAPSHOT_INDEX_FILE}")
|
|
fi
|
|
|
|
# Download each snapshot.
|
|
# If the FILEPATH is missing a leading `snapshots/`, add it.
|
|
# If it is missing a trailing `.json.txt`, add it.
|
|
N="${N:-$(($(getconf _NPROCESSORS_ONLN) * 2))}"
|
|
|
|
for index in "${!FILEPATH[@]}"; do
|
|
printf "\33[2K\rDownloading snapshots (%s/%s)" "$((index + 1))" "${#FILEPATH[@]}" >&2
|
|
|
|
if [[ "${FILEPATH[$index]}" != snapshots/* ]]; then
|
|
FILEPATH[index]="snapshots/${FILEPATH[$index]}"
|
|
fi
|
|
if [[ "${FILEPATH[$index]}" != *.json.txt ]]; then
|
|
FILEPATH[index]="${FILEPATH[$index]}.json.txt"
|
|
fi
|
|
|
|
# Concurrency control. Let up to ${N} jobs run in the background.
|
|
mapfile -t pids < <(jobs -pr)
|
|
[[ ${#pids[@]} -ge ${N} ]] && wait -n
|
|
unset pids
|
|
|
|
DIRPATH="${SNAPSHOT_DIR}/${REF_BASE}/$(dirname "${FILEPATH[$index]}")"
|
|
FILENAME="$(basename "${FILEPATH[$index]}")"
|
|
mkdir -p "${DIRPATH}"
|
|
SNAPSHOT_BASE_URL="https://raw.githubusercontent.com/${REPO}/${REF_BASE}/${FILEPATH[$index]}"
|
|
if ! [[ -f "${DIRPATH}/${FILENAME}" ]]; then
|
|
curl \
|
|
--location \
|
|
--header "Authorization: token $GITHUB_TOKEN" \
|
|
--output "${DIRPATH}/${FILENAME}" \
|
|
--silent \
|
|
"${SNAPSHOT_BASE_URL}" &
|
|
fi
|
|
|
|
DIRPATH="${SNAPSHOT_DIR}/${REF_HEAD}/$(dirname "${FILEPATH[$index]}")"
|
|
FILENAME="$(basename "${FILEPATH[$index]}")"
|
|
mkdir -p "${DIRPATH}"
|
|
SNAPSHOT_URL="https://raw.githubusercontent.com/${REPO}/${REF_HEAD}/${FILEPATH[$index]}"
|
|
if ! [[ -f "${DIRPATH}/${FILENAME}" ]]; then
|
|
curl \
|
|
--location \
|
|
--header "Authorization: token $GITHUB_TOKEN" \
|
|
--output "${DIRPATH}/${FILENAME}" \
|
|
--silent \
|
|
"${SNAPSHOT_URL}" &
|
|
fi
|
|
done
|
|
|
|
wait
|
|
echo ", done." >&2
|
|
|
|
# XXX: This was indulgent. Revisit performance here.
|
|
{
|
|
for index in "${!FILEPATH[@]}"; do
|
|
SNAPSHOT1="${SNAPSHOT_DIR}/${REF_BASE}/${FILEPATH[$index]}"
|
|
SNAPSHOT2="${SNAPSHOT_DIR}/${REF_HEAD}/${FILEPATH[$index]}"
|
|
|
|
if [[ -n "${VERBOSE}" ]]; then
|
|
"${SCRIPT_DIR}/classify_diff.sh" -v "${SNAPSHOT1}" "${SNAPSHOT2}"
|
|
else
|
|
"${SCRIPT_DIR}/classify_diff.sh" "${SNAPSHOT1}" "${SNAPSHOT2}"
|
|
fi
|
|
done
|
|
} | \
|
|
jq 'select(.identical == false)' | \
|
|
jq -s 'reduce(.[].changes |
|
|
to_entries[] |
|
|
select(.value != 0)) as {"key": $key, "value": $value}
|
|
({}; .["changes"][$key]["hunks"] += $value |
|
|
.["changes"][$key]["files"] += 1)' | \
|
|
jq -r '.changes |
|
|
to_entries |
|
|
["-----------,-----,-----"] +
|
|
map("\(.key | gsub("_"; " ") | ascii_upcase),\(.value.hunks),\(.value.files)") |
|
|
.[]' | \
|
|
column -t -N "Change Type,Hunks,Files" -o " | " -s ","
|