Skip to content

Instantly share code, notes, and snippets.

@vladshablinsky
Last active August 29, 2015 14:23
Show Gist options
  • Save vladshablinsky/bd4f1e4c29a4ed28fa59 to your computer and use it in GitHub Desktop.
Save vladshablinsky/bd4f1e4c29a4ed28fa59 to your computer and use it in GitHub Desktop.
Formula renames: progress and todo

Homebrew: Formula renames

Workflow

  • Explore tap structure
  • Check if the installed formula in the Cellar belongs to appropriate tap while performing migrations
  • Metadata for tap formula migrations
  • Dependencies on taps
  • What if several formulae installed to the same directory
  • Skip if multiple taps formulae installed???
  • What to do with INSTALL_RECEIPT? The path to the formula is wrong after the migration.
  • user and repo .downcase (No need)
  • don't treat tap_formula_renames.rb like a formula
  • INSTALL_RECEIPT.json isn't changed after tap migratioins! issue/bug/feature?
  • Change INSTALL_RECEIPT after formula renames and migrations
  • Update cmd/migrator !!!
  • πŸ”₯ What if formula was installed using its path??? Everything that uses Formulary.from_rack fails. We have INSTALL_RECEIPT.json for each keg and can find path to the formula, maybe it worth doing so? SOLUTION: rescue exceptions in uninstall everything else behaves same as it was.
  • πŸ”₯ Check everywhere what tap the formula belongs to or add Exception? (Check if installed formula belongs to tap or to core formula) SOLUTION: The same as previous.
  • Probably worth adding a test for brew uninstall <formula>, where <formula> doesn't belong to any tap?
  • ??? Ignore if tap isn't specified???
  • if tap is nil, then treat package like a core formula.
  • πŸ”₯ πŸ”₯ πŸ”₯ If we install from tap and then it moves to in that tap brew migrate <formula> doesn't work, because it can't find the formula among core formulae with oldname. TODO: probably try to search through taps. However, brew migrate user/repo/<formula> works.

(SOLVED) If multiple formulae from taps with given oldname available, TapFormulaWithOldnameAmbiguityError is raised.

  • πŸ”₯ πŸ”₯ πŸ”₯ Same is true for dependencies on oldname. If something depends on formula from tap and there is no such a formula in among core formulae, then we try to find appropriate formula among taps. However, if the formula in tap is renamed, we won't find it. It's because from_oldname method in Formulary doesn't serach through tap renames.

Week 8

  • rebase onto origin/master
  • write script for testing update
  • test structure
    • MiniTest framework
    • testing_env.rb
    • temporary files e.g. for tests
    • test_ARGV.rb

Confusing pitfall πŸ”₯ (SOLVED)

ln -s /private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs/other

works fine

irb(main):027:0> other
=> #<Pathname:/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs/other>
irb(main):028:0> other.make_relative_symlink Pathname.new "/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5"
=> 0

works fine

irb(main):029:0> other.make_relative_symlink Pathname.new "/private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cella
=> 0

broken

def make_relative_symlink(src)
    dirname.mkpath
    File.symlink(src.relative_path_from(dirname), self)
end

relative_path_from doesn't accept symlinks. link

➜ vlad:Homebrew$ cd /private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs
➜ vlad:PinnedKegs$ readlink other
../../../../../../../../../private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
➜ vlad:PinnedKegs$ cd /var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs
➜ vlad:PinnedKegs$ readlink other
../../../../../../../../../private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
➜ vlad:PinnedKegs$ cd ../../../../../../../../../private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
➜ vlad:2.5$ cd /private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs
➜ vlad:PinnedKegs$ pwd
/private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/prefix/Library/PinnedKegs
➜ vlad:PinnedKegs$ cd ../../../../../../../../../private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
cd: no such file or directory: ../../../../../../../../../private/var/folders/ng/7d7b3z697tq28vn6j28b0hl80000gn/T/homebrew-tests/cellar/oldname/2.5
➜ vlad:PinnedKegs$

The problem is in relative symlinks, when symlink is passed SOLUTION: in short: can't pass one link with realpath and another without realpath.

  • write script for testing migrate

  • get familiar with existing unit tests

  • unit tests for migrator

  • test keg-only with otool -L

➜ vlad:Homebrew$ mv ../Formula/openssl.rb ../Formula/newopenssl.rb
➜ vlad:Homebrew$ vim ../Formula/newopenssl.rb                 formula-renames βœ—
➜ vlad:Homebrew$ vim formula_renames.rb                       formula-renames βœ—
➜ vlad:Homebrew$ brew migrate openssl                         formula-renames βœ—
==> Migrating openssl to newopenssl
Copying to: /usr/local/Cellar/newopenssl
==> Uninstalling and unlinking openssl
==> Linking newopenssl
➜ vlad:Homebrew$ otool -L ../../Cellar/openssl/1.0.2c/bin/openssl
../../Cellar/openssl/1.0.2c/bin/openssl:
	/usr/local/Cellar/openssl/1.0.2c/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/local/Cellar/openssl/1.0.2c/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)

Week 9

  • check uninstall for bugs with realpath
  • script for testing migrator
  • check update/uninstall
  • ??? fixing all existing INSTALL_RECEIPT? (can be broken for tap migrations)
  • unit tests for formulary (no tests at all)

