Skip to content
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

autoPatchelfHook: do not patch statically linked files #47222

Merged
merged 4 commits into from Sep 25, 2018

Conversation

Mic92
Copy link
Member

@Mic92 Mic92 commented Sep 23, 2018

Motivation for this change
Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Fits CONTRIBUTING.md.

@GrahamcOfBorg
Copy link

Success on x86_64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/lmiihhfxffmbmrfjz5p3yl3k3ps7njpf-auto-patchelf-hook.drv
these paths will be fetched (0.27 MiB download, 5.26 MiB unpacked):
  /nix/store/hnkii4sq1g48k5wq5d479wddw8pj844j-file-5.34
copying path '/nix/store/hnkii4sq1g48k5wq5d479wddw8pj844j-file-5.34' from 'https://cache.nixos.org'...
building '/nix/store/lmiihhfxffmbmrfjz5p3yl3k3ps7njpf-auto-patchelf-hook.drv'...
/nix/store/06kjp7k4dcl03rs5rimzlq3y9vapjwg9-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on x86_64-darwin (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/77qz9i4a14b1cps07qd97pn8ym41b4gq-auto-patchelf-hook.drv
these paths will be fetched (0.27 MiB download, 5.24 MiB unpacked):
  /nix/store/vrmrbs2bdll8b9qf6zfg7lnm2ppwbny2-file-5.34
copying path '/nix/store/vrmrbs2bdll8b9qf6zfg7lnm2ppwbny2-file-5.34' from 'https://cache.nixos.org'...
building '/nix/store/77qz9i4a14b1cps07qd97pn8ym41b4gq-auto-patchelf-hook.drv'...
/nix/store/rj1vbnv4g2gr12ji26ckn9pmrbvcj00s-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on aarch64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

  /nix/store/j6migyd1qv56z6mspvbskwjx96yaqfa6-coreutils-8.29
  /nix/store/nghz9s7syj5hjflq9fk0r6480sh18h04-findutils-4.6.0
  /nix/store/vhyv19xv3lzs9sdd8ss81fx05bq96zv0-stdenv-linux
copying path '/nix/store/bkzcra2m4fn9y0jjhlzb2s66b97rjf32-file-5.34' from 'https://cache.nixos.org'...
copying path '/nix/store/j6migyd1qv56z6mspvbskwjx96yaqfa6-coreutils-8.29' from 'https://cache.nixos.org'...
copying path '/nix/store/102z71i6ps917xm39il291dc2dqbj52g-diffutils-3.6' from 'https://cache.nixos.org'...
copying path '/nix/store/nghz9s7syj5hjflq9fk0r6480sh18h04-findutils-4.6.0' from 'https://cache.nixos.org'...
copying path '/nix/store/vhyv19xv3lzs9sdd8ss81fx05bq96zv0-stdenv-linux' from 'https://cache.nixos.org'...
building '/nix/store/sd8kxd0ndkl7cls9cydvgpdgnzvm9b89-auto-patchelf-hook.drv'...
/nix/store/g8gx227yax0zvz3qb79mqvqfqyf9b6lz-auto-patchelf-hook

@Ericson2314
Copy link
Member

What does "statically linked" mean exactly? I'm surprised ofborg says this isn't a mass rebuild.

@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

This is not a mass-rebuild because we only use autoPatchelfHook for programs that we do not compiled from source. Many of them also have an unfree license. Statically linked means it does not use a runtime linker (e.x.: ld-linux-x86-64.so.2). patchelf will fail on those files and it also does not make any sense to set the RPATH on statically linked files because there are not libraries loaded.

Copy link
Member

@aszlig aszlig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move the detection to findElfs instead. In addition I think using file for detecting ELFs is not a very good idea in the first place and we should change the detection method in findElfs altogether, because some magic files tend to have quite expensive search operations.

@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

What else should be used instead of file?

@Ericson2314
Copy link
Member

@Mic92 there is a n isELF in stdenv that does the the magic byte lookup manually in bash.

@aszlig
Copy link
Member

aszlig commented Sep 23, 2018

@Ericson2314: Hm, looking at that one, it looks a bit too fuzzy to me, for example it will return success if you have a text file like this:

ELF file headers
----------------

...

So IMHO something like printf '\x7fELF' | cmp -s -n 4 - "$1" is a better test (although it needs to fork off to an external command).

@aszlig
Copy link
Member

aszlig commented Sep 23, 2018

@Ericson2314: Replacing [[ "$magic" =~ ELF ]] by [ "$magic" = "$(echo -ne "\x7fELF")" ] should work better however and also doesn't need to fork.

@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

@aszlig proposed the following patch on irc:

https://gist.githubusercontent.com/aszlig/c3883b5f193ae238d5ab3cfc227a66fb/raw/051de4484ac7b3f979320cfa64142b56bb60af7f/auto-patchelf.patch

I will combine this with the stdenv's native isElf patch later.

@Ericson2314
Copy link
Member

I don't disagree with improving isELF. Care should be taken with isExecutable though to not break Darwin (or Windows cross-compilation! :D).

@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

patchelf does not work on windows/macOS so isExecutable only needs to work on elf-based operating systems.

@GrahamcOfBorg
Copy link

Success on x86_64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/hi8nybl5zvn69f4qgfjxrlay311ff4ry-auto-patchelf-hook.drv
building '/nix/store/hi8nybl5zvn69f4qgfjxrlay311ff4ry-auto-patchelf-hook.drv'...
/nix/store/rmkcnyzfcgmf040h935w192cncvs222q-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on x86_64-darwin (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/pwhg2qfpbjbcpg9a22sx71nr9n9471fw-auto-patchelf-hook.drv
building '/nix/store/pwhg2qfpbjbcpg9a22sx71nr9n9471fw-auto-patchelf-hook.drv'...
/nix/store/ff35adp20iamng21q5phb9imwdv5slda-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on aarch64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/avy6qi8cvwkkg71xb904dhciplahdn6h-auto-patchelf-hook.drv
building '/nix/store/avy6qi8cvwkkg71xb904dhciplahdn6h-auto-patchelf-hook.drv'...
/nix/store/jpygswdai77kcr9d6pxg6lwjf0i25dn4-auto-patchelf-hook

findElfs "$prefix" | while read -r elffile; do
autoPatchelfFile "$elffile"
done
while IFS= read -r -d $'\0' file; do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the IFS=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page says it is required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're right, otherwise it's trimmed by IFS.

autoPatchelfFile "$elffile"
done
while IFS= read -r -d $'\0' file; do
if isELF "$file"; then
Copy link
Member

@aszlig aszlig Sep 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe even:

isELF "$file" || continue
readelf -l "$file" | grep -q '^ *INTERP\>' || continue
autoPatchelfFile "$file"

IMHO makes it less "pyramidic".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

micro optimization...

Also speed up quite significantly due less forking.
@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

regarding isELF, I opened: #47249

@GrahamcOfBorg
Copy link

Success on x86_64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/1yn3fr48rhssv1jxb5lq77hlwscls91q-auto-patchelf-hook.drv
these paths will be fetched (0.46 MiB download, 1.99 MiB unpacked):
  /nix/store/kw3mm7s20416m26xp7i60qp0v9rwpdxj-stdenv-linux
  /nix/store/zdadpaj5z8k9ifkgsc16yvfjn4wdv0r2-gawk-4.2.1
copying path '/nix/store/zdadpaj5z8k9ifkgsc16yvfjn4wdv0r2-gawk-4.2.1' from 'https://cache.nixos.org'...
copying path '/nix/store/kw3mm7s20416m26xp7i60qp0v9rwpdxj-stdenv-linux' from 'https://cache.nixos.org'...
building '/nix/store/1yn3fr48rhssv1jxb5lq77hlwscls91q-auto-patchelf-hook.drv'...
/nix/store/5ixz5zy2csy1xfydpd62w5fn27sd4ip2-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on x86_64-darwin (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

these derivations will be built:
  /nix/store/sfd27pprlcqhqldavn97mli6bhrqnhvy-auto-patchelf-hook.drv
building '/nix/store/sfd27pprlcqhqldavn97mli6bhrqnhvy-auto-patchelf-hook.drv'...
/nix/store/ccpqxdwcf0xq1l8wv7r4sg7vlmya1pdd-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on aarch64-linux (full log)

Attempted: autoPatchelfHook

Partial log (click to expand)

copying path '/nix/store/7rwg9nnr957pr9p715yfan908m4f8zbh-xz-5.2.4-bin' from 'https://cache.nixos.org'...
copying path '/nix/store/5mc7czqfrp32mb35rjmij6545vanx435-acl-2.2.52' from 'https://cache.nixos.org'...
copying path '/nix/store/591702m05jkg4b0qigqbx8rnxfzzsvbf-gnugrep-3.1' from 'https://cache.nixos.org'...
copying path '/nix/store/j6migyd1qv56z6mspvbskwjx96yaqfa6-coreutils-8.29' from 'https://cache.nixos.org'...
copying path '/nix/store/6bjfc900zpkn6mxxhbh7y4dcnrnnfdk2-gnutar-1.30' from 'https://cache.nixos.org'...
copying path '/nix/store/102z71i6ps917xm39il291dc2dqbj52g-diffutils-3.6' from 'https://cache.nixos.org'...
copying path '/nix/store/nghz9s7syj5hjflq9fk0r6480sh18h04-findutils-4.6.0' from 'https://cache.nixos.org'...
copying path '/nix/store/vhyv19xv3lzs9sdd8ss81fx05bq96zv0-stdenv-linux' from 'https://cache.nixos.org'...
building '/nix/store/2s6fj1pc4aakbv199hhjhspq4z5j5rzl-auto-patchelf-hook.drv'...
/nix/store/9i0x4xwjh19as5msf7zrwarcasbwr632-auto-patchelf-hook

@aszlig
Copy link
Member

aszlig commented Sep 23, 2018

@Mic92: Ah, damn, I did one as well (without the printf though): #47244

@aszlig
Copy link
Member

aszlig commented Sep 23, 2018

Going to test (and if successful, merge) this later on all of the usages in nixpkgs, verifying whether their contents are still the same.

@Mic92
Copy link
Member Author

Mic92 commented Sep 23, 2018

fair enough.

@aszlig
Copy link
Member

aszlig commented Sep 25, 2018

@Mic92: Status update: Something is still wrong here (I did the diffs with nuke-refs and removal of symlinks):

/nix/store/x5cm6sd3gdjmci52n8kvvjgswpfi4a70-cups-kyodialog3-8.1601 -> /nix/store/2gcjl6s908w2p1x89cf5j84kyh6rywgp-cups-kyodialog3-8.1601 has same content
/nix/store/37cysx7kadf9y20hxjlqv9q43mz5na75-elasticsearch-6.3.2 -> /nix/store/1cfq9hcl3hx9jf1bmdarmxvyq0z35v4g-elasticsearch-6.3.2 differs
/nix/store/1m2p230mapcdvlhf8krwk1akqk4ixn0n-franz-4.0.4 -> /nix/store/vndsx1f6cvayq1ycq6l95dn2xnxkh7zn-franz-4.0.4 differs
/nix/store/xrnz7ybyky0d4za6sv4bvr6wc44h1af2-gurobi-8.0.1 -> /nix/store/bjisljzafs9dkahsyrnnbp87hlb0cgqf-gurobi-8.0.1 has same content
/nix/store/cgyw3ypp23v52mnvy1dmy59dgbsxs75j-javacard-devkit-2.2.2 -> /nix/store/fadw4cch8s9fbc3g8q0nhw34djkwnl98-javacard-devkit-2.2.2 differs
/nix/store/nzwcjpyfhd79akibfx5crx8vzj61l0md-masterpdfeditor-5.1.12 -> /nix/store/gysbggmmkwqhxf7kvpfnii511dkfg11j-masterpdfeditor-5.1.12 has same content
/nix/store/kxg2vy5r5iv2cahi75791a03cb02dw7w-MaXX-Indy-1.1.0 -> /nix/store/ddnwh1spca4hrydy3h6ggncd8fhriigd-MaXX-Indy-1.1.0 differs
/nix/store/pjya5g02278sgnfkjnjadnfka8yqysdf-oracle-instantclient-12.2.0.1.0 -> /nix/store/l9lz6a8cnh7qrn8xfxzic40mhd671gr8-oracle-instantclient-12.2.0.1.0 differs
/nix/store/h9h3pnbp4r7wsfwyp4mp0pivl7xxcx8k-powershell-6.0.4 -> /nix/store/43riljrhgf9m9x32l47czc329x058fzp-powershell-6.0.4 differs
/nix/store/gsj66nn6k97vg0r1pk3s735v65fz08j4-reaper-5.94 -> /nix/store/yiyav307xry96a12ssm1w45gsdydnvka-reaper-5.94 differs
/nix/store/jslx0sp3c6fh6wav6v21856ybkv52lwq-teamviewer-13.1.3026 -> /nix/store/l4gdf66v1iwanzgrb988dxjnkkh0v7mz-teamviewer-13.1.3026 has same content
/nix/store/mqyz5nixjwibaqdg27bsr7wgnbk8kg8w-msodbcsql17-17.2.0.1-1 -> /nix/store/0fpca3zcl8r28gl40cv2kgwwz14nlqkp-msodbcsql17-17.2.0.1-1 differs
/nix/store/l0yfgzrjhyp9a5v29pxrhf9f47gm7x8q-virtlyst-1.2.0 -> /nix/store/yyvpdzbbh57xc2ksrj8cvahkmsd8l4la-virtlyst-1.2.0 differs
/nix/store/jv8hf696l8vzdfrav721cglsw63v9cf3-wavebox-3.14.10 -> /nix/store/vvmi7likxwdr149xijqc03hw99a87gb2-wavebox-3.14.10 differs
/nix/store/qfhf9f9irmd40ap6nzj40pqg37kb3msb-zoom-us-2.4.129780.0915 -> /nix/store/wwdq49h5qksw3ksmqpzqv8bb54ydi7lv-zoom-us-2.4.129780.0915 has same content

Some of these differences look a bit odd as well (might be patchelf versus non-patchelfed):

   Version:                           1 (current)
   OS/ABI:                            UNIX - System V
   ABI Version:                       0
   Type:                              DYN (Shared object file)
   Machine:                           Advanced Micro Devices X86-64
   Version:                           0x1
   Entry point address:               0x44380
-  Start of program headers:          7983104 (bytes into file)
+  Start of program headers:          64 (bytes into file)
   Start of section headers:          7977872 (bytes into file)
   Flags:                             0x0
   Size of this header:               64 (bytes)
   Size of program headers:           56 (bytes)
-  Number of program headers:         8
+  Number of program headers:         7
   Size of section headers:           64 (bytes)
   Number of section headers:         31
-  Section header string table index: 28
+  Section header string table index: 30

@aszlig aszlig self-assigned this Sep 25, 2018
@aszlig
Copy link
Member

aszlig commented Sep 25, 2018

Ah, damn... of course... we now check for PT_INTERP which is not a very good idea given that it only applies to executables. So we need to only do this if isExecutable is also the case.

If the ELF file is not an executable, we do not get a PT_INTERP section,
because after all, it's a *shared* library.

So instead of checking for PT_INTERP (to avoid statically linked
executables) for all ELF files, we add another check to see if it's an
executable and *only* skip it when it is and there's no PT_INTERP.

Signed-off-by: aszlig <aszlig@nix.build>
The "maxx" package recursively runs isExecutable on a bunch of files and
since the change to use "readelf" instead of "file" a lot of errors like
this one are printed during build:

  readelf: Error: Not an ELF file - it has the wrong magic bytes at the
  start

While the isExecutable was never meant to be used outside of the
autoPatchelfHook, it's still a good idea to silence the errors because
whenever readelf fails, it clearly indicates that the file in question
is not a valid ELF file.

Signed-off-by: aszlig <aszlig@nix.build>
The unfree variant of elasticsearch uses autoPatchelfHook and since we
removed the dependency on file for the hook itself in
58a97df we no longer have zlib
propagated.

So we need to explicitly state that dependency here.

Signed-off-by: aszlig <aszlig@nix.build>
Cc: @apeschar, @basvandijk
@GrahamcOfBorg
Copy link

Success on aarch64-linux (full log)

Attempted: autoPatchelfHook

The following builds were skipped because they don't evaluate on aarch64-linux: elasticsearch

Partial log (click to expand)

copying path '/nix/store/5mkmmv570vcd316zyrl2gjx9fx7diamx-paxctl-0.9' from 'https://cache.nixos.org'...
copying path '/nix/store/l23i4s7n3vk2va4l366ik1a78lxhkkdf-xz-5.2.4' from 'https://cache.nixos.org'...
copying path '/nix/store/l93zy028a4b7d7md143nsccx7p7micsi-patch-2.7.6' from 'https://cache.nixos.org'...
copying path '/nix/store/01yaafshnlclr1i1mdc6l0pk30z3zady-hook' from 'https://cache.nixos.org'...
copying path '/nix/store/7rwg9nnr957pr9p715yfan908m4f8zbh-xz-5.2.4-bin' from 'https://cache.nixos.org'...
copying path '/nix/store/102z71i6ps917xm39il291dc2dqbj52g-diffutils-3.6' from 'https://cache.nixos.org'...
copying path '/nix/store/nghz9s7syj5hjflq9fk0r6480sh18h04-findutils-4.6.0' from 'https://cache.nixos.org'...
copying path '/nix/store/vhyv19xv3lzs9sdd8ss81fx05bq96zv0-stdenv-linux' from 'https://cache.nixos.org'...
building '/nix/store/lm4lv4sba3q6ig58f1mmywwwnacg4l7i-auto-patchelf-hook.drv'...
/nix/store/sz89qqp9djg1axnasbi3l050qrin5v1v-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on x86_64-darwin (full log)

Attempted: autoPatchelfHook

The following builds were skipped because they don't evaluate on x86_64-darwin: elasticsearch

Partial log (click to expand)


b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
  { allowUnfree = true; }
to ~/.config/nixpkgs/config.nix.


these derivations will be built:
  /nix/store/lmd30g0rb0d8sqs3lrm14drxdndzpakz-auto-patchelf-hook.drv
building '/nix/store/lmd30g0rb0d8sqs3lrm14drxdndzpakz-auto-patchelf-hook.drv'...
/nix/store/6gwnwpm2nv20f3qk3ggl303g7sdmgamb-auto-patchelf-hook

@GrahamcOfBorg
Copy link

Success on x86_64-linux (full log)

Attempted: autoPatchelfHook

The following builds were skipped because they don't evaluate on x86_64-linux: elasticsearch

Partial log (click to expand)


b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
  { allowUnfree = true; }
to ~/.config/nixpkgs/config.nix.


these derivations will be built:
  /nix/store/fxma5s49jsf2qx5gn24a5l2m2xw4c89f-auto-patchelf-hook.drv
building '/nix/store/fxma5s49jsf2qx5gn24a5l2m2xw4c89f-auto-patchelf-hook.drv'...
/nix/store/060grijg3f2icl963b5dkrxyw68n460l-auto-patchelf-hook

@aszlig aszlig merged commit 8df68a9 into NixOS:master Sep 25, 2018
aszlig added a commit that referenced this pull request Sep 25, 2018
This includes the initialy commit was done by @Mic92 plus a few fixes
from my side. So essentially this avoids patching statically linked
executables and also speeds up searching for ELF files altogether.

I've tested this by comparing the outputs of all the derivations which
make use of this hook using the following Nix expression:

  let
    getPackagesForRev = rev: with import (builtins.fetchGit {
      url = ./.;
      inherit rev;
    }) { config.allowUnfree = true; }; [
      cups-kyodialog3 elasticsearch franz gurobi javacard-devkit
      masterpdfeditor maxx oracle-instantclient powershell reaper
      teamviewer unixODBCDrivers.msodbcsql17 virtlyst wavebox zoom-us
    ];

    pkgs = import <nixpkgs> {};
    baseRev = "ef764eb0d8314b81a012dae04642b4766199956d";

  in pkgs.runCommand "diff-contents" {
    chset = pkgs.lib.zipListsWith (old: new: pkgs.runCommand "diff" {
      inherit old new;
      nativeBuildInputs = [ pkgs.nukeReferences ];
    } ''
      mkdir -p "''${NIX_STORE#/}"
      cp --no-preserve=all -r "$old" "''${NIX_STORE#/}"
      cp --no-preserve=all -r "$new" "''${NIX_STORE#/}"
      find "''${old#/}" "''${new#/}" \
        \( -type f -exec nuke-refs {} + \) -o \( -type l -delete \)
      mkdir "$out"
      echo "$old" > "$out/old-path"
      echo "$new" > "$out/new-path"
      diff -Nur "''${old#/}" "''${new#/}" > "$out/diff" || :
    '') (getPackagesForRev baseRev) (getPackagesForRev "");
  } ''
    err=0
    for c in $chset; do
      if [ -s "$c/diff" ]; then
        echo "$(< "$c/old-path") -> $(< "$c/new-path")" \
             "differs, report: $c/diff" >&2
        err=1
      fi
    done
    [ $err -eq 0 ] && touch "$out"
  ''

With these changes there is only one derivation which has altered
contents, which is "franz". However the reason why it has differing
contents is not directly because of the autoPatchelfHook changes, but
because the "env-vars" file from the builder is in
"$out/opt/franz/env-vars" (Cc: @gnidorah) and we now have different
contents for NIX_CFLAGS_COMPILE and other environment variables.

I also tested this against a random static binary and the hook no longer
tries to patch it.

Merges: #47222
@Mic92 Mic92 deleted the auto-patchelf branch September 25, 2018 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants