-
Notifications
You must be signed in to change notification settings - Fork 649
fix(libabigail): strip scanner-flagged PR30329 sqlite debuginfo fixtures from upstream tarball #17394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
fix(libabigail): strip scanner-flagged PR30329 sqlite debuginfo fixtures from upstream tarball #17394
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # The upstream libabigail tarball `libabigail-2.9.tar.xz` (Source0) ships an | ||
| # abidiff regression-test fixture set whose two separated-debuginfo files trip | ||
| # anti-malware scanning on the AZL RPM-signing pipeline, which rejects | ||
| # encrypted / unscannable payloads inside SRPMs: | ||
| # | ||
| # - tests/data/test-abidiff-exit/PR30329/{old,new}-image/usr/lib/debug/ | ||
| # usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6.debug | ||
| # Stripped DWARF debuginfo for a pre-built upstream sqlite3 shared | ||
| # library, used (with dwz multifile companions) to exercise abidiff | ||
| # across separated-debuginfo + dwz layouts. The scanner flags both | ||
| # .debug files as "packer_high_entropy:eod". | ||
| # | ||
| # Replace upstream Source0 with a deterministically-repacked tarball produced | ||
| # by base/comps/libabigail/modify_source.sh, which strips the entire | ||
| # PR30329/ fixture directory so nothing in-tree references the missing | ||
| # files. The two corresponding `InOutSpec in_out_specs[]` entries in | ||
| # tests/test-abidiff-exit.cc are dropped by a companion overlay patch | ||
| # (`tests-drop-PR30329-fixture-entries.patch`, applied below), keeping | ||
| # `make check` green. The upstream filename is preserved so | ||
| # `replace-upstream = true` swaps the entry in place in the Fedora `sources` | ||
| # manifest -- no spec edit required. | ||
| [components.libabigail] | ||
|
|
||
| [[components.libabigail.source-files]] | ||
| filename = "libabigail-2.9.tar.xz" | ||
| hash = "efa38b7de791d97910e292dc638537c98d920a68201110727bb5c2d6a6055b6da24beace05db5d540ef4349ce2b4f1592a6aceb4e4249e30a179a037bec2f5d4" | ||
| hash-type = "SHA512" | ||
| origin = { type = "download", uri = "https://azltempstaginglookaside.blob.core.windows.net/repo/pkgs_modified/libabigail/libabigail-2.9.tar.xz/sha512/efa38b7de791d97910e292dc638537c98d920a68201110727bb5c2d6a6055b6da24beace05db5d540ef4349ce2b4f1592a6aceb4e4249e30a179a037bec2f5d4/libabigail-2.9.tar.xz" } | ||
| replace-upstream = true | ||
| replace-reason = "Repacked source tarball with tests/data/test-abidiff-exit/PR30329/ removed (two libsqlite3.so.0.8.6.debug fixtures inside it were flagged as packer_high_entropy:eod by the AZL signing-pipeline AV scanner). The matching InOutSpec entries in tests/test-abidiff-exit.cc are dropped by the companion overlay patch tests-drop-PR30329-fixture-entries.patch. See modify_source.sh." | ||
|
|
||
| [[components.libabigail.overlays]] | ||
| description = "Drop the two tests/test-abidiff-exit.cc InOutSpec entries that exercise the PR30329 fixture set (removed from the AZL-repacked Source0 because its two libsqlite3.so.0.8.6.debug files are flagged packer_high_entropy:eod by the AZL signing-pipeline AV scanner). Without this patch `make check` fails trying to open the missing fixtures." | ||
| type = "patch-add" | ||
| file = "tests-drop-PR30329-fixture-entries.patch" | ||
| source = "tests-drop-PR30329-fixture-entries.patch" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # libabigail: deterministic strip-and-repack of upstream `libabigail-2.9.tar.xz` | ||
| # with the PR30329 testsuite fixture set (which trips anti-malware scanning on | ||
| # the AZL RPM-signing pipeline) removed. The corresponding two entries in | ||
| # `tests/test-abidiff-exit.cc` that exercise the removed fixture are dropped | ||
| # by a companion overlay patch (see `libabigail.comp.toml`); this script does | ||
| # file removal only, no in-tarball source patching. | ||
| # Rationale lives in the comp.toml `replace-reason` field. | ||
| # | ||
| # Usage: bash base/comps/libabigail/modify_source.sh | ||
| # Output: base/build/work/scratch/libabigail/libabigail-2.9.tar.xz (+ .sha512) | ||
| # The upstream tarball is cached under a `.upstream` suffix; re-runs reuse it. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| # Pin umask so the extraction step below produces the same mode bits | ||
| # regardless of the caller's umask. With `--no-same-permissions`, tar ANDs | ||
| # each entry's mode against `~umask`, so e.g. umask 077 would silently strip | ||
| # group/other read bits and change the bytes of the repacked tarball. The | ||
| # repack step does not re-assert per-file modes (only owner/group/mtime), so | ||
| # this pin is what guarantees a byte-identical output across machines. | ||
| umask 022 | ||
|
|
||
| # --- Constants -------------------------------------------------------------- | ||
|
|
||
| readonly COMPONENT="libabigail" | ||
| readonly UPSTREAM_VERSION="2.9" | ||
| readonly UPSTREAM_FILENAME="${COMPONENT}-${UPSTREAM_VERSION}.tar.xz" | ||
| readonly UPSTREAM_TOPDIR="${COMPONENT}-${UPSTREAM_VERSION}" | ||
| readonly UPSTREAM_URL="https://mirrors.kernel.org/sourceware/libabigail/${UPSTREAM_FILENAME}" | ||
|
|
||
| readonly UPSTREAM_SHA512="5bdf5ec49a5931a61bf28317b41eee583d6277d00ac621b2d2a97bbc0d816c3662bcfe13a5ac7aeee11c947afb69a5a0a9a8015fcebad09965b45af9b1e23606" | ||
|
|
||
| # Directory (relative to ${UPSTREAM_TOPDIR}) to strip in its entirety. The | ||
| # PR30329 fixture set is a libabigail abidiff regression test built around a | ||
| # pair of stripped sqlite3 shared libraries + their separated debuginfo + | ||
| # dwz-multifile components. The two `libsqlite3.so.0.8.6.debug` separated- | ||
| # debuginfo files inside it are flagged as encrypted/unscannable payloads by | ||
| # the AV scanner ("packer_high_entropy:eod") in the AZL RPM-signing pipeline. | ||
| # We strip the whole PR30329/ directory (not just the two .debug files) so | ||
| # nothing in the tarball still references the missing pieces; the two | ||
| # corresponding `InOutSpec` entries in tests/test-abidiff-exit.cc are dropped | ||
| # by the companion overlay patch | ||
| # `tests-drop-PR30329-fixture-entries.patch` (see libabigail.comp.toml) so | ||
| # `make check` still passes. | ||
| readonly REMOVE_DIRS=( | ||
| "tests/data/test-abidiff-exit/PR30329" | ||
| ) | ||
|
|
||
| # Deterministic-repack mtime: 2020-01-01T00:00:00Z (1577836800). | ||
| # Any fixed epoch works; do not change without also bumping the | ||
| # `hash` in libabigail.comp.toml. | ||
| readonly DETERMINISTIC_MTIME="@1577836800" | ||
|
|
||
| # --- Work directory --------------------------------------------------------- | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" | ||
| WORKDIR="${REPO_ROOT}/base/build/work/scratch/${COMPONENT}" | ||
|
|
||
| mkdir -p "${WORKDIR}" | ||
| cd "${WORKDIR}" | ||
|
|
||
| echo "[1/5] Working in ${WORKDIR}" | ||
|
|
||
| # --- Download upstream ------------------------------------------------------ | ||
| # | ||
| # The upstream tarball is cached under a `.upstream` suffix so that | ||
| # the repacked output written at the canonical `${UPSTREAM_FILENAME}` | ||
| # path below cannot clobber the cache on re-runs. Treat the cache | ||
| # as authoritative only after SHA-512 verification. | ||
|
|
||
| UPSTREAM_CACHE="${WORKDIR}/${UPSTREAM_FILENAME}.upstream" | ||
|
|
||
| if [[ ! -f "${UPSTREAM_CACHE}" ]]; then | ||
| echo "[2/5] Downloading ${UPSTREAM_FILENAME} from ${UPSTREAM_URL}" | ||
| # `--proto` / `--proto-redir` restrict the initial request *and* any | ||
| # redirect target to HTTPS, so a downgrade to plain HTTP is refused. | ||
| curl -fsSL --retry 3 \ | ||
| --proto '=https' --proto-redir '=https' \ | ||
| -o "${UPSTREAM_CACHE}.part" "${UPSTREAM_URL}" | ||
| mv "${UPSTREAM_CACHE}.part" "${UPSTREAM_CACHE}" | ||
| else | ||
| echo "[2/5] Using cached upstream tarball ${UPSTREAM_CACHE}" | ||
| fi | ||
|
|
||
| # --- Verify upstream SHA-512 ------------------------------------------------ | ||
|
|
||
| echo "[3/5] Verifying upstream SHA-512" | ||
| COMPUTED_UPSTREAM_SHA512="$(sha512sum "${UPSTREAM_CACHE}" | awk '{print $1}')" | ||
| if [[ "${COMPUTED_UPSTREAM_SHA512}" != "${UPSTREAM_SHA512}" ]]; then | ||
| echo "ERROR: upstream SHA-512 mismatch (cache may be corrupt; delete ${UPSTREAM_CACHE} and re-run)" >&2 | ||
| echo " expected: ${UPSTREAM_SHA512}" >&2 | ||
| echo " computed: ${COMPUTED_UPSTREAM_SHA512}" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # --- Extract + strip -------------------------------------------------------- | ||
|
|
||
| echo "[4/5] Extracting and stripping ${#REMOVE_DIRS[@]} fixture dir(s) from ${UPSTREAM_TOPDIR}" | ||
| rm -rf "${WORKDIR}/${UPSTREAM_TOPDIR}" | ||
| # `--no-same-owner` / `--no-same-permissions` prevent tar from applying the | ||
| # archive's uid/gid/mode bits to the extracted tree. They are already the | ||
| # default for non-root users, but explicit hardening makes the script safe | ||
| # to run under sudo (where the defaults flip) and defends against any | ||
| # setuid/setgid bits or unexpected ownership in the upstream tarball. | ||
| # Deterministic owner/group is re-asserted in the repack step below. | ||
| tar -C "${WORKDIR}" --no-same-owner --no-same-permissions -xf "${UPSTREAM_CACHE}" | ||
| for REMOVE_DIR in "${REMOVE_DIRS[@]}"; do | ||
| if [[ ! -d "${WORKDIR}/${UPSTREAM_TOPDIR}/${REMOVE_DIR}" ]]; then | ||
| echo "ERROR: expected '${UPSTREAM_TOPDIR}/${REMOVE_DIR}' not present in upstream tarball" >&2 | ||
| exit 1 | ||
| fi | ||
| echo " stripping ${UPSTREAM_TOPDIR}/${REMOVE_DIR}" | ||
| rm -rf "${WORKDIR}/${UPSTREAM_TOPDIR}/${REMOVE_DIR}" | ||
| done | ||
|
|
||
| # --- Repack deterministically ----------------------------------------------- | ||
|
|
||
| echo "[5/5] Repacking deterministically as ${UPSTREAM_FILENAME}" | ||
| # Deterministic flags: | ||
| # --sort=name stable entry order | ||
| # --owner=0 --group=0 no host uid/gid leakage | ||
| # --numeric-owner force numeric uid/gid | ||
| # --mtime=@<epoch> fixed mtime | ||
| # --format=gnu handles long paths deterministically | ||
| # LC_ALL=C pins sort collation so --sort=name is locale-independent. | ||
| # xz -9e -T1 picks max compression with single-threaded output (multi-threaded | ||
| # xz produces non-deterministic byte streams). The upstream tarball is .xz so | ||
| # we re-emit .xz to keep the filename and Source0 unchanged. | ||
| # | ||
| # Heads-up: this step is slow. libabigail-2.9 unpacks to ~990 MiB (the source | ||
| # tree is dominated by abidiff regression-test fixtures), so the single- | ||
| # threaded `xz -9e` pass below is on the order of minutes, not seconds. | ||
| # Reference timing on a 12th-gen Intel desktop (i9-12900K, 12 vCPUs): ~6-7 | ||
| # minutes wall time for the full tar+xz pipeline (xz dominates; tar itself | ||
| # is a few seconds). The download (~500 MiB) and extract/strip steps before | ||
| # this finish in well under a minute on the same hardware. Slower CPUs can | ||
| # easily push this past 10 minutes -- so if it looks hung, give it time. | ||
| MODIFIED_TARBALL="${WORKDIR}/${UPSTREAM_FILENAME}" | ||
| rm -f "${MODIFIED_TARBALL}" | ||
| LC_ALL=C tar \ | ||
| -C "${WORKDIR}" \ | ||
| --sort=name \ | ||
| --owner=0 --group=0 --numeric-owner \ | ||
| --mtime="${DETERMINISTIC_MTIME}" \ | ||
| --format=gnu \ | ||
| -cf - "${UPSTREAM_TOPDIR}" \ | ||
| | xz -9e -T1 -c > "${MODIFIED_TARBALL}" | ||
|
|
||
| MODIFIED_SHA512="$(sha512sum "${MODIFIED_TARBALL}" | awk '{print $1}')" | ||
| echo "${MODIFIED_SHA512} ${UPSTREAM_FILENAME}" > "${MODIFIED_TARBALL}.sha512" | ||
|
|
||
| echo | ||
| echo "================================================================" | ||
| echo "DONE" | ||
| echo " modified tarball: ${WORKDIR}/${UPSTREAM_FILENAME}" | ||
| echo " SHA512: ${MODIFIED_SHA512}" | ||
| echo "================================================================" | ||
| echo | ||
| echo " To upload the modified tarball to the lookaside:" | ||
| echo " az storage blob upload \\" | ||
| echo " --auth-mode login \\" | ||
| echo " --account-name azltempstaginglookaside \\" | ||
| echo " --container-name repo \\" | ||
| echo " --name \"pkgs_modified/${COMPONENT}/${UPSTREAM_FILENAME}/sha512/${MODIFIED_SHA512}/${UPSTREAM_FILENAME}\" \\" | ||
| echo " --file \"${WORKDIR}/${UPSTREAM_FILENAME}\"" | ||
50 changes: 50 additions & 0 deletions
50
base/comps/libabigail/tests-drop-PR30329-fixture-entries.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| Drop test-abidiff-exit entries that reference the PR30329 fixture set | ||
|
|
||
| The two `InOutSpec in_out_specs[]` entries referencing | ||
| `tests/data/test-abidiff-exit/PR30329/` are dropped here because the | ||
| AZL-repacked source tarball strips the PR30329 fixture directory | ||
| (`base/comps/libabigail/modify_source.sh`). The PR30329 fixture's two | ||
| separated-debuginfo files (`libsqlite3.so.0.8.6.debug`) trip the | ||
| anti-malware scanner on the AZL RPM-signing pipeline as | ||
| `packer_high_entropy:eod`. Without this patch `make check` would fail | ||
| trying to open the missing fixture files. | ||
|
|
||
| --- a/tests/test-abidiff-exit.cc | ||
| +++ b/tests/test-abidiff-exit.cc | ||
| @@ -983,36 +983,6 @@ | ||
| "output/test-abidiff-exit/ada-subrange/test2-ada-subrange-redundant/test2-ada-subrange-redundant-report-2.txt" | ||
| }, | ||
| { | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "", | ||
| - "", | ||
| - "", | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/debug", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/debug", | ||
| - "", | ||
| - "", | ||
| - "--no-default-suppression", | ||
| - abigail::tools_utils::ABIDIFF_ABI_CHANGE, | ||
| - "data/test-abidiff-exit/PR30329/PR30329-report-1.txt", | ||
| - "output/test-abidiff-exit/PR30329/PR30329-report-1.txt" | ||
| - }, | ||
| - { | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "", | ||
| - "", | ||
| - "", | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/debug,data/test-abidiff-exit/PR30329/old-image/usr/lib/debug/dwz/components/sqlite.bst", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/debug,data/test-abidiff-exit/PR30329/old-image/usr/lib/debug/dwz/components/sqlite.bst", | ||
| - "", | ||
| - "", | ||
| - "--no-default-suppression", | ||
| - abigail::tools_utils::ABIDIFF_ABI_CHANGE, | ||
| - "data/test-abidiff-exit/PR30329/PR30329-report-1.txt", | ||
| - "output/test-abidiff-exit/PR30329/PR30329-report-1.txt" | ||
| - }, | ||
| - { | ||
| "data/test-abidiff-exit/PR30503/libsdl/1.2.60/lib64/libSDL-1.2.so.1.2.60", | ||
| "data/test-abidiff-exit/PR30503/libsdl/1.2.64/lib64/libSDL-1.2.so.1.2.64", | ||
| "", |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| SHA512 (libabigail-2.9.tar.xz) = 5bdf5ec49a5931a61bf28317b41eee583d6277d00ac621b2d2a97bbc0d816c3662bcfe13a5ac7aeee11c947afb69a5a0a9a8015fcebad09965b45af9b1e23606 | ||
| SHA512 (libabigail-2.9.tar.xz) = efa38b7de791d97910e292dc638537c98d920a68201110727bb5c2d6a6055b6da24beace05db5d540ef4349ce2b4f1592a6aceb4e4249e30a179a037bec2f5d4 |
50 changes: 50 additions & 0 deletions
50
specs/l/libabigail/tests-drop-PR30329-fixture-entries.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| Drop test-abidiff-exit entries that reference the PR30329 fixture set | ||
|
|
||
| The two `InOutSpec in_out_specs[]` entries referencing | ||
| `tests/data/test-abidiff-exit/PR30329/` are dropped here because the | ||
| AZL-repacked source tarball strips the PR30329 fixture directory | ||
| (`base/comps/libabigail/modify_source.sh`). The PR30329 fixture's two | ||
| separated-debuginfo files (`libsqlite3.so.0.8.6.debug`) trip the | ||
| anti-malware scanner on the AZL RPM-signing pipeline as | ||
| `packer_high_entropy:eod`. Without this patch `make check` would fail | ||
| trying to open the missing fixture files. | ||
|
|
||
| --- a/tests/test-abidiff-exit.cc | ||
| +++ b/tests/test-abidiff-exit.cc | ||
| @@ -983,36 +983,6 @@ | ||
| "output/test-abidiff-exit/ada-subrange/test2-ada-subrange-redundant/test2-ada-subrange-redundant-report-2.txt" | ||
| }, | ||
| { | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "", | ||
| - "", | ||
| - "", | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/debug", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/debug", | ||
| - "", | ||
| - "", | ||
| - "--no-default-suppression", | ||
| - abigail::tools_utils::ABIDIFF_ABI_CHANGE, | ||
| - "data/test-abidiff-exit/PR30329/PR30329-report-1.txt", | ||
| - "output/test-abidiff-exit/PR30329/PR30329-report-1.txt" | ||
| - }, | ||
| - { | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6", | ||
| - "", | ||
| - "", | ||
| - "", | ||
| - "data/test-abidiff-exit/PR30329/old-image/usr/lib/debug,data/test-abidiff-exit/PR30329/old-image/usr/lib/debug/dwz/components/sqlite.bst", | ||
| - "data/test-abidiff-exit/PR30329/new-image/usr/lib/debug,data/test-abidiff-exit/PR30329/old-image/usr/lib/debug/dwz/components/sqlite.bst", | ||
| - "", | ||
| - "", | ||
| - "--no-default-suppression", | ||
| - abigail::tools_utils::ABIDIFF_ABI_CHANGE, | ||
| - "data/test-abidiff-exit/PR30329/PR30329-report-1.txt", | ||
| - "output/test-abidiff-exit/PR30329/PR30329-report-1.txt" | ||
| - }, | ||
| - { | ||
| "data/test-abidiff-exit/PR30503/libsdl/1.2.60/lib64/libSDL-1.2.so.1.2.60", | ||
| "data/test-abidiff-exit/PR30503/libsdl/1.2.64/lib64/libSDL-1.2.so.1.2.64", | ||
| "", |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.