Week 10

  • What sequence to use in Dependency#to_formula

    • from name
    • from oldname
    • from tap
    • ftom tap oldname
  • Cleanup

  • Fix Exceptions for unavailable oldname
  • πŸ”₯ brew reinstall. Example: if core ack -> newack and we have ack in some tap, then brew reinstall ack reinstalls ack.

    (DON'T DO ANYTHING IF Cellar/f.name is a symlink.

? Formula#installed -- says something installed even if it's a symlink. ? Tests for oldname unavailable errors.

  • Add Formulary#to_rack and change ARGV#kegs behaviour.
  • Remove from_oldname and all stuff depending on it.

Week 11

Cleanup and test

Tests

  • Migrate core formula
  • Migrate tap formula (canonical/fully-qualified)
➜ vlad:Homebrew$ brew install libpng                            formula-renames βœ—
==> Installing libpng from vladshablinsky/homebrew-taptest
==> Downloading https://homebrew.bintray.com/bottles/libpng-1.6.17.yosemite.bottle
Already downloaded: /Library/Caches/Homebrew/libpng-1.6.17.yosemite.bottle.tar.gz
==> Pouring libpng-1.6.17.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/libpng/1.6.17: 17 files, 1.2M
➜ vlad:Homebrew$ brew migrate libpng                            formula-renames βœ—
Error: No available formula with oldname libpng

πŸ”₯ πŸ”₯ πŸ”₯

  • Migrate core formula when the same tap formula exists
  • Migrate tap formula when same core one exists
  • Migrate tap formula using canonical name
  • Migrate tap formula using canonical name, when multiple taps have this formula
  • Install formula from path and try to migrate it (MigratorDifferentTapsError is raised)
  • Migrate formula, that depends on another
  • Migrate library some formula depends on (tested on zint and libpng, itstool and libxml2)
  • The same for library formulae
  • Dependency on tap oldname
  • Dependency on core formula oldname (reinstall zint after renaming libpng)
  • Uninstall using oldname (canonical/fully-qualified)
  • Uninstall usinga newname (canonical/fully-qualified)

update tests

  • libpng -> newlibpng, libpng installed
  • libpng -> newlibpng, user/repo/libpng installed
  • user/repo/libpng -> user/repo/newlibpng, libpng installed
  • user1/repo1/linpng -> user1/repo1/newlibpng, user/repo/libpng installed
  • libpng -> newlibpng & user/repo/libpng -> user/repo/newlibpng, libpng installed
  • libpng -> newlibpng & user/repo/libpng -> user/repo/newlibpng, user/repo/libpng installed
  • libpng -> newlibpng & user/repo/libpng -> user/repo/newlibpng, user1/repo1/libpng installed

What was added and changed

  • Add migrate command.
  • uninstall doesn't uninstall name if Cellar/name is a symlink.
  • uninstall uninstalls package with its oldname symlinks.
  • update perform formula migrations and tap formula migrations for renamed formulae.
  • update changes Tab if formula migrates to a tap.
  • Formula#oldname returns oldname for formula.
  • Formulary.from_oldname(ref) returns formula instance for formula with ref as oldname.
  • FormulaWithOldnameUnavailableError < FormulaUnavailableError exception if no formula with oldname available.
  • TapFormulaWithOldnameUnavailableError < TapFormulaUnavailableError same for tap formulae.
  • Dependency#to_formula(name) tries to load formula with that oldname and if it can't, it loads formula with that name
  • Add renamed_formulae to extend/ARGV.rb to instantiate new formulae from old name.
  • ARGV.kegs raises NotAKegError if passed name is a symlink.
  • update skips tap_formula_renames.rb and doesn't treat it like a formula.
  • Don't treat tap_formula_renames.rb like a formula in Tap#formula_files.
  • Add Tap#tap_formula_renames loads tap_formula_renames.rb file.

Uninstalling

Initial uninstall command was divided into two behaviours: with --force and without it.

  • brew uninstall --force <formula>

    before:

    1. get ARGV.named
    2. get canonical name
    3. get rack directory
    4. remove each subdir as a keg
    5. remove pin
  • brew uninstall <formula>

    before:

    1. get ARGV.kegs:
    1. get canonical name and rack
    2. get rack subdirs
    3. init linked_keg_ref, opt_prefix
    4. `if opt_prefix.symlink? && opt_prefix.directory?` => keg from opt_prefix.real_path
    5. same for `linked_keg_ref`
    6. else if number of subdirs is 1, then keg from the subdir.
    7. else if prefix for formula tap formula (`Formulary.factory(name)`) or for core
       formula (`Formulary.from_rack(rack)`)
       isn't empty, then keg from this prefix
    8. else raise `MultipleVersionsInstalledError`
    
    1. remove one keg for each given name

    brew uninstall [--force] ack and brew uninstall [ --force] user/repo/ack uninstall any installed ack

    now both with --force and withou it:

    • make a formula instance for rack
    • remove symlinks for oldname

Structure for tap formula renames

  • Each tap with renamed formulae has tap_formula_renames.rb has TAP_FORMULA_RENAMES hash, which store information about renames in the following format:

    # tap_formula_renames.rb
    TAP_FORMULA_RENAMES = {
      "ack" => "newack"
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment