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

Separate staging from download. #4402

Merged
merged 8 commits into from
Jul 12, 2018

Conversation

reitermarkus
Copy link
Member

@reitermarkus reitermarkus commented Jul 1, 2018

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew tests with your changes locally?

Separate unpacking the download from download strategies. These unpack strategies should then also used in the Container classes used for staging casks.

@ghost ghost assigned reitermarkus Jul 1, 2018
@ghost ghost added the in progress Maintainers are working on this label Jul 1, 2018
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

As a general comment it's not clear due to the file change what's been moved as-is and what's been changed. I'd suggest moving to unpack strategy in one PR (or at least one commit) and then making the relevant changes in another.

magic_number = magic_number(path)

strategies = [
JarUnpackStrategy,
Copy link
Member

Choose a reason for hiding this comment

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

Can this be inferred somehow rather than hardcoded? If not, a class level constant feels a bit nicer.

Copy link
Member Author

Choose a reason for hiding this comment

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

a class level constant feels a bit nicer.

Not sure how a constant could replace this.

Copy link
Member

Choose a reason for hiding this comment

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

A frozen constant array of classes rather than a variable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I thought you were talking about JarUnpackStrategy.

def self.from(download:); end

def self.magic_number(path)
File.binread(path, 252)
Copy link
Member

Choose a reason for hiding this comment

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

A constant explaining what 252 means would be good.


# This is so that bad tarballs and zips produce good error messages.
strategy ||= case path.extname
when ".tar.gz", ".tgz", ".tar.bz2", ".tbz"
Copy link
Member

Choose a reason for hiding this comment

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

Might want to add .tar.xz here?

end

attr_reader :path, :magic_number
private :magic_number
Copy link
Member

Choose a reason for hiding this comment

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

I think it's probably nicer to just use the instance variable.

end
end
end
private :pipe_to_tar
Copy link
Member

Choose a reason for hiding this comment

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

Can you order these so you can have a single private?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm trying to follow the new RuboCop rule (which we disabled for now because of false positives). Basically, the private should be next to the method, so it is much easier to tell if a method is private or not.

Copy link
Member

Choose a reason for hiding this comment

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

Does that rule have an autocorrect? If not I think it's going to prove too painful to implement this consistently across the codebase (and I think remaining consistent for now would be better).

end

class UncompressedUnpackStrategy < UnpackStrategy
def self.can_extract?(**)
Copy link
Member

Choose a reason for hiding this comment

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

Would be nicer to avoid ** here I think.

end

def extract_to_dir(unpack_dir)
if OS.mac?
Copy link
Member

Choose a reason for hiding this comment

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

This should use os/extend (if it's needed, I'm not sure why it is)

return true if magic_number.match?(/^.{257}ustar/n)

# Check if `tar` can list the contents, then it can also extract it.
IO.popen(["tar", "-t", "-f", path], err: File::NULL) { |stdout| !stdout.read(1).nil? }
Copy link
Member

Choose a reason for hiding this comment

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

do form block here

@reitermarkus
Copy link
Member Author

As a general comment it's not clear due to the file change what's been moved as-is and what's been changed. I'd suggest moving to unpack strategy in one PR (or at least one commit) and then making the relevant changes in another.

Nothing has been moved as-is, because previously everything was just unpacked in PWD. So every command had to be adapted to support unpacking into a specified directory.

@MikeMcQuaid
Copy link
Member

Nothing has been moved as-is, because previously everything was just unpacked in PWD. So every command had to be adapted to support unpacking into a specified directory.

Sure; I'm suggesting this adaption happen in a future PR (or even separate commits in this one) and this one focuses on trying to just move things as-is. It's very difficult to review what's changed and what hasn't, as-is.

@reitermarkus reitermarkus force-pushed the separate-staging branch 9 times, most recently from 27d629b to 2395008 Compare July 6, 2018 16:10
@reitermarkus reitermarkus requested review from MikeMcQuaid and JCount and removed request for JCount July 6, 2018 16:22
@@ -45,7 +46,9 @@ def ohai(*args)
# Unpack {#cached_location} into the current working directory, and possibly
# chdir into the newly-unpacked directory.
# Unlike {Resource#stage}, this does not take a block.
def stage; end
def stage
UnpackStrategy.detect(cached_location).extract(basename: basename_without_params)
Copy link
Member

Choose a reason for hiding this comment

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

Stick .extract on a new line, maybe?

@@ -384,7 +344,7 @@ def _fetch
# Useful for installing jars.
class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy
def stage
cp cached_location, basename_without_params, preserve: true
UncompressedUnpackStrategy.new(cached_location).extract(basename: basename_without_params)
Copy link
Member

Choose a reason for hiding this comment

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

Stick .extract on a new line, maybe?

quiet_safe_system cvspath, { quiet_flag: "-Q" }, "-d", @url, "login" if @url.include? "pserver"
quiet_safe_system cvspath, { quiet_flag: "-Q" }, "-d", @url, "checkout", "-d", cache_filename, @module
quiet_safe_system "cvs", { quiet_flag: "-Q" }, "-d", @url, "login" if @url.include? "pserver"
quiet_safe_system "cvs", { quiet_flag: "-Q" }, "-d", @url, "checkout", "-d", cached_location.basename, @module, chdir: cached_location.dirname
Copy link
Member

Choose a reason for hiding this comment

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

Can this line be split across a few? It's pretty long.

@@ -0,0 +1,5 @@
class ZipUnpackStrategy
def extract_to_dir(unpack_dir, **)
safe_system "ditto", "-x", "-k", "--", path, unpack_dir
Copy link
Member

Choose a reason for hiding this comment

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

What makes this preferable to unzip?

Copy link
Member Author

Choose a reason for hiding this comment

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

This keeps Finder attributes intact.

Copy link
Member

Choose a reason for hiding this comment

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

What's that needed for?

end
end
end
private :pipe_to_tar
Copy link
Member

Choose a reason for hiding this comment

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

Does that rule have an autocorrect? If not I think it's going to prove too painful to implement this consistently across the codebase (and I think remaining consistent for now would be better).

path.directory?
end

def extract_to_dir(unpack_dir, **)
Copy link
Member

Choose a reason for hiding this comment

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

Would be nicer to avoid ** here I think.

end

class P7ZipUnpackStrategy < UnpackStrategy
def self.can_extract?(magic_number:, **)
Copy link
Member

Choose a reason for hiding this comment

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

Would be nicer to avoid ** here I think (and everywhere below).

Copy link
Member Author

Choose a reason for hiding this comment

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

How would you avoid these?

Copy link
Member

Choose a reason for hiding this comment

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

Specify the arguments that would be passed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Then RuboCop will complain about unused arguments.

Copy link
Member

Choose a reason for hiding this comment

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

@reitermarkus They can be prefixed with _ and there may be other options depending on the types of arguments (I could be wrong on this, though). I think that's preferable to accepting literally any number of random arguments.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, there is a RuboCop setting to allow unused keyword arguments.

return true if magic_number.match?(/\A.{257}ustar/n)

# Check if `tar` can list the contents, then it can also extract it.
IO.popen(["tar", "-t", "-f", path], err: File::NULL) do |stdout|
Copy link
Member

Choose a reason for hiding this comment

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

Let's go back to tf here for consistency


class ZipUnpackStrategy < UnpackStrategy
def self.can_extract?(magic_number:, **)
magic_number.match?(/\APK(\003\004|\005\006)/n)
Copy link
Member

Choose a reason for hiding this comment

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

Feels like there's maybe a nicer interface for this given pretty much all the self.can_extract methods are the same?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can't think of any, since some need more than just the magic number.

Copy link
Member

Choose a reason for hiding this comment

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

How about the default implementation in UnpackStrategy being magic_number.match?(magic_regex) or something and each subclass can define magic_regex?

Copy link
Member Author

@reitermarkus reitermarkus Jul 6, 2018

Choose a reason for hiding this comment

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

Tried it, but it only get's worse.

For example, XzUnpackStrategy < TarUnpackStrategy < FileUnpackStrategy:

This needs to override the custom can_extract? in TarUnpackStrategy, so it will be exactly the same as in FileUnpackStrategy again, and we also have to add a magic_regex constant/method.

Copy link
Member

Choose a reason for hiding this comment

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

Cool, no worries, thanks for checking.

return false unless magic_number.match?(/\ASQLite format 3\000/n)

# Fossil database is made up of artifacts, so the `artifact` table must exist.
Utils.popen_read("sqlite3", path, "select count(*) from sqlite_master where type = 'view' and name = 'artifact'").to_i == 1
Copy link
Member

Choose a reason for hiding this comment

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

This is a long line; can it get split?

@reitermarkus reitermarkus force-pushed the separate-staging branch 4 times, most recently from 8a0b5be to 8f55a8c Compare July 6, 2018 19:41
path.directory?
end

def extract_to_dir(unpack_dir, **)
Copy link
Member

Choose a reason for hiding this comment

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

Keyword args look good; can this have the final args named _? Thanks.

def extract_to_dir(unpack_dir, **)
FileUtils.cp_r path.children, unpack_dir, preserve: true
end
private :extract_to_dir
Copy link
Member

Choose a reason for hiding this comment

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

Would still like to see this be consistent with the rest of Homebrew and, if you feel like it, a general fixup of the RuboCop rule after this is merged and consistent.

@reitermarkus reitermarkus force-pushed the separate-staging branch 4 times, most recently from ea208f4 to b0a58cb Compare July 10, 2018 23:57
@reitermarkus
Copy link
Member Author

@JCount and @MikeMcQuaid, one last look?

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

A few nits, none of which is blocking so feel free to disagree with any of them. Nice work 👍

def quiet_safe_system(*args)
safe_system(*expand_safe_system_args(args))
def basename_without_params
return nil if @url.nil?
Copy link
Member

Choose a reason for hiding this comment

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

return unless @url?

@@ -960,16 +886,23 @@ def repo_valid?
(cached_location/"CVS").directory?
end

def quiet_flag
ARGV.verbose? ? nil : "-Q"
Copy link
Member

Choose a reason for hiding this comment

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

"-Q" if ARGV.verbose??

magic_number = if path.directory?
""
else
File.binread(path, MAX_MAGIC_NUMBER_LENGTH) || ""
Copy link
Member

Choose a reason for hiding this comment

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

Given the "" fallback in both cases I wonder if:

magic_number = File.binread(path, MAX_MAGIC_NUMBER_LENGTH) if path.directory?
magic_number ||= ""

or similar might be better?

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried some variations, but I like this version because it is more explicit.


def extract_nested_tar(unpack_dir, basename:)
return unless DependencyCollector.tar_needs_xz_dependency?
return unless (children = unpack_dir.children).count == 1
Copy link
Member

Choose a reason for hiding this comment

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

I have no real preference but if .. != sometimes reads nicer to me.

@reitermarkus
Copy link
Member Author

Should be ready to go now.

Copy link
Contributor

@JCount JCount left a comment

Choose a reason for hiding this comment

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

Looks good to me, but let’s wait on @MikeMcQuaid before merging.

@reitermarkus reitermarkus merged commit aa3a369 into Homebrew:master Jul 12, 2018
@ghost ghost removed the in progress Maintainers are working on this label Jul 12, 2018
@reitermarkus reitermarkus deleted the separate-staging branch July 12, 2018 19:28
@ilovezfs
Copy link
Contributor

iMac-TMP:Homebrew joe$ git bisect good
5b3bbb76c9d224c08ae498677868b69aab71ff7c is the first bad commit
commit 5b3bbb76c9d224c08ae498677868b69aab71ff7c
Author: Markus Reiter <[email protected]>
Date:   Sun Jul 1 23:35:29 2018 +0200

    Separate staging from download.

:040000 040000 6d5d52263444dbca74d030b9714bd87afff1d386 183e6d00db7777ddcadf1ce9017e320bed2d45c2 M	Library

This has caused some Git checkouts to be unclean that were previously clean.

For kubernetes-cli,

Before:

bash-3.2$ git status
Not currently on any branch.
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.brew_home/

nothing added to commit but untracked files present (use "git add" to track)

After:

bash-3.2$ git status
Not currently on any branch.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	typechange: .bazelrc
	typechange: .kazelcfg.json
	typechange: BUILD.bazel
	typechange: Makefile
	typechange: Makefile.generated_files
	typechange: WORKSPACE

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.brew_home/

no changes added to commit (use "git add" and/or "git commit -a")

This is now causing CI failure for anything that checks that its build info reports a clean not dirty tag.

@ilovezfs
Copy link
Contributor

iMac-TMP:Homebrew joe$ git bisect bad
5b3bbb76c9d224c08ae498677868b69aab71ff7c is the first bad commit
commit 5b3bbb76c9d224c08ae498677868b69aab71ff7c
Author: Markus Reiter <[email protected]>
Date:   Sun Jul 1 23:35:29 2018 +0200

    Separate staging from download.

:040000 040000 6d5d52263444dbca74d030b9714bd87afff1d386 183e6d00db7777ddcadf1ce9017e320bed2d45c2 M	Library
iMac-TMP:Homebrew joe$ 

This has broken the ability to install cromwell on El Capitan.

See https://jenkins.brew.sh/job/Homebrew%20Testing/1023/ and https://jenkins.brew.sh/job/Homebrew%20Core%20Pull%20Requests/27643/

==> brew install --verbose cromwell
==> FAILED
/usr/bin/sandbox-exec -f /private/tmp/homebrew20180720-32126-1i16pn6.sb nice /usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/bin/ruby -W0 -I /usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/site_ruby/2.3.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/site_ruby/2.3.0/x86_64-darwin9.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/site_ruby/2.3.0/universal-darwin9.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/site_ruby:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/vendor_ruby/2.3.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin9.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/vendor_ruby/2.3.0/universal-darwin9.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/vendor_ruby:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/2.3.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/2.3.0/x86_64-darwin9.0:/usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.7/lib/ruby/2.3.0/universal-darwin9.0:/usr/local/Homebrew/Library/Homebrew/cask/lib:/usr/local/Homebrew/Library/Homebrew -- /usr/local/Homebrew/Library/Homebrew/build.rb /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/cromwell.rb --verbose
==> Downloading https://github.com/broadinstitute/cromwell/releases/download/33.1/cromwell-33.1.jar
Already downloaded: /Users/brew/Library/Caches/Homebrew/cromwell-33.1.jar
==> Verifying cromwell-33.1.jar checksum
unzip -qq /Users/brew/Library/Caches/Homebrew/cromwell-33.1.jar -d /private/tmp/d20180720-32128-18ejwwt
warning [/Users/brew/Library/Caches/Homebrew/cromwell-33.1.jar]:  76 extra bytes at beginning or within zipfile
  (attempting to process anyway)
error [/Users/brew/Library/Caches/Homebrew/cromwell-33.1.jar]:  reported length of central directory is
  -76 bytes too long (Atari STZip zipfile?  J.H.Holm ZIPSPLIT 1.1
  zipfile?).  Compensating...
error:  expected central file header signature not found (file #133751).
  (please check that you have transferred or created the zipfile in the
  appropriate BINARY mode and that you have compiled UnZip properly)
Error: Failure while executing: unzip -qq /Users/brew/Library/Caches/Homebrew/cromwell-33.1.jar -d /private/tmp/d20180720-32128-18ejwwt

@lock lock bot added the outdated PR was locked due to age label Aug 19, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Aug 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants