Check-in Differences
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 46b7983812b50762 To 049fb106f34a323d
|
2014-12-27
| ||
| 11:54 | Fix clamp option declaration from :bool to :flag. check-in: 3c39fc7d17 user: mario tags: trunk | |
|
2014-12-26
| ||
| 20:25 | Fix staging_path target name, and path localization for downloaded phars. Retain just target package information from composer.lock. check-in: 049fb106f3 user: mario tags: trunk | |
| 13:39 | Minor bugfix release 1.3.3.4, updated composer plugin, more consistent staging/build_path handling, :attrs usage, exceptions instead of warnings. check-in: 193345284b user: mario tags: trunk, v1.3.3.4 | |
|
2014-06-18
| ||
| 06:43 | Merge pull request #712 from mlafeldt/cpan-metadata-nil Ignore unset CPAN metadata fields check-in: 1f2b4a8ab8 user: jls@semicomplete.com tags: trunk | |
|
2014-06-02
| ||
| 10:28 | Ignore unset CPAN metadata fields Otherwise this can happen: $ fpm -t deb -s cpan --verbose 'Lingua::JA::Romanize::Japanese' Asking metacpan about a module {:module=>"Lingua::JA::Romanize::Japanese", :level=>:info} Downloading perl module {:distribution=>"Lingua-JA-Romanize-Japanese", :version=>nil, :level=>:info} Setting package name from 'name' {:name=>"Lingua-JA-Romanize-Japanese", :level=>:info} /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/lib/fpm/package/cpan.rb:86:in `input': undefined method `join' for nil:NilClass (NoMethodError) from /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/lib/fpm/command.rb:299:in `block in execute' from /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/lib/fpm/command.rb:298:in `each' from /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/lib/fpm/command.rb:298:in `execute' from /usr/lib/ruby/gems/1.9.1/gems/clamp-0.6.3/lib/clamp/command.rb:67:in `run' from /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/lib/fpm/command.rb:449:in `run' from /usr/lib/ruby/gems/1.9.1/gems/clamp-0.6.3/lib/clamp/command.rb:125:in `run' from /usr/lib/ruby/gems/1.9.1/gems/fpm-1.1.0/bin/fpm:8:in `<top (required)>' from /usr/bin/fpm:23:in `load' from /usr/bin/fpm:23:in `<main>' check-in: 46b7983812 user: mathias.lafeldt@gmail.com tags: trunk | |
|
2014-05-20
| ||
| 18:31 | Merge pull request #700 from gerbercj/feature/sh_package_bugfixes Feature/sh package bugfixes check-in: 97546f0f78 user: jls@semicomplete.com tags: trunk | |
Changes to .travis.yml.
1 2 3 4 5 6 7 8 9 10 | language: ruby rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 script: rspec before_install: - sudo apt-get update -qq - sudo apt-get install -qq rpm | > | 1 2 3 4 5 6 7 8 9 10 11 | language: ruby rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 script: rspec before_install: - sudo apt-get update -qq - sudo apt-get install -qq rpm - sudo apt-get install -qq lintian |
Changes to CHANGELIST.
1 2 3 4 5 6 7 | 1.1.0 (April 23, 2014) - New package type: zip, for converting to and from zip files (Jordan Sissel) - New package type: sh, a self-extracting package installation shell archive. (#651, Chris Gerber) - 'fpm --version' will now emit the version of fpm. - rpm: supports packaging fifo files (Adam Stephens) - deb: Add --deb-use-file-permissions (Adam Stephens) - cpan: Improve how fpm tries to find cpan artifacts for download (#614, Tim Nicholas) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
1.3.3.4 (December 26, 2014)
- Restructured staging/conversion step to separate flags and dependency
resolution for multi-target building.
- Composer module changed to match Debian/Fedora packaging guidelines.
Prefixed vendor/ path is omitted, composer managebility averted.
Dependency transformation still is somewhat rough.
- Phar, Src, Exe, Composer module now use :attrs instead of [:meta].
- Added backtrace output for generic error message.
- Simple update filter added to "unprefix" input system packages.
1.3.3.3 (December 22, 2014)
- Added IPK / Listaller packaging target plugin.
- Added Windows (EXE) installer generation.
- Added PHP composer as source plugin.
- Added source-based file packaging support (-t src plugin).
- Added PHP phar archive generation plugin.
- Introduction of multi-target building with -t rpm,deb,ipk.
- Added -u update filters for simple packaging tasks (existing plugins: man,
desktop, appdata, fixperms, strip, composer, deps).
- Added --deb-sign option.
- Changed zip plugin to omit /tmp/package* path.
- Now supports .pax and .cpio through the tar plugin.
- First release of the `xpm` branch. Gemspec adapted.
1.3.3 (December 11, 2014)
- The fpm project now uses Contributor Covenant. You can read more about this on
the website: http://contributor-covenant.org/
- npm: Fix bug causing all `-s npm` attempts to fail due to a missing method.
This bug was introduced in 1.3.0. (#800, #806; Jordan Sissel)
- rpm: fix bug in rpm input causing a crash if the input rpm did not have any triggers
(#801, #802; Ted Elwartowski)
1.3.2 (November 4, 2014)
- deb: conversion from another deb will automatically use any changelog found in
the source deb (Jordan Sissel)
1.3.1 (November 4, 2014)
- deb: fix md5sums generation such that `dpkg -V` now works (#799, Matteo Panella)
- rpm: Use maximum compression when choosing xz (#797, Ashish Kulkarni)
1.3.0 (October 25, 2014)
- Fixed a bunch of Ruby 1.8.7-related bugs. (Jordan Sissel)
- cpan: Fix bug in author handling (#744, Leon Weidauer)
- cpan: Better removal of perllocal.pod (#763, #443, #510, Mathias Lafeldt)
- rpm: Use lstat calls instead of stat, so we don't follow symlinks (#765, Shrijeet Paliwal)
- rpm and deb: Now supports script actions on upgrades. This adds two new flags:
--before-upgrade and --after-upgrade. (#772, #661; Daniel Haskin)
- rpm: Package triggers are now supported. New flags: --rpm-trigger-before-install,
--rpm-trigger-after-install, --rpm-trigger-before-uninstall,
--rpm-trigger-after-target-uninstall. (#626, Maxime Caumartin)
- rpm: Add --rpm-init flag; similar to --deb-init. (Josh Dolitsky)
- sh: Skip installation if already installed for the given version. If forced,
the old installation is renamed. (#776, Chris Gerber)
- deb: Allow Vendor field to be omitted now by specifying `--vendor ""` (#778, Nate Brown)
- general: Add --log=level flag for setting log level. Levels are error, warn, info, debug. (Jordan SIssel)
- cpan: Check for Build.PL first before Makefile.PL (#787, Daniel Jay Haskin)
- dir: Don't follow symlinks when copying files (#658, Jordan Sissel)
- deb: Automatically provide a 'changes' file in debs because lintian
complains if they are missing. (#784, Jordan Sissel)
- deb: Fix and warn for package names that have spaces (#779, Grantlyk)
- npm: Automatically set the prefix to `npm prefix -g` (#758, Brady Wetherington and Jordan Sissel)
1.2.0 (July 25, 2014)
- rpm: Add --rpm-verifyscript for adding a custom rpm verify script to
your package. (Remi Hakim)
- Allow the -p flag to target a directory for writing the output package
(#656, Jordan Sissel)
- Add --debug-workspace which skips any workspace cleanup to let users debug things
if they break. (#720, #734; Jordan Sissel)
- rpm: Add --rpm-attr for controlling attribute settings per file. This setting
will likely be removed in the future once rpmbuild is no longer needed.
(#719)
- deb: Add --deb-meta-file to add arbitrary files to the control dir (#599, Dan Brown)
- deb: Add --deb-interest and --deb-activate for adding package triggers (#595, Dan Brown)
- cpan: Fix small bug in handling empty metadata fields (#712, Mathias Lafeldt)
- rpm: Fix bug when specifying both --architecture and --rpm-os (#707, #716; Alan Ivey)
- gem: Fix bug where --gem-version-bins is given but package has no bins (#688, Jan Vansteenkiste)
- deb: Set permissions correct on the package's internals. Makes lintian happier. (Jan Vansteenkiste)
- rpm: rpmbuild's _tmppath now respects --workdir (#714, Jordan Sissel)
- gem/rpm: Add --rpm-verbatim-gem-dependencies to use old-style (fpm 0.4.x)
rpm gem dependencies (#724, Jordan Sissel)
- gem/rpm: Fix bug for gem pessimistic constraints when converting to rpm (Tom Duckering)
- python: Fix small bug with pip invocations (#727, Dane Knecht)
1.1.0 (April 23, 2014)
- New package type: zip, for converting to and from zip files (Jordan Sissel)
- New package type: sh, a self-extracting package installation shell archive. (#651, Chris Gerber)
- 'fpm --version' will now emit the version of fpm.
- rpm: supports packaging fifo files (Adam Stephens)
- deb: Add --deb-use-file-permissions (Adam Stephens)
- cpan: Improve how fpm tries to find cpan artifacts for download (#614, Tim Nicholas)
|
| ︙ | ︙ |
Added CODE_OF_CONDUCT.md.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | # Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) |
Changes to CONTRIBUTORS.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 | Luke Macken (github: lmacken) Matt Blair (github: mblair) Thomas Meson (github: zllak) Oliver Hookins (github: ohookins) llasram sbuss Brett Gailey (github: dnbert) If you have contributed (bug reports, feature requests, help in IRC, blog posts, code, etc) and aren't listed here, please let me know if you wish to be added! | > | 15 16 17 18 19 20 21 22 23 24 25 26 | Luke Macken (github: lmacken) Matt Blair (github: mblair) Thomas Meson (github: zllak) Oliver Hookins (github: ohookins) llasram sbuss Brett Gailey (github: dnbert) Daniel Haskin (github: djhaskin987) If you have contributed (bug reports, feature requests, help in IRC, blog posts, code, etc) and aren't listed here, please let me know if you wish to be added! |
Changes to README.md.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 | The goal of FPM is to be able to easily build platform-native packages. * Creating packages easily (deb, rpm, etc) * Tweaking existing packages (removing files, changing metadata/dependencies) * Stripping pre/post/maintainer scripts from packages ## System packages | | > > | | > > > > | > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
The goal of FPM is to be able to easily build platform-native packages.
* Creating packages easily (deb, rpm, etc)
* Tweaking existing packages (removing files, changing metadata/dependencies)
* Stripping pre/post/maintainer scripts from packages
## System packages
Many Linux distros do not ship ruby C headers or a compiler by default, and
you'll need that to install fpm.
apt-get install ruby-dev gcc
yum install ruby-devel gcc
Additional packages will be required depending on the source and target package
types (rpmbuild for rpm, etc.). FPM will show the commands that are required
which you must map to your distribution's package names.
## Get with the download
You can install fpm with gem:
gem install fpm
Building a package named "awesome" might look something like this:
fpm -s <source type> -t <target type> [list of sources]...
"Source type" is what your package is coming from; a directory (dir), a rubygem
(gem), an rpm (rpm), a python package (python), a php pear module (pear), etc.
"Target type" is what your output package form should be. Most common are "rpm"
and "deb" but others exist (solaris, etc)
You have two options for learning to run FPM:
1. If you're impatient, just scan through `fpm --help`; you'll need various
|
| ︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 | request. If you don't know git, I also accept diff(1) formatted patches - whatever is most comfortable for you. * Want to lurk about and see what others are doing? IRC (#fpm on irc.freenode.org) is a good place for this as is the [mailing list](http://groups.google.com/group/fpm-users) ### Contributing by forking from GitHub First, create a github account if you do not already have one. Log in to github and go to [the main fpm github page](https://github.com/jordansissel/fpm). At the top right, click on the button labeled "Fork". This will put a forked copy of the main fpm repo into your account. Next, clone your account's github repo of fpm. For example: | > > > > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | request. If you don't know git, I also accept diff(1) formatted patches - whatever is most comfortable for you. * Want to lurk about and see what others are doing? IRC (#fpm on irc.freenode.org) is a good place for this as is the [mailing list](http://groups.google.com/group/fpm-users) ### Contributing by forking from GitHub Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. See the [CODE\_OF\_CONDUCT.md](CODE_OF_CONDUCT.md). First, create a github account if you do not already have one. Log in to github and go to [the main fpm github page](https://github.com/jordansissel/fpm). At the top right, click on the button labeled "Fork". This will put a forked copy of the main fpm repo into your account. Next, clone your account's github repo of fpm. For example: |
| ︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 188 189 190 | At this point, the fpm command should run directly from the code in your cloned repo. Now simply make whatever changes you want, commit the code, and push your commit back to master. If you think your changes are ready to be merged back to the main fpm repo, you can generate a pull request on the github website for your repo and send it in for review. ## More Documentation [See the wiki for more docs](https://github.com/jordansissel/fpm/wiki) | > > > > > > > > > > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
At this point, the fpm command should run directly from the code in your cloned
repo. Now simply make whatever changes you want, commit the code, and push
your commit back to master.
If you think your changes are ready to be merged back to the main fpm repo, you
can generate a pull request on the github website for your repo and send it in
for review.
##Problems running bundle install?
If you are installing on Mac OS 10.9 (Mavericks) you will need to make sure that
you have the standalone command line tools seperate from Xcode:
$ xcode-select --install
Finally, click the install button on the prompt that appears.
## More Documentation
[See the wiki for more docs](https://github.com/jordansissel/fpm/wiki)
|
Deleted bin/fpm.
|
| < < < < < < < < |
Added bin/xpm.
> > > > > > > > | 1 2 3 4 5 6 7 8 | #!/usr/bin/env ruby require "rubygems" $: << File.join(File.dirname(__FILE__), "..", "lib") require "fpm" require "fpm/command" exit(FPM::Command.run || 0) |
Changes to examples/fpm-with-system-ruby/Makefile.
1 2 3 4 5 6 7 8 | # # feel free to change these to whatever makes sense # # debian package we rely on RUBY_PACKAGE=ruby # and the executable that comes from it RUBY_BIN=/usr/bin/ruby # the version we name the deb | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # # feel free to change these to whatever makes sense # # debian package we rely on RUBY_PACKAGE=ruby # and the executable that comes from it RUBY_BIN=/usr/bin/ruby # the version we name the deb VERSION=1.1.0 # where to get the sauce GIT_URL=https://github.com/jordansissel/fpm.git # the tag we checkout to build from TAG_SPEC=refs/tags/v$(VERSION) CHECKOUT_DIR=fpm-checkout BUILD_DIR=build |
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | rm -rf $(CHECKOUT_DIR) rm -rf $(BUILD_DIR) rm -f fpm*.deb $(CHECKOUT_DIR): rm -rf $(CHECKOUT_DIR) git clone $(GIT_URL) $(CHECKOUT_DIR) --depth 1 | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | rm -rf $(CHECKOUT_DIR) rm -rf $(BUILD_DIR) rm -f fpm*.deb $(CHECKOUT_DIR): rm -rf $(CHECKOUT_DIR) git clone $(GIT_URL) $(CHECKOUT_DIR) --depth 1 cd $(CHECKOUT_DIR) && git fetch && git checkout $(TAG_SPEC) $(BUNDLE_BIN): $(GEM_CMD) install bundler --install-dir=$(GEM_PATH) --no-ri --no-rdoc $(FPM_BIN): mkdir --parents $(BIN_DIR) # Couldn't think of a nice way to do this, so here is this code turd |
| ︙ | ︙ |
Deleted fpm.gemspec.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to lib/fpm/command.rb.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
# Lastly, include the default help output via Clamp.
super
].join("\n")
end # def help
option "-t", "OUTPUT_TYPE",
| | | > > > > > > > > > > > > > > > > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# Lastly, include the default help output via Clamp.
super
].join("\n")
end # def help
option "-t", "OUTPUT_TYPE",
"Package types you want to create (deb, rpm, solaris, etc)",
:attribute_name => :output_type
option "-s", "INPUT_TYPE",
"Input source or format (dir, gem, rpm, deb, python, etc)",
:attribute_name => :input_type
option ["-u", "--update"], "FILTER,LIST",
"Apply post-processing filters (man, appdata, desktop, etc.)",
:attribute_name => :update_filter
# option "--sign", "KEY",
# "Sign package (needs a keyname for Debian packages)",
# :attribute_name => :sign
option "-C", "CHDIR",
"Change directory to here before searching for files",
:attribute_name => :chdir
option "--prefix", "PREFIX",
"A path to prefix files with when building the target package. This may " \
"be necessary for all input packages. For example, the 'gem' type will " \
"prefix with your gem directory automatically."
option ["-p", "--package"], "OUTPUT", "The package file path to output."
option ["-f", "--force"], :flag, "Force output even if it will overwrite an " \
"existing file", :default => false
option ["-n", "--name"], "NAME", "The name to give to the package"
loglevels = %w(error warn info debug)
option "--log", "LEVEL", "Set the log level. Values: #{loglevels.join(", ")}.",
:attribute_name => :log_level do |val|
val.downcase.tap do |v|
if !loglevels.include?(v)
raise FPM::Package::InvalidArgument, "Invalid log level, #{v.inspect}. Must be one of: #{loglevels.join(", ")}"
end
end
end # --log
option "--verbose", :flag, "Enable verbose output"
option "--debug", :flag, "Enable debug output"
option "--debug-workspace", :flag, "Keep any file workspaces around for " \
"debugging. This will disable automatic cleanup of package staging and " \
"build paths. It will also print which directories are available."
option ["-v", "--version"], "VERSION", "The version to give to the package",
:default => 1.0
option "--iteration", "ITERATION",
"The iteration to give to the package. RPM calls this the 'release'. " \
"FreeBSD calls it 'PORTREVISION'. Debian calls this 'debian_revision'"
option "--epoch", "EPOCH",
"The epoch value for this package. RPM and Debian calls this 'epoch'. " \
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
"A dependency. This flag can be specified multiple times. Value is " \
"usually in the form of: -d 'name' or -d 'name > version'",
:multivalued => true, :attribute_name => :dependencies
option "--no-depends", :flag, "Do not list any dependencies in this package",
:default => false
| | | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
"A dependency. This flag can be specified multiple times. Value is " \
"usually in the form of: -d 'name' or -d 'name > version'",
:multivalued => true, :attribute_name => :dependencies
option "--no-depends", :flag, "Do not list any dependencies in this package",
:default => false
option "--no-auto-depends", :flag, "Do not list any dependencies in this " \
"package automatically", :default => false
option "--provides", "PROVIDES",
"What this package provides (usually a name). This flag can be " \
"specified multiple times.", :multivalued => true,
:attribute_name => :provides
option "--conflicts", "CONFLICTS",
"Other packages/versions this package conflicts with. This flag can " \
"specified multiple times.", :multivalued => true,
:attribute_name => :conflicts
option "--replaces", "REPLACES",
"Other packages/versions this package replaces. This flag can be " \
"specified multiple times.", :multivalued => true,
:attribute_name => :replaces
option "--config-files", "CONFIG_FILES",
"Mark a file in the package as being a config file. This uses 'conffiles'" \
" in debs and %config in rpm. If you have multiple files to mark as " \
"configuration files, specify this flag multiple times. If argument is " \
|
| ︙ | ︙ | |||
135 136 137 138 139 140 141 |
option "--url", "URI", "Add a url for this package.",
:default => "http://example.com/no-uri-given"
option "--inputs", "INPUTS_PATH",
"The path to a file containing a newline-separated list of " \
"files and dirs to use as input."
option "--post-install", "FILE",
| | | | | | | | | | > > > > > > > > > > > > > > > > > | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
option "--url", "URI", "Add a url for this package.",
:default => "http://example.com/no-uri-given"
option "--inputs", "INPUTS_PATH",
"The path to a file containing a newline-separated list of " \
"files and dirs to use as input."
option "--post-install", "FILE",
"(DEPRECATED, use --after-install) A script to be run after " \
"package installation" do |val|
@after_install = File.expand_path(val) # Get the full path to the script
end # --post-install (DEPRECATED)
option "--pre-install", "FILE",
"(DEPRECATED, use --before-install) A script to be run before " \
"package installation" do |val|
@before_install = File.expand_path(val) # Get the full path to the script
end # --pre-install (DEPRECATED)
option "--post-uninstall", "FILE",
"(DEPRECATED, use --after-remove) A script to be run after " \
"package removal" do |val|
@after_remove = File.expand_path(val) # Get the full path to the script
end # --post-uninstall (DEPRECATED)
option "--pre-uninstall", "FILE",
"(DEPRECATED, use --before-remove) A script to be run before " \
"package removal" do |val|
@before_remove = File.expand_path(val) # Get the full path to the script
end # --pre-uninstall (DEPRECATED)
option "--after-install", "FILE",
"A script to be run after package installation" do |val|
File.expand_path(val) # Get the full path to the script
end # --after-install
option "--before-install", "FILE",
"A script to be run before package installation" do |val|
File.expand_path(val) # Get the full path to the script
end # --before-install
option "--after-remove", "FILE",
"A script to be run after package removal" do |val|
File.expand_path(val) # Get the full path to the script
end # --after-remove
option "--before-remove", "FILE",
"A script to be run before package removal" do |val|
File.expand_path(val) # Get the full path to the script
end # --before-remove
option "--after-upgrade", "FILE",
"A script to be run after package upgrade. If not specified,\n" \
"--before-install, --after-install, --before-remove, and \n" \
"--after-remove wil behave in a backwards-compatible manner\n" \
"(they will not be upgrade-case aware).\n" \
"Currently only supports deb and rpm packages." do |val|
File.expand_path(val) # Get the full path to the script
end # --after-upgrade
option "--before-upgrade", "FILE",
"A script to be run before package upgrade. If not specified,\n" \
"--before-install, --after-install, --before-remove, and \n" \
"--after-remove wil behave in a backwards-compatible manner\n" \
"(they will not be upgrade-case aware).\n" \
"Currently only supports deb and rpm packages." do |val|
File.expand_path(val) # Get the full path to the script
end # --before-upgrade
option "--template-scripts", :flag,
"Allow scripts to be templated. This lets you use ERB to template your " \
"packaging scripts (for --after-install, etc). For example, you can do " \
"things like <%= name %> to get the package name. For more information, " \
"see the fpm wiki: " \
"https://github.com/jordansissel/fpm/wiki/Script-Templates"
option "--template-value", "KEY=VALUE",
"Make 'key' available in script templates, so <%= key %> given will be " \
"the provided value. Implies --template-scripts",
:multivalued => true do |kv|
@template_scripts = true
next kv.split("=", 2)
end
option "--workdir", "WORKDIR",
"The directory you want fpm to do its work in, where 'work' is any file " \
"copying, downloading, etc. Roughly any scratch space fpm needs to build " \
"your package.", :default => Dir.tmpdir
parameter "[ARGS] ...",
"Inputs to the source package type. For the 'dir' type, this is the files" \
" and directories you want to include in the package. For others, like " \
"'gem', it specifies the packages to download and use as the gem input",
:attribute_name => :args
|
| ︙ | ︙ | |||
220 221 222 223 224 225 226 |
def execute
# Short-circuit if someone simply runs `fpm --version`
if ARGV == [ "--version" ]
puts FPM::VERSION
return 0
end
| | > > > > > > | | | | | < < < < | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
def execute
# Short-circuit if someone simply runs `fpm --version`
if ARGV == [ "--version" ]
puts FPM::VERSION
return 0
end
logger.level = :warn
logger.level = :info if verbose? # --verbose
logger.level = :debug if debug? # --debug
if log_level
logger.level = log_level.to_sym
end
if (stray_flags = args.grep(/^-/); stray_flags.any?)
logger.warn("All flags should be before the first argument " \
"(stray flags found: #{stray_flags}")
end
# Some older behavior, if you specify:
# 'fpm -s dir -t ... -C somepath'
# fpm would assume you meant to add '.' to the end of the commandline.
# Let's hack that. https://github.com/jordansissel/fpm/issues/187
if input_type == "dir" and args.empty? and !chdir.nil?
logger.info("No args, but -s dir and -C are given, assuming '.' as input")
args << "."
end
logger.info("Setting workdir", :workdir => workdir)
ENV["TMP"] = workdir
validator = Validator.new(self)
if !validator.ok?
validator.messages.each do |message|
logger.warn(message)
end
logger.fatal("Fix the above problems, and you'll be rolling packages in no time!")
return 1
end
input_class = FPM::Package.types[input_type]
input = input_class.new
# Merge in package settings.
# The 'settings' stuff comes in from #apply_options, which goes through
# all the options defined in known packages and puts them into our command.
# Flags in packages defined as "--foo-bar" become named "--<packagetype>-foo-bar"
|
| ︙ | ︙ | |||
283 284 285 286 287 288 289 |
# If the instance variable @{attr} is defined, then
# it means the flag was given on the command line.
flag_given = instance_variable_defined?("@#{attr}")
input.attributes["#{attr}_given?".to_sym] = flag_given
attr = "#{attr}?" if !respond_to?(attr) # handle boolean :flag cases
input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
| | | | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# If the instance variable @{attr} is defined, then
# it means the flag was given on the command line.
flag_given = instance_variable_defined?("@#{attr}")
input.attributes["#{attr}_given?".to_sym] = flag_given
attr = "#{attr}?" if !respond_to?(attr) # handle boolean :flag cases
input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
logger.debug("Setting attribute", attr.to_sym => send(attr))
end
end
# Each remaining command line parameter is used as an 'input' argument.
# For directories, this means paths. For things like gem and python, this
# means package name or paths to the packages (rails, foo-1.0.gem, django,
# bar/setup.py, etc)
args.each do |arg|
input.input(arg)
end
# If --inputs was specified, read it as a file.
if !inputs.nil?
if !File.exists?(inputs)
logger.fatal("File given for --inputs does not exist (#{inputs})")
return 66 #EX_NOINPUT
end
# Read each line as a path
File.new(inputs, "r").each_line do |line|
# Handle each line as if it were an argument
input.input(line.strip)
end
end
# Override package settings if they are not the default flag values
# the below proc essentially does:
#
# if someflag != default_someflag
# input.someflag = someflag
# end
set = proc do |object, attribute|
# if the package's attribute is currently nil *or* the flag setting for this
# attribute is non-default, use the value.
if object.send(attribute).nil? || send(attribute) != send("default_#{attribute}")
logger.info("Setting from flags: #{attribute}=#{send(attribute)}")
object.send("#{attribute}=", send(attribute))
end
end
set.call(input, :architecture)
set.call(input, :category)
set.call(input, :description)
set.call(input, :epoch)
|
| ︙ | ︙ | |||
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
input.conflicts += conflicts
input.dependencies += dependencies
input.provides += provides
input.replaces += replaces
input.config_files += config_files
input.directories += directories
script_errors = []
setscript = proc do |scriptname|
# 'self.send(scriptname) == self.before_install == --before-install
# Gets the path to the script
path = self.send(scriptname)
# Skip scripts not set
next if path.nil?
if !File.exists?(path)
| > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | > > > > > > > > > > > > > > | | > > < | | > > > | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
input.conflicts += conflicts
input.dependencies += dependencies
input.provides += provides
input.replaces += replaces
input.config_files += config_files
input.directories += directories
#-- not sure what this was meant for, attrs was a hash already
#h = {}
#attrs.each do | e |
#
# s = e.split(':', 2)
# h[s.last] = s.first
#end
#input.attrs = h
script_errors = []
setscript = proc do |scriptname|
# 'self.send(scriptname) == self.before_install == --before-install
# Gets the path to the script
path = self.send(scriptname)
# Skip scripts not set
next if path.nil?
if !File.exists?(path)
logger.error("No such file (for #{scriptname.to_s}): #{path.inspect}")
script_errors << path
end
# Load the script into memory.
input.scripts[scriptname] = File.read(path)
end
setscript.call(:before_install)
setscript.call(:after_install)
setscript.call(:before_remove)
setscript.call(:after_remove)
setscript.call(:before_upgrade)
setscript.call(:after_upgrade)
# Bail if any setscript calls had errors. We don't need to log
# anything because we've already logged the error(s) above.
return 1 if script_errors.any?
# Validate the package
if input.name.nil? or input.name.empty?
logger.fatal("No name given for this package (set name with '-n', " \
"for example, '-n packagename')")
return 1
end
# Apply update filters on staging dir, prior to packaging
unless update_filter.nil?
update_filter.split(/[\s,;+]+/).each do |filter_type|
opts = []
if filter_type =~ /(\w+)[=:]+(.+)/
filter_type, opts = [$1, $2.split(/\W+/)]
end
if not FPM::Package.types.include?("filter_#{filter_type}")
logger.warn("Unknown -u update filter '#{filter_type}'")
next
end
filter = input.convert(FPM::Package.types["filter_#{filter_type}"])
filter.update(opts)
end
end
# Traverse output types (-t deb,rpm,pkg,exe).
# Scope output here, so it's available for ensure block.
output = nil
output_type.split(/[\s,;+]+/).uniq.each do |output_type|
# Convert to the output type
output_class = FPM::Package.types[output_type]
output = input.convert(output_class)
# Provide any template values as methods on the package.
if template_scripts?
template_value_list.each do |key, value|
(class << output; self; end).send(:define_method, key) { value }
end
end
# Write the output somewhere, package can be nil if no --package is specified,
# and that's OK.
# If the package output (-p flag) is a directory, write to the default file name
# but inside that directory.
if ! package.nil? && File.directory?(package)
package_file = File.join(package, output.to_s)
else
package_file = output.to_s(package)
end
begin
output.output(package_file)
rescue FPM::Package::FileAlreadyExists => e
logger.fatal(e.message)
return 77 #EX_PERM
rescue FPM::Package::ParentDirectoryMissing => e
logger.fatal(e.message)
return 73 #EX_CANTCREAT
end
logger.log("Created package", :path => package_file)
end # each output_type
rescue FPM::Util::ExecutableNotFound => e
logger.error("Need executable '#{e}' to convert #{input_type} to #{output_type}")
return 69 #EX_UNAVAILABLE
rescue FPM::InvalidPackageConfiguration => e
logger.error("Invalid package configuration: #{e}")
return 78 #EX_CONFIG
rescue FPM::Util::ProcessFailed => e
logger.error("Process failed: #{e}")
return 74 #EX_IOERR
rescue => e
logger.fatal("Error: #{e}\n#{e.backtrace}")
return 70 #EX_SOFTWARE
ensure
if debug_workspace?
# only emit them if they have files
[input, output].each do |plugin|
next if plugin.nil?
[:staging_path, :build_path].each do |pathtype|
path = plugin.send(pathtype)
next unless Dir.open(path).to_a.size > 2
logger.log("plugin directory", :plugin => plugin.type, :pathtype => pathtype, :path => path)
end
end
else
input.cleanup unless input.nil?
output.cleanup unless output.nil?
end
return 0 #EX_OK
end # def execute
def run(*args)
logger.subscribe(STDOUT)
# fpm initialization files, note the order of the following array is
# important, try .fpm in users home directory first and then the current
# directory
rc_files = [ ".fpm" ]
rc_files << File.join(ENV["HOME"], ".fpm") if ENV["HOME"]
rc_files.each do |rc_file|
if File.readable? rc_file
logger.warn("Loading flags from rc file #{rc_file}")
File.readlines(rc_file).each do |line|
# reverse becasue 'unshift' pushes onto the left side of the array.
Shellwords.shellsplit(line).reverse.each do |arg|
# Put '.fpm'-file flags *before* the command line flags
# so that we the CLI can override the .fpm flags
ARGV.unshift(arg)
end
end
end
end
super(*args)
rescue FPM::Package::InvalidArgument => e
logger.error("Invalid package argument: #{e}")
return 1
end # def run
# A simple flag validator
#
# The goal of this class is to ensure the flags and arguments given
# are a valid configuration.
class Validator
|
| ︙ | ︙ | |||
491 492 493 494 495 496 497 |
mandatory(FPM::Package.types.include?(val),
"Invalid input package -s flag) type #{val.inspect}. " \
"Expected one of: #{types.join(", ")}")
end
with(@command.output_type) do |val|
next if val.nil?
| > | > | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
mandatory(FPM::Package.types.include?(val),
"Invalid input package -s flag) type #{val.inspect}. " \
"Expected one of: #{types.join(", ")}")
end
with(@command.output_type) do |val|
next if val.nil?
val.split(/[\s,;+]+/).each do |val|
mandatory(FPM::Package.types.include?(val),
"Invalid output package (-t flag) type #{val.inspect}. " \
"Expected one of: #{types.join(", ")}")
end
end
with (@command.dependencies) do |dependencies|
# Verify dependencies don't include commas (#257)
dependencies.each do |dep|
next unless dep.include?(",")
splitdeps = dep.split(/\s*,\s*/)
|
| ︙ | ︙ |
Changes to lib/fpm/package.rb.
| ︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 | attr_accessor :directories # Any other attributes specific to this package. # This is where you'd put rpm, deb, or other specific attributes. attr_accessor :attributes private def initialize | > > < < | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
attr_accessor :directories
# Any other attributes specific to this package.
# This is where you'd put rpm, deb, or other specific attributes.
attr_accessor :attributes
attr_accessor :attrs
private
def initialize
# Attributes for this specific package
@attributes = {}
# Reference
# http://www.debian.org/doc/manuals/maint-guide/first.en.html
# http://wiki.debian.org/DeveloperConfiguration
# https://github.com/jordansissel/fpm/issues/37
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
@provides = []
@conflicts = []
@replaces = []
@dependencies = []
@scripts = {}
@config_files = []
@directories = []
staging_path
build_path
end # def initialize
# Get the 'type' for this instance.
#
# For FPM::Package::ABC, this returns 'abc'
def type
self.class.type
end # def type
# Convert this package to a new package type
def convert(klass)
| > | | | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
@provides = []
@conflicts = []
@replaces = []
@dependencies = []
@scripts = {}
@config_files = []
@directories = []
@attrs = {}
staging_path
build_path
end # def initialize
# Get the 'type' for this instance.
#
# For FPM::Package::ABC, this returns 'abc'
def type
self.class.type
end # def type
# Convert this package to a new package type
def convert(klass)
logger.info("Converting #{self.type} to #{klass.type}")
exclude
pkg = klass.new
pkg.cleanup_staging # purge any directories that may have been created by klass.new
# copy other bits
ivars = [
:@architecture, :@category, :@config_files, :@conflicts,
:@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
:@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
:@directories, :@staging_path, :@attrs
]
ivars.each do |ivar|
#logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
#:from => self.type, :to => pkg.type)
pkg.instance_variable_set(ivar, instance_variable_get(ivar))
end
# Attributes are special! We do not want to remove the default values of
# the destination package type unless their value is specified on the
# source package object.
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 |
else
return File.join(@build_path, path)
end
end # def build_path
# Clean up any temporary storage used by this class.
def cleanup
| | | | | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
else
return File.join(@build_path, path)
end
end # def build_path
# Clean up any temporary storage used by this class.
def cleanup
cleanup_staging unless logger.level == :debug
cleanup_build unless logger.level == :debug
end # def cleanup
def cleanup_staging
if File.directory?(staging_path)
logger.debug("Cleaning up staging path", :path => staging_path)
FileUtils.rm_r(staging_path)
end
end # def cleanup_staging
def cleanup_build
if File.directory?(build_path)
logger.debug("Cleaning up build path", :path => build_path)
FileUtils.rm_r(build_path)
end
end # def cleanup_build
# List all files in the staging_path
#
# The paths will all be relative to staging_path and will not include that
|
| ︙ | ︙ | |||
320 321 322 323 324 325 326 |
def template_dir
File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
end
def template(path)
template_path = File.join(template_dir, path)
template_code = File.read(template_path)
| | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
def template_dir
File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
end
def template(path)
template_path = File.join(template_dir, path)
template_code = File.read(template_path)
logger.info("Reading template", :path => template_path)
erb = ERB.new(template_code, nil, "-")
erb.filename = template_path
return erb
end # def template
def to_s(fmt="NAME.TYPE")
fmt = "NAME.TYPE" if fmt.nil?
fullversion = version.to_s
fullversion += "-#{iteration}" if iteration
return fmt.gsub("ARCH", architecture.to_s) \
.gsub("NAME", name.to_s) \
.gsub("FULLVERSION", fullversion) \
.gsub("VERSION", version.to_s) \
.gsub("ITERATION", iteration.to_s) \
.gsub("EPOCH", epoch.to_s) \
.gsub("TYPE", type.to_s)
end # def to_s
def edit_file(path)
editor = ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi'
logger.info("Launching editor", :file => path)
command = "#{editor} #{Shellwords.escape(path)}"
system("#{editor} #{Shellwords.escape(path)}")
if !$?.success?
raise ProcessFailed.new("'#{editor}' failed (exit code " \
"#{$?.exitstatus}) Full command was: "\
"#{command}");
end
|
| ︙ | ︙ | |||
370 371 372 373 374 375 376 |
installdir = staging_path
end
Find.find(installdir) do |path|
match_path = path.sub("#{installdir.chomp('/')}/", '')
attributes[:excludes].each do |wildcard|
| | | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
installdir = staging_path
end
Find.find(installdir) do |path|
match_path = path.sub("#{installdir.chomp('/')}/", '')
attributes[:excludes].each do |wildcard|
logger.debug("Checking path against wildcard", :path => match_path, :wildcard => wildcard)
if File.fnmatch(wildcard, match_path)
logger.info("Removing excluded path", :path => match_path, :matches => wildcard)
FileUtils.remove_entry_secure(path)
Find.prune
break
end
end
end
end # def exclude
|
| ︙ | ︙ | |||
493 494 495 496 497 498 499 |
def output_check(output_path)
if !File.directory?(File.dirname(output_path))
raise ParentDirectoryMissing.new(output_path)
end
if File.file?(output_path)
if attributes[:force?]
| | | 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
def output_check(output_path)
if !File.directory?(File.dirname(output_path))
raise ParentDirectoryMissing.new(output_path)
end
if File.file?(output_path)
if attributes[:force?]
logger.warn("Force flag given. Overwriting package at #{output_path}")
File.delete(output_path)
else
raise FileAlreadyExists.new(output_path)
end
end
end # def output_path
|
| ︙ | ︙ | |||
515 516 517 518 519 520 521 |
# General public API
public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files,
:version, :script, :provides=)
# Package internal public api
public(:cleanup_staging, :cleanup_build, :staging_path, :converted_from,
| | | 516 517 518 519 520 521 522 523 524 |
# General public API
public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files,
:version, :script, :provides=)
# Package internal public api
public(:cleanup_staging, :cleanup_build, :staging_path, :converted_from,
:edit_file, :build_path)
end # class FPM::Package
|
Added lib/fpm/package/composer.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
#
# encoding: utf-8
# api: fpm
# title: Composer source
# description: Converts composer bundles into system or/and phar packages
# type: package
# depends: bin:composer
# category: source
# version: 0.5
# state: beta
# license: MITL
# author: mario#include-once:org
#
# Creates system packages for composer/packagist bundles.
#
# → Either works on individual vendor/*/*/ paths as input
# (the vendor/ prefix can be omitted).
#
# → Or downloads a single vnd/pkgname bundle.
#
# Also supports different target variations:
#
# → Syspackages (deb/rpm) end up in /usr/share/php/Vnd/Pkg.
#
# → Whereas --composer-phar creates Phars embedded into system
# packages, with target names of /usr/share/php/vnd-pkg.phar.
#
# → While a standard `-t phar` target will just compact individual
# components into localized phars.
#
# NOTES
#
# → Currently rewritten to conform to Debian pkg-php-tools and Fedora
# schemes.
# → The vendor/ extraction prefix isn't retained any longer. Composer wasn't
# meant to manage system-globally installed libraries.
# → System packages thus need a global autoloader (shared.phar / phpab)
# or manual includes.
# → The build process utilizes `composer require` to fetch new packages,
# if xpm -s composer isn't run from within a composer managed project.
# → Bring in line with Debian packaging scheme, dh_phpcomposer/pkg-php
# dropped -composer- in package names, got rid of nested /vendor/deep/dirs/
# yet to adopt complete version/dependency translation after all?
# ø Dependencies are neither in line with Fedora/RPM version expressions,
# http://fedoraproject.org/wiki/Packaging:PHP
# https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning
# ø Unclear: require/use `-u composer` for reading meta? Currently just
# composer.lock is scanned for input.
#
require "fpm/package"
require "fpm/util"
require "fpm/errors"
require "fpm/package/zip"
require "fileutils"
require "json"
# Composer reading (source only)
class FPM::Package::Composer < FPM::Package
option "--ver", "1.0\@dev", "Which version to checkout", :default=>nil
option "--phar", :flag, "Convert bundle into .phar plugin package", :default=>false
public
attr_accessor :in_bundle
def initialize(*args)
super(*args)
@architecture = "all"
@name_prefix = "" # hold "php-" or "phar-" syspackage name prefix
@target_dir = nil # two-level base directory under /usr/share/php
@in_bundle = "n/a" # packagist bundle name
@once = false # prevent double .input() invocation
end
# download composer bundle, or compact from existing vendor/ checkout
def input(vnd_pkg_path)
# general params
in_bundle = vnd_pkg_path.gsub(/^(.+\/+)*vendor\/+|\/(?=\/)|\/+$/, "")
@name = in_bundle.gsub(/[\W]+/, "-")
json = {}
if @once
@once = true
raise FPM::InvalidPackageConfiguration, "You can't input multiple bundle names. Only one package can be built at a time currently. Use a shell loop please."
elsif in_bundle =~ /^composer\/\w+\.\w+/
raise FPM::InvalidPackageConfiguration, "composer/*.* files specified as input. Supply only one bundle id."
end
# copying mode
if File.exist?("vendor/" + in_bundle)
json = parse_lock("composer.lock", in_bundle)[in_bundle]
# localize contents below vendor/*/*/ input directory
::Dir.chdir("./vendor/#{in_bundle}/#{json['target-dir']}/") do
FileUtils.cp_r(glob("./*"), build_path)
end
else
# download one package (and dependencies, which are thrown away afterwards)
::Dir.chdir(staging_path) do
ver = attributes[:composer_ver]
safesystem(
composer, "require", "--prefer-dist", "--update-no-dev", "--ignore-platform-reqs",
"--no-ansi", "--no-interaction", in_bundle, *(ver ? [ver] : [])
)
# localize Vnd/Pkg folder
json = parse_lock("composer.lock", in_bundle)[in_bundle]
FileUtils.mv(glob("./vendor/#{in_bundle}/#{json['target-dir']}/*"), build_path)
FileUtils.rm_r(glob("#{staging_path}/*"))
end
end
#-- staging
# At this point the build_path contains just the actual class files, etc.
# Conversion to sys/phar/sysphar is handled in convert() along with the
# dependency translation.
composer_json_import(json)
@target_dir = json.include?("target-dir") ? json["target-dir"] : in_bundle
attributes[:phar_format] = "zip+gz" unless attributes[:phar_format_given?]
end # def output
def convert(klass)
pkg = super(klass)
# becomes local -t phar
if klass == FPM::Package::Phar
pkg.instance_variable_set(:@staging_path, build_path)
@name_prefix = "" # needs to be reset in case of multi-target building
# prepare matroska phar-in-deb/rpm, ends up in /usr/share/php/*.phar
elsif attributes[:composer_phar_given?]
FileUtils.mkdir_p(staging_dest = "#{staging_path}/usr/share/php")
::Dir.chdir("#{build_path}") do
phar = convert(FPM::Package::Phar) # will loop internally
phar.output("#{staging_dest}/#{@name}.phar");
end
@name_prefix = "phar-"
# system package (deb/rpm) with plain files under /usr/share/php/Vnd/Pkg
else
dest = "/usr/share/php/#{@target_dir}"
FileUtils.mkdir_p(dest = "#{pkg.staging_path}/#{dest}")
FileUtils.cp_r(glob("#{build_path}/*"), dest)
@name_prefix = "php-"
end
# add dependencies
pkg.name = "#{@name_prefix}#{@name}"
pkg.dependencies += @cdeps.collect { |k,v| require_convert(k, v, @name_prefix, klass) }.flatten.compact
return pkg
end # def convert
# translate package names and versions
def require_convert(k, v, prefix, klass)
# package type/name maps
map = { FPM::Package::RPM => :rpm, FPM::Package::Deb => :deb, FPM::Package::Phar => :phar }
typ = map.include?(klass) ? map[klass] : :deb
pn = {
:php => { :phar => "php", :deb => "php5-common", :rpm => "php(language)" },
:ext => { :phar => "ext:", :deb => "php5-", :rpm => "php-" },
:lib => { :phar => "sys:lib", :deb => "lib", :rpm => "lib" },
:bin => { :phar => "bin:", :deb => "", :rpm => "/usr/bin/" }
}
# package names, magic values
case k = k.strip.gsub(/\W+/, "-")
when /^php(-32bit|-64bit)?|^hhvm|^quercus/
k = pn[:php][typ]
@architecture = ($1 == "-32bit") ? "x86" : "amd64" if $1
when /^(ext|lib|bin)-(\w+)$/
k = pn[$1.to_sym][typ] + $2
else
k = prefix + k
end
# expand version specifiers (this is intentionally incomplete)
if attributes[:no_depends_given?]
v = ""
else
v = v.split(",").map {
|v|
case v = ver(v, typ)
when "*"
""
when /^[\d.-]+~*$/ # 1.0.1
" = #{v}"
when /^((\d+\.)*(\d+))\.\*/ # 1.0.*
[" >= #{$1}.0", " <= #{$1}.999"]
when /^([!><=]*)([\d.-]+~*)$/ # >= 2.0 # debianize_op() normalizes >, <, = anyway
" #{$1} #{$2}"
when /^~\s*([\d.-]+~*)$/ # ~2.0 # deb.fix_dependency translates that into a range ["pkg(>=1)", "pkg(<<2)"]
" ~> #{$1}"
else
""
end
}
end
return k ? v.flatten.map { |v| k + v } : nil
end
# normalize version strings to packaging system
def ver(v, typ)
v.gsub!(/ (?:^.+ \sAS\s (.+$))? | \s+() | ^v() /nix, "\\1")
case typ
when :deb
v.gsub!(/[-@](dev|patch).*$/, "~~")
v.gsub!(/[-@](alpha|beta|RC|stable).*$/, "~")
when :rpm
v.gsub!(/[-@](dev|patch|alpha|beta|RC|stable).*$/, "")
else
v.gsub!(/@/, "-")
end
return v
end
# collect per-package composer.json infos
def composer_json_import(json)
if json.key? "name" and not attributes[:vendor_given?]
@vendor = json["name"].split(/\W/).first
end
if json.key? "version" and not attributes[:version_given?]
@version = json["version"].sub(/^v/, "")
end
if json.key? "description" and not attributes[:description_given?]
@description = json["description"]
end
if json.key? "license" and not attributes[:license_given?]
@license = [json["license"]].flatten.join(", ")
end
if json.key? "homepage" and not attributes[:url_given?]
@url = json["homepage"]
end
if json.key? "authors" and not attributes[:maintainer_given?]
@maintainer = json["authors"].map{ |v| v.values.join(", ") }.first or nil
end
if json.key? "require" and dependencies.empty?
@cdeps = json["require"]
end
# stash away complete composer struct for possible phar building
@attrs[:composer] = json
end
# Extract package sections from composer.lock file, turn into pkgname→hash
def parse_lock(fn, in_bundle)
json = JSON.parse(File.read(fn))
if !json.key? "packages"
json["packages"] = []
end
if json.key? "packages-dev"
json["packages"] += json["packages-dev"]
end
lock = Hash[ json["packages"].map{ |entry| [entry["name"], entry] } ]
unless lock.key? in_bundle
raise FPM::InvalidPackageConfiguration, "Package name '#{in_bundle}' absent in composer.lock"
end
return lock
end
# Add composer.lock package date into per-package composer.json→extra→lock
def inject_lock(fn, extra)
json = JSON.parse(File.read(fn))
json["extra"] ||= {}
json["extra"]["lock"] = extra
File.write(fn, JSON.pretty_generate(json))
end
# Locate composer binary
def composer
(`which composer` or `which composer.phar` or ("php "+`locate composer.phar`)).split("\n").first
end
def glob(path)
::Dir.glob(path)
end
end # class ::Composer
|
Changes to lib/fpm/package/cpan.rb.
| ︙ | ︙ | |||
69 70 71 72 73 74 75 |
self.license = case metadata["license"]
when Array; metadata["license"].first
when nil; "unknown"
else; metadata["license"]
end
unless metadata["distribution"].nil?
| | | > > | | > > > > | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
self.license = case metadata["license"]
when Array; metadata["license"].first
when nil; "unknown"
else; metadata["license"]
end
unless metadata["distribution"].nil?
logger.info("Setting package name from 'distribution'",
:distribution => metadata["distribution"])
self.name = fix_name(metadata["distribution"])
else
logger.info("Setting package name from 'name'",
:name => metadata["name"])
self.name = fix_name(metadata["name"])
end
# author is not always set or it may be a string instead of an array
self.vendor = case metadata["author"]
when String; metadata["author"]
when Array; metadata["author"].join(", ")
else
raise FPM::InvalidPackageConfiguration, "Unexpected CPAN 'author' field type: #{metadata["author"].class}. This is a bug."
end if metadata.include?("author")
self.url = metadata["resources"]["homepage"] rescue "unknown"
# TODO(sissel): figure out if this perl module compiles anything
# and set the architecture appropriately.
self.architecture = "all"
# Install any build/configure dependencies with cpanm.
# We'll install to a temporary directory.
logger.info("Installing any build or configure dependencies")
cpanm_flags = ["-L", build_path("cpan"), moduledir]
cpanm_flags += ["-n"] if attributes[:cpan_test?]
cpanm_flags += ["--mirror", "#{attributes[:cpan_mirror]}"] if !attributes[:cpan_mirror].nil?
cpanm_flags += ["--mirror-only"] if attributes[:cpan_mirror_only?] && !attributes[:cpan_mirror].nil?
safesystem(attributes[:cpan_cpanm_bin], *cpanm_flags)
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
end #no_auto_depends
::Dir.chdir(moduledir) do
# TODO(sissel): install build and config dependencies to resolve
# build/configure requirements.
# META.yml calls it 'configure_requires' and 'build_requires'
# META.json calls it prereqs/build and prereqs/configure
| | < < < < < < < < < < < < < < < < < < < < < < < | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
end #no_auto_depends
::Dir.chdir(moduledir) do
# TODO(sissel): install build and config dependencies to resolve
# build/configure requirements.
# META.yml calls it 'configure_requires' and 'build_requires'
# META.json calls it prereqs/build and prereqs/configure
prefix = attributes[:prefix] || "/usr/local"
# TODO(sissel): Set default INSTALL path?
# Try Makefile.PL, Build.PL
#
if File.exists?("Build.PL")
# Module::Build is in use here; different actions required.
safesystem(attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Build.PL")
safesystem(attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"./Build")
|
| ︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
--destdir #{staging_path} --prefix #{prefix} --destdir #{staging_path}")
else
safesystem("./Build", "install",
"--prefix", prefix, "--destdir", staging_path,
# Empty install_base to avoid local::lib being used.
"--install_base", "")
end
else
raise FPM::InvalidPackageConfiguration,
"I don't know how to build #{name}. No Makefile.PL nor " \
"Build.PL found"
end
# Fix any files likely to cause conflicts that are duplicated
# across packages.
# https://github.com/jordansissel/fpm/issues/443
# https://github.com/jordansissel/fpm/issues/510
| > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
--destdir #{staging_path} --prefix #{prefix} --destdir #{staging_path}")
else
safesystem("./Build", "install",
"--prefix", prefix, "--destdir", staging_path,
# Empty install_base to avoid local::lib being used.
"--install_base", "")
end
elsif File.exists?("Makefile.PL")
if attributes[:cpan_perl_lib_path]
perl_lib_path = attributes[:cpan_perl_lib_path]
safesystem(attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Makefile.PL", "PREFIX=#{prefix}", "LIB=#{perl_lib_path}",
# Empty install_base to avoid local::lib being used.
"INSTALL_BASE=")
else
safesystem(attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Makefile.PL", "PREFIX=#{prefix}",
# Empty install_base to avoid local::lib being used.
"INSTALL_BASE=")
end
if attributes[:cpan_test?]
make = [ "env", "PERL5LIB=#{build_path("cpan/lib/perl5")}", "make" ]
else
make = [ "make" ]
end
safesystem(*make)
safesystem(*(make + ["test"])) if attributes[:cpan_test?]
safesystem(*(make + ["DESTDIR=#{staging_path}", "install"]))
else
raise FPM::InvalidPackageConfiguration,
"I don't know how to build #{name}. No Makefile.PL nor " \
"Build.PL found"
end
# Fix any files likely to cause conflicts that are duplicated
# across packages.
# https://github.com/jordansissel/fpm/issues/443
# https://github.com/jordansissel/fpm/issues/510
glob_prefix = attributes[:cpan_perl_lib_path] || prefix
::Dir.glob(File.join(staging_path, glob_prefix, "**/perllocal.pod")).each do |path|
logger.debug("Removing useless file.",
:path => path.gsub(staging_path, ""))
File.unlink(path)
end
end
# TODO(sissel): figure out if this perl module compiles anything
# and set the architecture appropriately.
self.architecture = "all"
# Find any shared objects in the staging directory to set architecture as
# native if found; otherwise keep the 'all' default.
Find.find(staging_path) do |path|
if path =~ /\.so$/
logger.info("Found shared library, setting architecture=native",
:path => path)
self.architecture = "native"
end
end
end
def unpack(tarball)
directory = build_path("module")
::Dir.mkdir(directory)
args = [ "-C", directory, "-zxf", tarball,
"--strip-components", "1" ]
safesystem("tar", *args)
return directory
end
def download(metadata, cpan_version=nil)
distribution = metadata["distribution"]
author = metadata["author"]
logger.info("Downloading perl module",
:distribution => distribution,
:version => cpan_version)
# default to latest versionunless we specify one
if cpan_version.nil?
self.version = metadata["version"]
else
if metadata["version"] =~ /^v\d/
self.version = "v#{cpan_version}"
else
self.version = cpan_version
end
end
metacpan_release_url = "http://api.metacpan.org/v0/release/#{author}/#{distribution}-#{self.version}"
begin
release_response = httpfetch(metacpan_release_url)
rescue Net::HTTPServerException => e
logger.error("metacpan release query failed.", :error => e.message,
:module => package, :url => metacpan_release_url)
raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
end
data = release_response.body
release_metadata = JSON.parse(data)
archive = release_metadata["archive"]
# should probably be basepathed from the url
tarball = File.basename(archive)
url_base = "http://www.cpan.org/"
url_base = "#{attributes[:cpan_mirror]}" if !attributes[:cpan_mirror].nil?
#url = "http://www.cpan.org/CPAN/authors/id/#{author[0,1]}/#{author[0,2]}/#{author}/#{tarball}"
url = "#{url_base}/authors/id/#{author[0,1]}/#{author[0,2]}/#{author}/#{archive}"
logger.debug("Fetching perl module", :url => url)
begin
response = httpfetch(url)
rescue Net::HTTPServerException => e
#logger.error("Download failed", :error => response.status_line,
#:url => url)
logger.error("Download failed", :error => e, :url => url)
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
end
File.open(build_path(tarball), "w") do |fd|
#response.read_body { |c| fd.write(c) }
fd.write(response.body)
end
return build_path(tarball)
end # def download
def search(package)
logger.info("Asking metacpan about a module", :module => package)
metacpan_url = "http://api.metacpan.org/v0/module/" + package
begin
response = httpfetch(metacpan_url)
rescue Net::HTTPServerException => e
#logger.error("metacpan query failed.", :error => response.status_line,
#:module => package, :url => metacpan_url)
logger.error("metacpan query failed.", :error => e.message,
:module => package, :url => metacpan_url)
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
end
#data = ""
#response.read_body { |c| p c; data << c }
data = response.body
|
| ︙ | ︙ |
Changes to lib/fpm/package/deb.rb.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
if !COMPRESSION_TYPES.include?(value)
raise ArgumentError, "deb compression value of '#{value}' is invalid. " \
"Must be one of #{COMPRESSION_TYPES.join(", ")}"
end
value
end
# Take care about the case when we want custom control file but still use fpm ...
option "--custom-control", "FILEPATH",
"Custom version of the Debian control file." do |control|
File.expand_path(control)
end
# Add custom debconf config file
| > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
if !COMPRESSION_TYPES.include?(value)
raise ArgumentError, "deb compression value of '#{value}' is invalid. " \
"Must be one of #{COMPRESSION_TYPES.join(", ")}"
end
value
end
option "--sign", "KEY",
"Sign the resulting package with named GPG key."
# Take care about the case when we want custom control file but still use fpm ...
option "--custom-control", "FILEPATH",
"Custom version of the Debian control file." do |control|
File.expand_path(control)
end
# Add custom debconf config file
|
| ︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
end
option "--suggests", "PACKAGE", "Add PACKAGE to Suggests" do |pkg|
@suggests ||= []
@suggests << pkg
next @suggests
end
option "--field", "'FIELD: VALUE'", "Add custom field to the control file" do |fv|
@custom_fields ||= {}
field, value = fv.split(/: */, 2)
@custom_fields[field] = value
next @custom_fields
end
| > > > > > > > > > > > > > > > > > > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
end
option "--suggests", "PACKAGE", "Add PACKAGE to Suggests" do |pkg|
@suggests ||= []
@suggests << pkg
next @suggests
end
option "--meta-file", "FILEPATH", "Add FILEPATH to DEBIAN directory" do |file|
@meta_files ||= []
@meta_files << File.expand_path(file)
next @meta_files
end
option "--interest", "EVENT", "Package is interested in EVENT trigger" do |event|
@interested_triggers ||= []
@interested_triggers << event
next @interested_triggers
end
option "--activate", "EVENT", "Package activates EVENT trigger" do |event|
@activated_triggers ||= []
@activated_triggers << event
next @activated_triggers
end
option "--field", "'FIELD: VALUE'", "Add custom field to the control file" do |fv|
@custom_fields ||= {}
field, value = fv.split(/: */, 2)
@custom_fields[field] = value
next @custom_fields
end
|
| ︙ | ︙ | |||
164 165 166 167 168 169 170 |
# Get the name of this package. See also FPM::Package#name
#
# This accessor actually modifies the name if it has some invalid or unwise
# characters.
def name
if @name =~ /[A-Z]/
| | | > > > > > > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# Get the name of this package. See also FPM::Package#name
#
# This accessor actually modifies the name if it has some invalid or unwise
# characters.
def name
if @name =~ /[A-Z]/
logger.warn("Debian tools (dpkg/apt) don't do well with packages " \
"that use capital letters in the name. In some cases it will " \
"automatically downcase them, in others it will not. It is confusing." \
" Best to not use any capital letters at all. I have downcased the " \
"package name for you just to be safe.",
:oldname => @name, :fixedname => @name.downcase)
@name = @name.downcase
end
if @name.include?("_")
logger.info("Debian package names cannot include underscores; " \
"automatically converting to dashes", :name => @name)
@name = @name.gsub(/[_]/, "-")
end
if @name.include?(" ")
logger.info("Debian package names cannot include spaces; " \
"automatically converting to dashes", :name => @name)
@name = @name.gsub(/[ ]/, "-")
end
return @name
end # def name
def prefix
return (attributes[:prefix] or "/")
end # def prefix
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
control = File.read(File.join(path, "control"))
parse = lambda do |field|
value = control[/^#{field.capitalize}: .*/]
if value.nil?
return nil
else
| | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
control = File.read(File.join(path, "control"))
parse = lambda do |field|
value = control[/^#{field.capitalize}: .*/]
if value.nil?
return nil
else
logger.info("deb field", field => value.split(": ", 2).last)
return value.split(": ",2).last
end
end
# Parse 'epoch:version-iteration' in the version string
version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
m = version_re.match(parse.call("Version"))
|
| ︙ | ︙ | |||
308 309 310 311 312 313 314 |
# Abort if the target path already exists.
# create 'debian-binary' file, required to make a valid debian package
File.write(build_path("debian-binary"), "2.0\n")
# If we are given --deb-shlibs but no --after-install script, we
# should implicitly create a before/after scripts that run ldconfig
| | | | > > > > > > > > > > > > > > > | | | > > | > | > > > > > | > | > > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# Abort if the target path already exists.
# create 'debian-binary' file, required to make a valid debian package
File.write(build_path("debian-binary"), "2.0\n")
# If we are given --deb-shlibs but no --after-install script, we
# should implicitly create a before/after scripts that run ldconfig
if attributes[:deb_shlibs]
if !script?(:after_install)
logger.info("You gave --deb-shlibs but no --after-install, so " \
"I am adding an after-install script that runs " \
"ldconfig to update the system library cache")
scripts[:after_install] = template("deb/ldconfig.sh.erb").result(binding)
end
if !script?(:after_remove)
logger.info("You gave --deb-shlibs but no --after-remove, so " \
"I am adding an after-remove script that runs " \
"ldconfig to update the system library cache")
scripts[:after_remove] = template("deb/ldconfig.sh.erb").result(binding)
end
end
if script?(:before_upgrade) or script?(:after_upgrade)
if script?(:before_install) or script?(:before_upgrade)
scripts[:before_install] = template("deb/preinst_upgrade.sh.erb").result(binding)
end
if script?(:before_remove)
scripts[:before_remove] = template("deb/prerm_upgrade.sh.erb").result(binding)
end
if script?(:after_install) or script?(:after_upgrade)
scripts[:after_install] = template("deb/postinst_upgrade.sh.erb").result(binding)
end
if script?(:after_remove)
scripts[:after_remove] = template("deb/postrm_upgrade.sh.erb").result(binding)
end
end
write_control_tarball
# Tar up the staging_path into data.tar.{compression type}
case self.attributes[:deb_compression]
when "gz", nil
datatar = build_path("data.tar.gz")
compression = "-z"
when "bzip2"
datatar = build_path("data.tar.bz2")
compression = "-j"
when "xz"
datatar = build_path("data.tar.xz")
compression = "-J"
else
raise FPM::InvalidPackageConfiguration,
"Unknown compression type '#{self.attributes[:deb_compression]}'"
end
# Write the changelog file
dest_changelog = File.join(staging_path, "usr/share/doc/#{name}/changelog.Debian.gz")
FileUtils.mkdir_p(File.dirname(dest_changelog))
File.new(dest_changelog, "wb", 0644).tap do |changelog|
Zlib::GzipWriter.new(changelog, Zlib::BEST_COMPRESSION).tap do |changelog_gz|
if attributes[:deb_changelog]
logger.info("Writing user-specified changelog", :source => attributes[:deb_changelog])
File.new(attributes[:deb_changelog]).tap do |fd|
chunk = nil
# Ruby 1.8.7 doesn't have IO#copy_stream
changelog_gz.write(chunk) while chunk = fd.read(16384)
end.close
else
logger.info("Creating boilerplate changelog file")
changelog_gz.write(template("deb/changelog.erb").result(binding))
end
end.close
end # No need to close, GzipWriter#close will close it.
attributes.fetch(:deb_init_list, []).each do |init|
name = File.basename(init, ".init")
dest_init = File.join(staging_path, "etc/init.d/#{name}")
FileUtils.mkdir_p(File.dirname(dest_init))
FileUtils.cp init, dest_init
File.chmod(0755, dest_init)
|
| ︙ | ︙ | |||
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
safesystem(*args)
# pack up the .deb, which is just an 'ar' archive with 3 files
# the 'debian-binary' file has to be first
with(File.expand_path(output_path)) do |output_path|
::Dir.chdir(build_path) do
safesystem("ar", "-qc", output_path, "debian-binary", "control.tar.gz", datatar)
end
end
end # def output
def converted_from(origin)
self.dependencies = self.dependencies.collect do |dep|
fix_dependency(dep)
end.flatten
self.provides = self.provides.collect do |provides|
fix_provides(provides)
end.flatten
| > > > | > > > > > > > > > > > > > | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
safesystem(*args)
# pack up the .deb, which is just an 'ar' archive with 3 files
# the 'debian-binary' file has to be first
with(File.expand_path(output_path)) do |output_path|
::Dir.chdir(build_path) do
safesystem("ar", "-qc", output_path, "debian-binary", "control.tar.gz", datatar)
if @attributes[:deb_sign]
safesystem("dpkg-sig", "-s", "builder", "-k", @attributes[:deb_sign], output_path)
end
end
end
end # def output
def converted_from(origin)
self.dependencies = self.dependencies.collect do |dep|
fix_dependency(dep)
end.flatten
self.provides = self.provides.collect do |provides|
fix_provides(provides)
end.flatten
if origin == FPM::Package::Deb
changelog_path = staging_path("usr/share/doc/#{name}/changelog.Debian.gz")
if File.exists?(changelog_path)
logger.debug("Found a deb changelog file, using it.", :path => changelog_path)
attributes[:deb_changelog] = build_path("deb_changelog")
File.open(attributes[:deb_changelog], "w") do |deb_changelog|
Zlib::GzipReader.open(changelog_path) do |gz|
IO::copy_stream(gz, deb_changelog)
end
end
File.unlink(changelog_path)
end
end
end # def converted_from
def debianize_op(op)
# Operators in debian packaging are <<, <=, =, >= and >>
# So any operator like < or > must be replaced
{:< => "<<", :> => ">>"}[op.to_sym] or op
end
|
| ︙ | ︙ | |||
423 424 425 426 427 428 429 |
dep = "#{name} (#{debianize_op(op)} #{version})"
end
end
name_re = /^[^ \(]+/
name = dep[name_re]
if name =~ /[A-Z]/
| | | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
dep = "#{name} (#{debianize_op(op)} #{version})"
end
end
name_re = /^[^ \(]+/
name = dep[name_re]
if name =~ /[A-Z]/
logger.warn("Downcasing dependency '#{name}' because deb packages " \
" don't work so good with uppercase names")
dep = dep.gsub(name_re) { |n| n.downcase }
end
if dep.include?("_")
logger.warn("Replacing dependency underscores with dashes in '#{dep}' because " \
"debs don't like underscores")
dep = dep.gsub("_", "-")
end
# Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
if dep =~ /\(~>/
name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
|
| ︙ | ︙ | |||
471 472 473 474 475 476 477 |
end
end # def fix_dependency
def fix_provides(provides)
name_re = /^[^ \(]+/
name = provides[name_re]
if name =~ /[A-Z]/
| | | | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
end
end # def fix_dependency
def fix_provides(provides)
name_re = /^[^ \(]+/
name = provides[name_re]
if name =~ /[A-Z]/
logger.warn("Downcasing provides '#{name}' because deb packages " \
" don't work so good with uppercase names")
provides = provides.gsub(name_re) { |n| n.downcase }
end
if provides.include?("_")
logger.warn("Replacing 'provides' underscores with dashes in '#{provides}' because " \
"debs don't like underscores")
provides = provides.gsub("_", "-")
end
return provides.rstrip
end
def control_path(path=nil)
|
| ︙ | ︙ | |||
502 503 504 505 506 507 508 509 510 511 512 |
def write_control_tarball
# Use custom Debian control file when given ...
write_control # write the control file
write_shlibs # write optional shlibs file
write_scripts # write the maintainer scripts
write_conffiles # write the conffiles
write_debconf # write the debconf files
write_md5sums # write the md5sums file
# Make the control.tar.gz
with(build_path("control.tar.gz")) do |controltar|
| > > | | | | | | | > | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
def write_control_tarball
# Use custom Debian control file when given ...
write_control # write the control file
write_shlibs # write optional shlibs file
write_scripts # write the maintainer scripts
write_conffiles # write the conffiles
write_debconf # write the debconf files
write_meta_files # write additional meta files
write_triggers # write trigger config to 'triggers' file
write_md5sums # write the md5sums file
# Make the control.tar.gz
with(build_path("control.tar.gz")) do |controltar|
logger.info("Creating", :path => controltar, :from => control_path)
args = [ tar_cmd, "-C", control_path, "-zcf", controltar,
"--owner=0", "--group=0", "--numeric-owner", "." ]
safesystem(*args)
end
logger.debug("Removing no longer needed control dir", :path => control_path)
ensure
FileUtils.rm_r(control_path)
end # def write_control_tarball
def write_control
# warn user if epoch is set
logger.warn("epoch in Version is set", :epoch => self.epoch) if self.epoch
# calculate installed-size if necessary:
if attributes[:deb_installed_size].nil?
logger.info("No deb_installed_size set, calculating now.")
total = 0
Find.find(staging_path) do |path|
stat = File.lstat(path)
next if stat.directory?
total += stat.size
end
# Per http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Installed-Size
# "The disk space is given as the integer value of the estimated
# installed size in bytes, divided by 1024 and rounded up."
attributes[:deb_installed_size] = total / 1024
end
# Write the control file
with(control_path("control")) do |control|
if attributes[:deb_custom_control]
logger.debug("Using '#{attributes[:deb_custom_control]}' template for the control file")
control_data = File.read(attributes[:deb_custom_control])
else
logger.debug("Using 'deb.erb' template for the control file")
control_data = template("deb.erb").result(binding)
end
logger.debug("Writing control file", :path => control)
File.write(control, control_data)
File.chmod(0644, control)
edit_file(control) if attributes[:edit?]
end
end # def write_control
# Write out the maintainer scripts
#
# SCRIPT_MAP is a map from the package ':after_install' to debian
# 'post_install' names
def write_scripts
SCRIPT_MAP.each do |scriptname, filename|
next unless script?(scriptname)
with(control_path(filename)) do |controlscript|
logger.debug("Writing control script", :source => filename, :target => controlscript)
File.write(controlscript, script(scriptname))
# deb maintainer scripts are required to be executable
File.chmod(0755, controlscript)
end
end
end # def write_scripts
|
| ︙ | ︙ | |||
590 591 592 593 594 595 596 |
rescue Errno::ENOENT => e
raise FPM::InvalidPackageConfiguration,
"Error trying to use '#{cfg_path}' as a config file in the package. Does it exist?"
end
end
allconfigs.sort!.uniq!
| | > | | | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
rescue Errno::ENOENT => e
raise FPM::InvalidPackageConfiguration,
"Error trying to use '#{cfg_path}' as a config file in the package. Does it exist?"
end
end
allconfigs.sort!.uniq!
with(control_path("conffiles")) do |conffiles|
File.open(conffiles, "w") do |out|
# 'config_files' comes from FPM::Package and is usually set with
# FPM::Command's --config-files flag
allconfigs.each do |cf|
# We need to put the leading / back. Stops lintian relative-conffile error.
out.puts("/" + cf)
end
end
File.chmod(0644, conffiles)
end
end # def write_conffiles
def write_shlibs
return unless attributes[:deb_shlibs]
logger.info("Adding shlibs", :content => attributes[:deb_shlibs])
File.open(control_path("shlibs"), "w") do |out|
out.write(attributes[:deb_shlibs])
end
end # def write_shlibs
def write_debconf
if attributes[:deb_config]
FileUtils.cp(attributes[:deb_config], control_path("config"))
File.chmod(0755, control_path("config"))
end
if attributes[:deb_templates]
FileUtils.cp(attributes[:deb_templates], control_path("templates"))
File.chmod(0755, control_path("templates"))
end
end # def write_debconf
def write_meta_files
files = attributes[:deb_meta_files]
return unless files
files.each do |fn|
dest = control_path(File.basename(fn))
FileUtils.cp(fn, dest)
File.chmod(0644, dest)
end
end
def write_triggers
lines = [['interest', :deb_interest],
['activate', :deb_activate]].map { |label, attr|
(attributes[attr] || []).map { |e| "#{label} #{e}\n" }
}.flatten.join('')
if lines.size > 0
File.open(control_path("triggers"), 'a') do |f|
f.write "\n" if f.size > 0
f.write lines
end
end
end
def write_md5sums
md5_sums = {}
Find.find(staging_path) do |path|
if File.file?(path) && !File.symlink?(path)
md5 = Digest::MD5.file(path).hexdigest
md5_path = path.gsub("#{staging_path}/", "")
md5_sums[md5_path] = md5
end
end
if not md5_sums.empty?
File.open(control_path("md5sums"), "w") do |out|
md5_sums.each do |path, md5|
out.puts "#{md5} #{path}"
end
end
File.chmod(0644, control_path("md5sums"))
end
end # def write_md5sums
def to_s(format=nil)
|
| ︙ | ︙ |
Changes to lib/fpm/package/dir.rb.
| ︙ | ︙ | |||
60 61 62 63 64 65 66 |
if attributes[:prefix]
destination = File.join(attributes[:prefix], destination)
end
destination = File.join(staging_path, destination)
| | | | | | | > | > > | | | | | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
if attributes[:prefix]
destination = File.join(attributes[:prefix], destination)
end
destination = File.join(staging_path, destination)
logger["method"] = "input"
begin
::Dir.chdir(chdir) do
begin
clone(source, destination)
rescue Errno::ENOENT => e
raise FPM::InvalidPackageConfiguration,
"Cannot package the path '#{File.join(chdir, source)}', does it exist?"
end
end
rescue Errno::ENOENT => e
raise FPM::InvalidPackageConfiguration,
"Cannot chdir to '#{chdir}'. Does it exist?"
end
# Set some defaults. This is useful because other package types
# can include license data from themselves (rpms, gems, etc),
# but to make sure a simple dir -> rpm works without having
# to specify a license.
self.license = "unknown"
self.vendor = [ENV["USER"], Socket.gethostname].join("@")
ensure
# Clean up any logger context we added.
logger.remove("method")
end # def input
# Output this package to the given directory.
def output(output_path)
output_check(output_path)
output_path = File.expand_path(output_path)
::Dir.chdir(staging_path) do
logger["method"] = "output"
clone(".", output_path)
end
ensure
logger.remove("method")
end # def output
private
# Copy a file or directory to a destination
#
# This is special because it respects the full path of the source.
# Aditionally, hardlinks will be used instead of copies.
#
# Example:
#
# clone("/tmp/hello/world", "/tmp/example")
#
# The above will copy, recursively, /tmp/hello/world into
# /tmp/example/hello/world
def clone(source, destination)
logger.debug("Cloning path", :source => source, :destination => destination)
# Edge case check; abort if the temporary directory is the source.
# If the temporary dir is the same path as the source, it causes
# fpm to recursively (and forever) copy the staging directory by
# accident (#542).
if File.expand_path(source) == File.expand_path(::Dir.tmpdir)
raise FPM::InvalidPackageConfiguration,
"A source directory cannot be the root of your temporary " \
"directory (#{::Dir.tmpdir}). fpm uses the temporary directory " \
"to stage files during packaging, so this setting would have " \
"caused fpm to loop creating staging directories and copying " \
"them into your package! Oops! If you are confused, maybe you could " \
"check your TMPDIR or TEMPDIR environment variables?"
end
# For single file copies, permit file destinations
fileinfo = File.lstat(source)
if fileinfo.file? && !File.directory?(destination)
if destination[-1,1] == "/"
copy(source, File.join(destination, source))
else
copy(source, destination)
end
elsif fileinfo.symlink?
copy(source, destination)
else
# Copy all files from 'path' into staging_path
Find.find(source) do |path|
target = File.join(destination, path)
copy(path, target)
end
end
end # def clone
# Copy a path.
#
# Files will be hardlinked if possible, but copied otherwise.
# Symlinks should be copied as symlinks.
def copy(source, destination)
logger.debug("Copying path", :source => source, :destination => destination)
directory = File.dirname(destination)
if !File.directory?(directory)
FileUtils.mkdir_p(directory)
end
if File.directory?(source)
if !File.symlink?(source)
# Create a directory if this path is a directory
logger.debug("Creating", :directory => destination)
if !File.directory?(destination)
FileUtils.mkdir(destination)
end
else
# Linking symlinked directories causes a hardlink to be created, which
# results in the source directory being wiped out during cleanup,
# so copy the symlink.
logger.debug("Copying symlinked directory", :source => source,
:destination => destination)
FileUtils.copy_entry(source, destination)
end
else
# Otherwise try copying the file.
begin
logger.debug("Linking", :source => source, :destination => destination)
File.link(source, destination)
rescue Errno::ENOENT, Errno::EXDEV, Errno::EPERM
# Hardlink attempt failed, copy it instead
logger.debug("Copying", :source => source, :destination => destination)
copy_entry(source, destination)
rescue Errno::EEXIST
sane_path = destination.gsub(staging_path, "")
logger.error("Cannot copy file, the destination path is probably a directory and I attempted to write a file.", :path => sane_path, :staging => staging_path)
end
end
copy_metadata(source, destination)
end # def copy
def copy_metadata(source, destination)
|
| ︙ | ︙ |
Added lib/fpm/package/exe.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# api: fpm
# title: win32 exe/sfx target
# description: Builds a self-extracting ZIP posing as Windows installer
# type: package
# category: target
# version: 0.3
# state: beta
# depends: bin:zip
# config: <opt name=prefix description="Relocatable extraction path">
# pack: exe.rb
# license: zlib, MITL
# doc: https://github.com/jordansissel/fpm/issues/810
# author: mario#include-once:org
#
# `fpm -t exe` outputs a self-extracting zip for Windows. It utilizes
# the »FreeExtractor« SFX by Andrew Fawcett (zlib license, binary below).
#
# http://freeextractor.sourceforge.net/FreeExtractor/ holds the original
# MakeSFX.exe builder. It's somewhat olden, but still very functional,
# installer-esque, smaller and more robust than e.g. 7zips sfx.
#
# Dist files will be packaged relocatable, with --prefix or --exe-dest
# serving as selectable target path at installation (--exe-autoextract
# can evade this).
#
# Following placeholders can be used in all path parameters:
#
# $programfiles$ $favorites$ $windir$
# $targetdir$ $quicklaunch$ $sysdir$
# $desktop$ $startmenu$ $curdir$
# $temp$ $startup$
#
# An --exe-shortcut='$desktop/program.lnk|$targetdir$/run.py' for instance
# creates a desktop icon. While --exe-exec='$targetdir$/setup.cmd' starts
# a post-installation script (shows up in a console box though).
#
# With a binary-safe editor, one can even edit the embedded ini-section
# in the generated .exe; It's advisable to apply `zip -A` afterwards however.
#
require "fpm/package"
require "fpm/util"
require "fileutils"
# Convert a ZIP to Windowns installer using FreeExtractor SFX
class FPM::Package::Exe < FPM::Package
option "--dest", "DIRECTORY", "Default extraction path, may include \$temp\$ or \$programfiles\$ as placeholder."
option "--icon", "ICO_FILE", "Icon (32x32 and 256 colors) to use for installer."
option "--shortcut", "ICON.lnk|CMD", "Create a .lnk file, where the link name could use \$desktop\$ as placeholder."
option "--exec", "CMD_FILE", "Start batch script after unpacking. Path could use \$targetdir\$ placeholder."
option "--autoextract", :flag, "Immediately start unpacking, no path selection dialog."
option "--delete", :flag, "Delete temporary files after --autoextract and --exec."
# Create zip, combine with SFX
def output(output_path)
output_check(output_path)
# Create zip
tmpzip = ::Dir::Tmpname.create(['fpm_exe_FE_', '.zip']) { }
::Dir.chdir(staging_path) do
files = Find.find(".").to_a
safesystem("zip", tmpzip, "-9", *files)
end
# Prepare INI
ini = [
"[FE]",
"Name='%s'" % name,
"ZipSize=%s" % File::size(tmpzip),
"Exec=%s" % attributes[:exe_exec],
"DefaultPath=%s" % (attributes[:exe_dest] || attributes[:prefix]),
"Intro=%s" % description[0..1023].dump,
"URL=%s" % url[0..127],
"Author=%s" % maintainer,
"AutoExtract=%s" % to_i(attributes[:exe_autoextract]),
"Delete=%s" % to_i(attributes[:exe_delete]),
"OpenFolder=0",
"NoGUI=0",
"Shortcut0=%s" % attributes[:exe_shortcut],
"Debug=0",
# FreeExtractor uses neither of the following settings,
# they're just preserved here for eventual re-extraction
"[FPM]",
"Version='%s'" % version,
"Epoch=%s" % epoch,
"Iteration=%s" % iteration,
"Architecture=%s"% architecture,
"Category=%s" % category,
"License=%s" % license,
"Provides=%s" % provides.to_s,
"Depends=%s" % dependencies.to_s,
]
# Icon file setting now required displacing parts of the binary (offset x5AFD)
if attributes[:exe_icon_given?]
if [2216, 2238].include? size = File.size(fn = attributes[:exe_icon])
icondat = File.read(fn, 2216, size==2216 ? 0 : 20, :binmod=>true).unpack("H*")[0]
@@feHeader.sub!(/2800000020000000 4000000001000800 (\s*[[:xdigit:]]{2}){2200}/x, icondat)
else
logger.error("Windows .ico resources must be exactly 2216 or 2238 bytes")
end
end
# Combine into installer
open(output_path, "wb") do |sfx|
sfx << [@@feHeader.gsub(/\s+/, "")].pack("H*")
sfx << ini.join("\n") << "\n"
sfx << "\x00\x03\x04\x1C" # ␀␄␃␜ padding: NUL byte, End-of-Text, End-of-Transmission, File-Separator
# Append ZIP file
open(tmpzip, "rb") do |zip|
IO::copy_stream(zip, sfx)
end
File.unlink(tmpzip)
end
# final zip tuning
safesystem("zip", "-A", output_path) # fix "junk" offsets in central directory
File.chmod(0755, output_path)
end # def output
# ReImport package infos from [FE]+[FPM] ini section, unpack zip
def input(path)
# tuned to FE installer section
if File.read(path, 48<<10, 20<<10) =~ /(\[FE\]\n.+?)\x00/m
# extract key=value fields
meta = Hash[$1.scan(/^(\w+)=["']?(.+?)['"]?$/).map{ |k,v| [k.downcase, v] }]
self.name = meta["name"]
self.maintainer = meta["author"]
self.version = meta["version"]
self.epoch = meta["epoch"]
self.iteration = meta["iteration"]
self.license = meta["license"]
self.category = meta["category"]
self.description = meta["intro"].gsub("\\n", "\n").gsub("\\\"", "\"")
self.architecture = meta["architecture"]
#self.dependencies = meta["depends"] .to_a ? JSON.parse ? eval ?
#self.provides = meta["provides"]
attributes[:post_install] = meta["exec"]
attributes[:exe_dest] = meta["defaultpath"]
attributes[:exe_autoextract] = meta["autoextract"]
attributes[:exe_delete] = meta["delete"]
attributes[:exe_shortcut] = meta["shortcut0"]
# keep other attributes?
#@attrs.merge!(meta)
end
# extract via Zip
zip = convert(FPM::Package::Zip)
zip.input(path)
#safesystem("unzip", "-d", build_path, path)
end
# FreeExtractor SFX header, http://freeextractor.sourceforge.net/FreeExtractor/
#
# This software is provided 'as-is', without any express or implied warranty.
# In no event will the author(s) be held liable for any damages arising from
# the use of this software. Permission is granted to anyone to use this
# software for any purpose, including commercial applications, and to alter
# it and redistribute it freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software in a
# product, an acknowledgment in the product documentation would be
# appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
#
# 3. This notice may not be removed or altered from any source distribution.
#
# Code Copyright (C) 2000-2001 Andrew Fawcett (andrewfawcett@users.sourceforge.net)
#
protected
@@feHeader = %q{
4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000e00000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000272030dd63415e8e63415e8e63415e8e63415e8e60415e8e63415f8e02415e8e015e4d8e6e415e8e8b5e558e62415e8e8b5e548e6f415e8edb47588e62415e8e5269636863415e8e000000000000000000000000000000000000000000000000504500004c0103004dfab63a0000000000000000e0001f010b010600006000000010000000300100009201000040010000a001000000400000100000000200000400000000000000040000000000000000b001000010000000000000020000000000100000100000000010000010000000000000100000000000000000000000bcab01007401000000a00100bc0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000555058300000000000300100001000000000000000040000000000000000000000000000800000e0
555058310000000000600000004001000054000000040000000000000000000000000000400000e02e727372630000000010000000a00100000e000000580000000000000000000000000000400000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0024496e666f3a20546869732066696c65206973207061636b6564207769746820746865205550582065786563757461626c65207061636b657220687474703a2f2f7570782e7473782e6f726720240a002449643a2055505820312e303720436f707972696768742028432920313939362d323030312074686520555058205465616d2e20416c6c205269676874732052657365727665642e20240a00555058210c09020ae8ba261082435eb39f770100ff510000001201002602008f
ffffffff558bec8b4d0c56578b7d088bf781e6ffff0000c1ef1085c975086a0158e90200fdedb7ff0111837d10000f86e2170053bab0150539551073038bffeffeb604290283fa100f8c97198bc2c1e8048bd8f7dbc1e3042063dffe03d30fb61903f304590103fe0702830c32c8030405060c32c82007080932c820830a0b0cce20830c0d0e0f5bfbeddf0383c110480f8577caff85d2740b1786f0f6ffeeb741154a75f5bbf1dd8bc633d28bf3f7f68bc78bf209ffadfbeff7f3de8bfa0f87202f5b14c1e0100bc65f5e5deeeddb87c318550c41750433c00d8b450828bd0bee850827f7d0b9441082cbf6befdb6b012532d03621a8bf0836d1e23f133f36bff0b1705c1eb4a04b59871400033c342181d1762df9e17065a018b34162b0833468eb5b7f019c623c120160485ee6e54985bc6302d22f016ba602c124f293df927646cfb06741b313a23f7ff4dfe1b5ae11075e513fa0744240c5333db568b74db7ffbdf073bc35774058b4e3c892a068b7c241483f8040f836ecbfffff805750bff760cff7728ff57245959833e06571004e86dfb6f374b11230f8b4628891e89463402300ab7dbb7f638895e1c450420740e5300ffd0143c83351e7bfbc40c8947306c5bc35364576a40880b7ff7ad3e4c56208bf81c85ff744c68a0057b6af7de63b30815302485c06510736b267b7feb2a8b5c2418533721281794852dd8
6c24232a1910dc56b8bbfdeb1d8327000385472cdc146a00e30ac76bb8dd38ad022b38108a0bdf4c257783ec2c901528144e348b4759b6ed1bee1f8945fcc32005081c6dfbff650c303bc8894df873052bc148eb050e2c07dd58286c16f01c13095f510778e9f6b6b3ffa7f01a40ba035a440c732369c1dd5873fc4884efcc770385bb7da1b08365836bfcd3e00952438350ffffff7808ebd877e0078bc8d1e883e10183e800894e1874634874172abdd96e0209483a5beb9fc16d30e9b6ff568ce316eb948d45e8575004e403e02cdb6db7dc50c71d3eed75e802e4e0ad7c61b7dc110b4812288946044277a9f1ca1f0640be55c7060c9b736e5300f4035e4f7a931301ed5263337c0711d8e8291039fb1773ee21aff9207322c42ab6db1736c1c1088924ebd929088d85b1ee4bf7d1255ce972c873b5b6e0dab8040a817d390451ee177ee3543a6a0258eb0a3518f7d81bc0f4b4b9b3d88906675fca2f09f0375cf8b977858c4e2c39677522718b56280b862ffc3bd07418895576082bc2486f792bc7b6fdb6ca77f02d7562f0107bf86fadb5ed6157560a6d393022120f6bdf0c0ddb3b9d350b8bc12b0638b7b5421b094cb2484bf008f66e8e4b397519ad257412275d33ddf9f9c849894dd08955952022edefb54609f1f84ff4760324b69b7bb7f472f0390a0f80f45303f8da46b7ed7c366316f4762935014fa1706bdf
05f003d8293bd0dcfd1751481e32b2048c0ed80318ba85160b4c3f7bc8437db7b12d9b1f361d6065308bd081e2e02a98cddd0781faa0051351b88ad4f05f94e01f6a048d840802cb509966e4b37114010c1d040eb7e70a6f03660800f110d6380a831c32f6c2c004a008735b9a033d63e170904eec3f0cb6fdf168fc0c8d9875ee5a6289048a85cb5bbb1d04ff4f5b08c1e9c10431df6b73ff72a5837e08137316121b0cac3024b65ffadd810026ebe4578d4e14a9248d461051b676b06d30378b00074a1879be14b2b5e5ce5cbe85d702212cb505d96ebb6f80d0f0c1eaf6e21ff4022a7cdbd2970f54011f10983c20cf4979750213e20955cdaddc928b10a1cf1423542d38286cac8d10effbecaed16eb8324801e47319d3d43cb6dbe06ea729fbfe1481b4c906ca276df0b2711275ce07038d4264c9c3d116de0e0f95c149a808f31e5db003d65b3c8d140802ec01885b1afbaed2650c8b2348013d36d26dafc81142037a0804f04a1b0d56ba5ed118c2024f3701c90138ecc012c07e8bb0c101b5ba04334488fceb02636b30947da7954171f0a6990bb3565abc744dd8ade165c3560d1445ec095851149fa16dbad403f0ec51818c71e9756edd8e057c0501534127f006968f87fb711c751ffb69242c4b976b96b9f47f278ad8d47e0cb22cf0ec148597014c8e59b1108241b82e5b2bd00ef2de051cfc89a45fe0066fc3
2b079a1f56014708d26e051cdf3e7cc75ed885c402c9c0f0e73c6aa13070020359a259b7020d0313a6a411a6b96eb473064e851800029a9bc0b347372604868ff6d2ccf5fdfb8c0ffdeb0f13920f606e1ca0891f6afd81bdb25d440add27eb1013b0e75ae983676e39ee3c3a838624b1463ffdd6004b96ac45109982afc3615b8057485625f64af6fb0c6451e7487506c09db204e69cc3c16afc02660b802756077189c307566b394e74cf2c5bc3410b67494d4e2ccdcbb24d4fcb2b0f4f4dd29dedad2b50eb4e9e0875012d92ef45eb2324fea537e1828683c9c9c3741347b35c36db391403a18b151816ce5136cb659518511a993919c58b2e667cd60c112c6271880e7a36461fa356ae245d3245186a1c8db3401778706350dabcfddb42548af38320008848108af68848118bcb76a3d948140514185d5a3146ea39180c8306b2626b746cc457aaf8c3d95f52a1c7761f081cc318d2b516a78b5dc2ae079f5d9436638b0bd75d02058dff9f2e9d96203dffe99100f8f80ae11a236c09874d0c0c4d78e70ecd784a186268c8572b0edd7a6b9767014e156fff7318834311b2eea9bb0614500710c8118410927bb4429e83c4184796a16c7db77415e649f7d91bc9f50290b6edb7630b220bbc5889433b43fc43dc6d7b37c10301990e39477328ab05da8306095ac46e091a0fb69308f8310060f71db22cd0149708d04b4033
d04a6d15936c059a7b8b90034001298f0d1d851badb60c0c08c16fe8756666c29dad4052036f7e845aa3fc2d99e8f6c11074147845b66dbae6211b2402ebdc1840846c6ed9dce80a0820de141907420ec8f6c2a708b202d03ab325440399016ade48836b32a70811189842809c10025702097501f9e4178504dfed8152ef0ce7e74a734b84c262ae8348dbc88719e71c3214f2ca01050ce12ed08d8b5ec2678bcaa3f7b82db41f28433b10730e290ca16d38362303c2ff7b969be79e709b6cb83bd2860722b463030d5891751e628b4fcacb583886ccd16b45f09d1caea6d25e6ebba13e93fb786ba21030793de862c7363099442c983bd14bbacd6d80751f5142e854121fb812910ab38b910f77fb6733f7ec8a09880a42f1ec480f3757a03d4349444149ecff8bf1b2a14bc148ff8323005cbf22e40ae4825a1cb9083f74538a4bb3b9dcc1b05c3ff90f4405049a41e1b27766eb40b00a9635c0dae5fd1e1dcca5ecec3f10ec25c128c6426fa43c080776e16d2060642edaff26cfe55b831ffc1157300e0c5003774d2fcbb22c4f4d4e4dd92b1e205b83f3890e015e0ea2086c0f16c9010e24fe93c16c91830ed2b2596e971b811c03291d84111ed32c97cd5f501f5620a80544c0015bffab04fbd89a01810662b71c18b5b3fc05248b4f348b5f201c76039e051c201efcc80f4d324d06471533df22593b13b10c5dfa1473
1770a452848b3030680bd81b3099da3f83c20ad55123c3a9b5994ceeda5d18e0f36dbfcdc6a652d3eb0b2bd121181b999136f47522dd9d388d7a58a423cb03e339ebc4dc36b0f616091e0e083ec3beb5d64945861b5d18731d899cdb2d737f8c1c72e61eb8c9bd5bd98f14191cc1937ff667f3f66c1886f6c3832bf6c3df00f63ad7ce9d105c652ec8882ef8ad6660a22d4283e30f3bd3bf6daf357d4f6f1a3172d3e60975186d8bd864fa3440282313805386dd2bd3360370d50201bec35544542ed83bde7217c877b4f67fc68a1888198a5801414006037f02eb1fb5ffe10b7403c68bf07dc6397508760f2904de66eeed251e4e75f73d0b780875f54ed8d01acb8bd9b9eb16518a4d6d0f053654f489f42288e3a63ef101817df05c722eb60a7228ea9aabb5b611888934d7e4ba678d4b8e269173026e48c761ae766da3fd19eb4bf51fe7e0466997d2eb346219c2256b6674171e1b3578bc947d16eb15164cd3ad51f8e665f1c1c1e3030cd068c314e3f455fc03ca37e4fbe7df11c3f0473420e0b40433d23bc2d96ef1db7431a51c3bca742a065014020856e2ff426f710c52f7de1bf65083e69f1e45b060e3401cdc14f71cbad4a2cfce4a58c306aacbd44b4bf674303f5429bfd86d5f27240074232b140c0956230b1407a1854a0d061c09661c8a1183b5000e3c5e2d06771b5f7133ff3bc7a8d2d58a003a2042a6a9
c290cbaf005d68df99ed1c380ab966103bf77507360bbdd46f54153e397e208991750ac7da3c736bdba63a1b7e2811241d0e24566d08716b6a180a5b53dc5cb7df6843fb7504a3eb748b4c24dc78142d4edd76ac3bcf070c7df68476df6ec08958bd087ce7f90f7f4b11d3e35db0b9857a1007c60c6b530b5f53ebf7d025b3f356fd96931cb6dbd7041c4145394b750e585fbadf5bfd19141b40c7eb120a13dce3eb47cc88efacebe56afa58211e6b4db8b7163bf057ebc3393e08db43b767c90607c1028d0ca4fbb7abd5a55b3c0c744ea90d5f83e0c885da08a9bea2024528c0d26d39700fc930014935d178684fd90e4ccf096c37b5b3b40405bcff0680d174f7b1d12df03811c7a50531b11a3dbeb56dc1e91dc152de77e4c700477c473e708acc720c4851468ddaedb406ede76a1f56f7e8df3a1a1e37413ae00803c35ff7f7d98dd61634179b723f89395dbd65e9619679d3202e4acf6883bd1dc701071676f9390321735b35c08bd88ffb6311399cd0dccf1e8360043b85db7503c71c6b99620bace498237fb205f786715156772e3eb7d9866d1b620bbd0c75c80888b9e72ea90ac5bdc88259d8461a00bb18b00817b0c243b62d09747f3af10efb33433e70c1e1100148290a4b0e9049550886ad64920b2b2635d8d61a7f3b0aa3dcb9bad60eaf6b8a0618d730e1444c32944de6c203bc944c32940492c25e973005
4464492d63eb58f66a02c574302f0653394c03e10ec7d3eb5c9ae05ab30e9e86ebedcb6ee35b38ebe99125b7e00370279acb65d334c4ee20284426a9354dd734d701172b3d413a5da0c90335510bfc003a3a624c5ed3046a13452f05dbe2a5977958eb4415fc535040140225d7ed620c466a000113296b5e7ab915c918e683f8fb4e18740803cdb5099f3800250c2a682dc1ac202a1891d638dc1d096c81ecf004fb7065db61a0c7f8558c02909665599694989ca0a4a859966559acb0b4b8bc0d0a9465c0c4d8311bb8df8801b7ff44858c8d0341f054b6a5e3397d8cec45100420de5c4fa0ff757d20a2b9908b37292ef10db6897520391009414c465c7a578176f33bf17dfc7308df78fbb7230f8d75c8583916cb4883ee3bc275f4390557147244d73905fd1bd46a36a8503773168d748d8cbb37c40b120f88306983c604d11772eeb6dde006d4c1e6197c358a4c03179a6deb2bdf57d0210e030e95505fe0970c00be33c94874137d034c3d90e6f8dde883c704068c3d1775ef8d15ec1fbffb8b0383c37374198b8c854c10552c8d8409ec17de7e893c8a418921d2473b2372d88b84351e063051605d6ba0f9b58fc5c2452c0af4c9fcf7db3bae52fb6caf6e20051055e40203edb0f4eb8ffe257ddcdfff28ebaadc72b7d42fe08b02dfc848d15aedfdb184c522e774f4a289112ef58ee29920f40303ed685bb0d920cc
df2bc353fc5db84a08ae8a55ec6a012bcb58d57ee1b642b7762d20e083caff2b14c5dba26d453b1faf410568aa7bb805560421e0210b0deab76ddbd49f831c6b4d283b5a7ed3e21670d1779e8d341081fe4479712eb5c0292c7a045f55f837e8ce5ee48d94c6023089f545f813b6b6353469f4cbd7b4274689176eb2884d15cb88458510db16defdc6d3e88b4a99d82bf9c1682b4214b1cd9163637c21eb8dd033161cf57507ccdf8f5f528a7983bb014b0c2ac33c468d0c888b6d832f500506c645d850379eddfff70b4d1073143d0094f81ac980e1a080c1606debdf32d45a44189e24028a0c084a5de86d1350817e8b3ce22bf47cd83f041a45f45e389836b7d05a9145081e7d5fc10b6f6ce199fe89a1d6c1e2457950aa5d8b042a1b72ea02ab2e0cad913049dd85455eb45b74063361ebf6055ff6d982d11107518b4e23fea10bb0d43bac0bfd83e810d5e6e84607ebe68d0dec587580fbeb3bfe280be00bc5dd82e345d40fcc12153955d0ccf6026cd9fc1a7de80109e29ad8550dfd52194daaf65d3a199efb5164fffa6820d2e110c2f2358976fe085753294aaa500257bcd0581b50829a7524be6f836e7d2c0f1c6860764468e432ad6e585b24530e394d0a6eae449b14744a793b3f04bbb9e6cbe638331820685477dc388601eb6a485250360f61eba5adc5390149d37735f6eb1158898dba41c80aebd2fcb0cab9
d24646586f4eb14f420bc103fa59618b0d08b26c0617d5ec0b080cd3b90d1ab1100b091019f52e55a04cc0c3f74995376a63625e4c0c3bcbbf6c8d1240ea0f2c102bd93bd80b71818d7683d805740a52fb1af5066894cb015f142ba878eb8e2ebb3896135351e23cffd0d16e48e550381275fc5b2b0a7c6c01feda05fc85da0d7218f3755f6b44af1425dc463463876776e2bd056066606c084dce997c052436101ad15030bceb7f68c10b41856fa04dc508ff15c8700785af35d8f6740d791555011665a2d66a2bab6b2542f7262bc5375630651e6b4be6deb66d591e597521151411c21fc41a0f17e903ce89ac1d6237d1064321005e4365c8ef50a13068d8447d083968c1dc0c1312000c46aeccf03bf9fe17101ff2c8614b72081803cf7327455bf0cc10fd1289bfd0bd538b1dc0e95650f800578d3c01605b67aa6d6a07be02bf89cf89516a63ab7d53d35aff4d79a9e0a06e0c1004bf0404422b51636828262b285468d8ccec6f89bb07203df1ebd637201504584b868f9a08e04543cc6731010507e51c146a2b4df73a8127962fbcaff02b75dec63bc777f834d12c9cb95676705492f057813724c1fd90e73d8d47fd9cb841db1d7e27be803cf3dd807cebcdbbb7404b75e906020575e2030675db4fae6f875f95f875f28e100f827302cb6c936eb680005150bc486a9849a25f758cf3b7f1cd5d5257bb2489536a03
560d680aae0bcc588080e045f83db4c7da049e1f4903105656a479e1ee6c2fc01a020bf088f4505727db4be7d59483ceff12fc91b9533220f88578a3b71cd9952808128b3b2bd36353b889007506a9fd23bb926d995e7b9498126d21806c687c4f94580adfc0f0988d1401b297730785f6d2ca740be052570be0b4c2a5dae84e3f29f0c02bd1e578196383d398065389bd74d28c859155800f9012ad3676fffd6a20598db5168bfb53f3a56a250bafc31a9800116eaf3b9afe637b017f837e7cbc07ba29c159ff3660c220cc6aa956a79beb7819abc1e704330b4c55a3835dc150043140a5022330ab14b03c2042cd88d0c81c14863d34180dda5a2245fffb6011381ae072eff7d02a7c6bb3429a25074814530346dbb66d84578984d0cb23c3a58993e09ab63debd3f41747756636f4edea817d104b010274c9fc9920d9dc12cea0523613a490016400a8acc864cb06b47013b0800c2003b8bc03d80036c063c413c832800c20ccd02503d800d48bf0435ba3328b9205d084aad047e885a853143bcf7430a2e4165e06730b80db0020685b6f6fb7247d1832761d830600761710f8c5ce3a3c519d0d754b5b0377ba432bd7004c3f081c77a3061a4c44c41520723f612b6d4020281ac63852d417b4280cd148da5aa104bc3cda0260ec11e98ed0761850923f60a9edaf852163c703d03203605b08b65f6a75755924e4531bb6
52bb458c5d28522db53895906e0370c86e15232f8ffd1403094569c8761c28761753b5c2c1b25767d17ea90db38c591b0c9514755097a974a059388cf6056352e9b3a5f0fc5b403fc07b238834424946245700c0c55b6389c0048d46788a30e3c18dbf7e100ef80ac12d68b6db94c197ab2f07d63e3d04eb10397e9628dd2f405c10403b46044f9c19fb2d96744ec65648034e4c551010f64b2dc4112e014e1459a56215a12db597dbf2d85272fb5e7c395ff7047c5e07264be2570cd46faf3d040126b147e32d99eb4e05eb2f4364536a6c5347662bd81b78f37436156e40349503661894a648e0560a1c589daa895e76b0c6da0a123653356a98cadbb6e1db7516448b4f387d5f3424580d54b641bb2b08648b0fb51f0e0cc42f05df0c3c68e275296a3868b6ed4233f6e67cf1501128022c1a22b25b30a11459da442cdbf60bae47407e5c054460e0121db478125e97440835016ead3218777c5eac06348c6b0cbf53e47200c3061c893ba745a62260dceaae0c6d78502712b2f459410b284e32f8e0b2f585dccfffeb0c70f80304730b597ae75f7c0c2113b190ad640827496a04064a531e5d21db1a1e700978087b08559a4b06b0d541d51511501b1b864531383af64086b2b761753e2a40056c42ae44fcc26c6b0633022d0c082a48b9434bc9760103fdf4e39bbdeaaa43fc98ce018bd4106ead25072c8bc7a1350807
f1dd082b276ec4302debf8eeb518707ceed3c13b78f5395e04209c29bc443e60ab52012323110ca0743339f27062603bd6402db62578182a18f36b340acd7808561c1f51d4baf4787655bfb121730acdcd0691aa0fb366684031560c8453f9778d625bb2cb2a80760418b976a3fd1110017e40297e5c66f17e0c60e9da56f064754c828b0a5a022e4b2f45cffb7611b408148a04eef68de6880c02401872ef4a65db366c98541301153c60020cad53cbf218017e14081c10706100ba6f5154eb428bc75e285dd47ef5fa026c15435c232b012e20b7fb4475083258ecba28a675013974167e6813bfc21e3efc870305395d868b8259720e9e2c580b87d0c2c8a70364516e6c2d1317b62416c96ae989bf0ff4b3164a1263f1c156394e60d5c642335b52d0588fc79db63ad7a0974cf7534c3dcb29d84e1b36bbda3453641b546a6dd7442f7c960ce52d5674b150f6135351563a560bb4441ddd7ccd5f5b726f2f36ed5937d4040faf10c3e89031b550c30d013fab40d530c3a1e4e9fb364637543574716c036ad6a104eb0b66b1eb2ed81e5e0c0750a5c5e86c05e938d840da85eb1ee63cd0101390df9b55eb3a2fd62c1d625e6f5436d75c201280091187de886c52d4a085fcfbcccce5baf6086840a2105a787e711776eee4ed6ab62ca2406418ed2a64ddced00e040fb7104a9b4e44353837777ba53935dca09a6071ccd8ee
8f84466a2c79a3e4177c34f689b3963cf96a3c223110a9b163de80717a2815b8b91a1ac1fdd4a905c48a998816941c08ab37b33db31ecc3a09245734a83236b27dc10cca20240b1cd9de333218183526171042b0661b280814c988acdfa0d1d5c4480e2d177dc8f3ab11a123073a6c587fec195c136f756d539daf4163d99620d6a9811f72a64dfed49078ab8dd4bcbe01f60a2194b15410d184b126a102f31cc26cd339d7283053127a5c5e5f9de0231b1c445685b88a063c2237f88d5634039c8a460146840bf480d8defeb73e100d46eb0a3c207e06460c207ffa238434ddd639c50fe9d4e874bc7c976f71cbcae801730fb745ec6a0a87d95e89583cc61ac4bba36898f1378e5045d453135c225a45e7074301fd0c5363ad33a6ae3bdf7e1768bd001df71033f18a11306e71e9172288140e4741f97cf17b070068ba499862d9b2dc2bc0ad6d25d89500aff27e04d84e08be25ebf38bc6fb105f808215822d7666cc2b97bac01b743bf87f55507d021585ff5f62bb2d0cd203f7c40c4a8a0e84c9ed9bdfda742a3476ce2bc88a10120fbe1c01037e17dee0d22bdaaf40803c7775e84e06ffe06b0b05120dd029755b80ce545b7307f2ae50c0c0dd53e773b80488488d0341eba5e96e2bdf9d51fb2ce559a15440cbc57f2032c05338023d1336d4b75b7eeb35e43ad0742d2c8bd66bbbfdad6f8a193ad87411773c0a03db
96e5edbf55f34138040a75e93801798a5601060bc6b6d3dfeb6c5bbc350b1a1ef0da3fda10b93046ab58079897808193d5184e3f202dda47c1c7b24138ff2c1ad62e7d2928cbf3ce03c13b037d190c30112c7f36f2b88201260411296b81a1db7d665036c34b5511bdfbc225c02c900f4716104875f266f6c80087617ec72b954fc1c3811b0204cc8afff68d8d0a45424ef9617c08047a7f0380c1e03cd1f0d6680b7c0a0204e08a0ca151dbdf62133ac87c0a7f0df2f617d45436ca4064c90113b6683c7a7c2167f6d743e37715603bd87f2e85db3a71418d6f0b7656e84441827d2fd0b9f6bd4bebe5164617edbffe0a097f07b8b9eb0d2bc6504e47dfc17c17bd565b089a8559b7702e803fee296af765ebefc65faa383a76128a08300e2e300807134bed1b703bc677eedf49c36b4db7083498fa32bb04a6bfbbdb5c7f5f53be78d47855612e2a53080d60ba0d6a2055fef2c9c642341317766c5d0cd4cc01b47cda18d00250885077d3c07607de4bce83e001f6c3402f81780f4fe8a3188d5950717c6503b12d5672dfb1d8235a186d903e1b5728c30c89648a642f15493740f98b20c382681900c9525dd76e805068c580bb041dbdd6d030aec702dad3f4e380db0cdd0df40cb24b9691adc649fa00fc08741e1e4c2e0ba04b5516d8ef4088ebfeb08d2df605dcbdfd080866abaa8d1379b367dbb9cc72923075d84059
1873b35863db50b7100b19f9824d06c416cebc841b35987740268a15845e6e065e797973e1fc8895e0be63b1e605107b89e9fe15e852254ac18f04e78698d4907905012daa0b110cd1d47b5c5eba47400936bebf975911593922d050a3264857f46e1846e7febcbe0ca4f00e53728c60dff0134653741e68081b0a6b77135e08b8251cebddcc9e4d2823c59d50e5bab131033b1857e6235050ef9d43160de8e8ea1dac5ce8d12edd1952e5021a10e1811cfca375448fe72407f98ddcfbddfbdcfbb99821c8f0a3862592c17614735af52635215ac873bb90e4243fccf7dc7260490eccf73ad001792e171bd4f9c84ec9607f1159ebc331bcc4f520cf0179b0c4f591e4b95cc8a41ed0f89c196cf70511599832d93490d8b18d0b0a0eeaf432da556a2e17328419c8f65338b0d8d9294c0d5074839ce4427f7be4fde52dd95e92fd13a06cbd0e269d2550c759121123d88cd355406f351b927db3effe610e1091481eed4206f99199306a52245b23cdd8fa1cd609085a7020cff00232eb74bb6637ecf050330cec5027e0215b67cb11ec0b087821bcc9082a12d4a2eb5af54842c0f427cf442f678f841cc0f43dea57a9d7a864099e219dd78cb5a0f45016dc482b198c31acea748b7e2307f2ac17180241e919eb349ffde8fd4116f10ce51e19f08b1d5c591e9d1c3dd71c9028e8d22952a903c9bcf41b61ced4d91d56bd7a0c
493034a4b3eac66b6ff6ca74705c1047131156c23c0435cce94533377bb3124f0977089730d9627d16d3fa11285766b2505754dd8961f5d41276caa185dd06c7a20ca9c24a2bf989120a7e09f7568d720115ca0288014142125b3c588b5ece5d55694f7ca13d1883641a33072a47018a407df8ea069a5c75bc10ebbe10cf573078cd7be8bdbbce32ec0e0a59175564e7572be11eb0e5e5837c51e55f3bc0c16c531dabac5d5b07fda042269b56689cd006cb209aae2876db00629b8ffb0d1a5600fad95aa3dd3d1368f83bfe3721d335a40cb41c1f2dc0d3d1045264c6ab35ffffe6e01bf825d6ebdc6681384d5a75d58b703c68f8f6271b775e03f056dc60c0813e5045123403ede275b86aebb58db9ef5e2d0bb1eba374ac580ac704fed768d824e80b48c04b26393d64de26e1eb0fb37454be2c568e18ebacfefc0a355c0d6834a4d89da032cbee6f6e37427e593d51df5c60e7ba17506a0557fa2c25bafc70e0ebf01305eb42be24640ecc866bee9ff456eb05681412fc362b08f0c56da44d4a03661d67015af7c705d4050dc8275493286c012ec2731a96828f04da5683e80f1f4b2214a6ea0d2dc766f59bdb8acaaf361b2774222dc9d2db61135e85ad8b010268a1dcb7b0c31d385253396557700aa82c757668f20318d663146d9bb5f63f2d72a319aa0dd005fb42c647683227242119fadad8a1e4c0c056b2ac68c8
650bbfd0eb259103265645472f0274e157e46a572e9b2b48873a4376dca1ba6f80742f2df13c74a21478ee5bde3c6a01567c85740455c65aec70560d2007123c7c2ecce6c25ce47a0bd65aa02306692ad588a42e033ec58e7158a4b70c68f3cdce1450d64644eba450e6733580c50f48dc5ed3b0ef10cb34b8b8925050cfe6a8ae11388f8d307e03ce0053add34c01f6f631a05368682000cc1c2a6a2b30c6d2d0df53066a126a66c02ccac1c2992f625c702e9c60a009e73be983c25fdc54240cb811c7563bc8ebdbe682e187341edf9901e90f08c39aee6c0dcf4906a6079e8193a0371a183b08852303f6049b1d87a309f746ad50ade7e1a69b85aa20a154a13444de10dcee3485580c68f7383e1e0107e4f91868f96a657d4003861a9ff5f218877d875718c6050d10bc8635db775bd46a46ed501dd800de3f87ebe0833d67d76e27cc5ee441281e81d8d1beef2c53a80c4d3c7f7ea9e0be2ec069c0262d51c19959f7f950c1901396785394570447422842957397977fae85331308ffd6c1e8108d9d83b9a5049c994b5fb8010e3b59885db6b8539a5c761c0666aabcc93b14f02cbd71f2592bc1995be6f4f9fe8cb248e803743617645498d343f8f99a7c01686348976881a384366cb0b75454e42314cd6c1d75126b5844ee15fb760e890d143905b750b31a7fb99c1669368b59a85ac1d6ac67317409c9ba17424458
8a7a57ead073f0ecc03a0871f45a327552056cd7a2213f5068f62afa81437c3050896f1d6a243f9df554f1a0c8f5645cf806747eaa65b110b7523956e0591c0081cfc703752338ff05c6c678637f3505c15954ff2de3f7c7efebe76a3468b87998a4d4622616827e0788b844c08dc0c7328bc12d382184cdf0b30e4715746f8e693b176a710da5188772047239c7c8c841ae1c1432e60ecc61a56f611435982de11e92095ff0182cb84cbdd6e5ec4c68b10767e95530b08274e04b02800d2677816581856c7a5dd8581dc8c8b2ff5757572c566e2d218029e2a5fc2d2d3904565f530623c7c0e03758045338e4c8490ee2028b02ed016c3e9b5b1a4201c800e100746c04086e332d7194812a53c921e4e4e9e4e941181c76b23d5250514a2ca5dc7f196f44873bf0752abe54af438f569ba45d1a716f4d24c412063157821a850dac7e7187bd6583d7556875740f0d796e878e305588df288260b620b33024f14a2ee11dc44ff114848c80fdeb5168f12716841dc649911616f2296325e1b66cb7e0bf169ff8f5004277ac3dec732b3da053c9d7419c90b1eb595d9685f66c90f0d8d87ddc17fbd55b37e028a53ee40c0663fe67a16d410d17ecbff4c774268d8da8fbf5459aad1e51f80d50109bc184814428962783077db8050e415f39b36fe1084c75294724107b92cd4618e8893d38b4b37fa3628ef80881d6a38cdfb64b
c8433e0c5c85d700e4e9b3400c5209fa39390083c9106c676b4086641414687ce4ec35f768a3803919643818043ba7a3006273c6d9421ee4e90f50688651a41aca1ec4c09401edb610b423c0437d566f33db15e158b809fa4abe35742ef52cdec698dfa726205c1f95ab4699747c228cde55432718060b68a4a5f9550b193c68ef0bb9b488074b473d0e8972ddaa14e77668016816b94bbbf1395d2c86d4d367c956eeab6b5300fc9859be862dcb9c28f8681e20d49d251968295006fd36de4236bb54a51440fa4ef56c48564e44305c2a5623d94d300a9efda67af916a23b45f0597318332549165c97cd328053a74b04e95a039e36ec0e2f3248b363aeb10f8206003529c107aec192ef21cb592ff0cde21dea3e0bc06c68eba768c723c5616079b3637041468a93035364980b0e289499b6d239aa411884c353be3541563d83018490088bff7b86f790ff8a8d38db4108f0edffe4fc3acb741680f92f7405045c75068d2e62db0090fc8a05409ab7ddddecfc3818246a0c68d45e753e1090fc2c8245388c0c7190932d5572501ccc70db917031776a46a83d9409ee5992fdbf25a339fd78afd44b268907ed162c82cdb23cdf4a8008234dccbfe06a329dedfe780253bf06405321575f62d644058f0a7acd987ad905f974644c2f811b0409dc57a7532409d026903320f47e0bd90b5aec012b789dd6f7269bb9274779536e
468481e12001818a7fd033472df32853df3bf37d15e216b00df50e7e168f8ec1d8a3f6c87ce460ebb52ccbe6089f13b0b27816647bcf0f030d74789ce099b13070084c3b919b058a4bc97a62148ac7b36f7c001bc0ab485655f581af72572e74142d10ca147020c2d5ca9cc113ea0478852b10c959e902161d5feb913c6c0a9909b7bfe8138016915656b34962b5dbbd1156661c8768fb6676bbb509d55dea6a3b0368fe177198d902b47c1d0c0f4b082d1e535c181b6a38f142962cf953bd5505dfb2937955b8ebd114360bb6d85319ed1d2c2513c9edfbf4d999865c2276a5068200384038f085dbbe5a536031d948545df80238c0ae2c7bfa1d8d2cd4dd61d432688252475324e8a596c59f4b2168a0e3d8b6e2b5e0403bae15de575b4aaf3e961dc504eb385fc01b9f8022f11b2060ace4f622528683d73ccc2f5143600bf911edf3ea32914c641decf6894784011b4298f146185d94f7791dad64b2915f17fe0b5918cc006ad5f3fbd056099310251a0e8c4cf08c68dc37d7b02d8c16b4df71292df9ad41ead731d5a1077e4868eca0031da06a9e7143a8ee49d32d68e81a560275fb6d11640835e8077f83c07d90aa2de96a0d7850eb01560396181888fa1d73571c98fb1268dc42afd72539254728e168079dfd463d13b29cfda1a071b3e78c0a0d999805bf4741b0f81b513399f8c3dec53655d0140f7dd1abab0a15
98b5b6e117e0f40fbc0ac65efc6cbe204e8f0b50b32b1864608cd548709911fa097968c0e5f14c8d0e3d0bfe98bf350c4b8539d2a3e012947090530062e0262b9310e30b68e5c088d85495701134ad5355a33029380dcad847befd43762fb4a6807de05b751205e146750c1da52e5fe2457506e35d4a366a85ef62b0fd37d695817dfc20a14a01fa80c17ea947eb9c8b7d15c8c8149869504b8513f2c803047423478159e040b6522266ac9f6786e8f774682b7d362ba3a095a2474f23338b18c12d204702ce83cec21d055c53515708d0936842c0917a92cd7d9845f3efbeb0a53ce1842f661e059063026689c9879921694050724592ceb0a3a8a4dd9837eb0a1a15af35721e7462d989550b277d7a4bb6418c726d6890a61fd7a36490c94e1e1488780cc8410e8064de783172be836cde5988bbb3dd48ee9b1fa8682468602907b6d36139bb0c1eae591d8890b0581a1884799e17d2194c0004df44be6d57092a6a061f3c68300b9b64c819a4244dd8f68017c4db4d201968f28c5cffb4186c21f58f11d0680ca69a622fbad8e985929c6ed0d17a9350b3d713411810d0a8209f94e8e91277ebbd409ba7803dcf3e1db8076a83dfe88038960d8078016e8dc6ae10b4df9fc640010a4012cfdf3a21d552cea6eac705111c5ef477d82c1ef18c83393dccf51883d8c68e74d059d7a6f4a5ca5643bdce8e5231444fad665ff4
bd08b5bb105323b920db29560a5315670206b285a48f682604413a885c4e968c43338087290cca054ec084670a3002752480a1ad5cbebabf427daf89ab7c5894590a140e0209815cfc30a77c0b2c8458fc1c11f95de1c580bd08ad16551b301b02a6557b8b5509e2891d9459b1bf5006700bbfb21cffd368e474b5241e236bd7f4db01cc923dac86bee85dc4e2980e6050315e241f3623562e181b8d8424dd81f1040ad43224151c8d9acc010f9680b485173a073c4418e92f7503f686546f845cf2f2112a99d962ad2f0e8b9c19ed610732bc84a56759d6c0972a08423a182bc851c536313ba9e4db50ecb6e015019b4be242e6133cfcb281c4a3c360640304b1028fbbb5fb5418682326cb0986dc8e418319d5a6bc9dfc2ee0857c9cfce5fdca60ee03c9e4fdaabf9ca746643a072ce060593b9cfc16398019461ce4fd25f696cd08da331f16cb1c2b5b2dd922c96ed87b0b8f3e12301a563ac08488ba9f12e4640558ea32e4fd6cb6920b0c2b6cb10963a75765df67676c292270e00f879c4c268186041826399497911392f044507610951e9e80a4a7bfebc78413930cd10c4494cdae2404134eac189fe07b30210c750d5ed3d9e00f142d235068a7fa66e5a4027cee2392e792c9229410cbb99221b990a88c21b9922152889221b9922f840c4cc921b980e9007c905cc990c678c9905cc9a3748086ec845c7074611e6c
4286e4424268f04286ec237f6485f6a148f178364226e9912bcd3e6a0a563687b119cfa0656a5656b61fbee229783701b99869ec7bcf46ab2bd5a056007ca3930d0e56ffa82617c490ea80c25561d5b36d54ec75a405c2724873c663d53ffdfd64f3146f5222e848a7bea5e2251496401656059a1da11aeb16325d9c80ef1493e4a0744487a055ab65bdd02f2e25850de27413abfc515065109dbbf338603f57e8139080d38b72431bdb88ace26851b7bcd4193924fcfba0221aa825d75ec93a030de6418410911a88ace20761fae0774f113fea7b68d077328ca07c705b08babfc48d55f85268f0158bbf1114c18a85a05263fd862863140f7608ecb063cd4d228629fd0b0c6f2976232436128b3c9539b6cd8d6d675215181125082a0851996d6608799004d02a88cbabf4dea4d8ecc3568e7068c40d6a46345c025ea39a545d340caaf37fb18be20831218e3ceb0689a81bc46307ff4004be30587518d4315f8b009b04db82c50773066380882dc3dc09f1890eee4e3533a02ac06e41c30000ffffff7f963007772c610eeeba51099919c46d078ff46a7035a563e9a395649e3288ffffffffdb0ea4b8dc791ee9d5e088d9d2972b4cb609bd7cb17e072db8e7911dbf906410ffffffffb71df220b06a4871b9f3de41be847dd4da1aebe4dd6d51b5d4f4c785d3835698ffffffff6c13c0a86b647af962fdecc9658a4f5c01
14d96c0663633d0ffaf50d088dc820a5feffff6e3b5e10694ce44160d5727167a2d1e4033c47d4044bfd69ffffffffd26bb50aa5faa8b5356c98b242d6c9bbdb40f9bcace36cd832755cdf45cf0dd6ffffffffdc593dd1abac30d9263a00de518051d7c81661d0bfb5f4b42123c4b3569995baffffffffcf0fa5bdb89eb802280888055fb2d90cc624e90bb1877c6f2f114c6858ab1d61ffffffffc13d2d66b69041dc760671db01bc20d2982a10d5ef8985b1711fb5b606a5e4bfffffffff9f33d4b8e8a2c9077834f9000f8ea8099618980ee1bb0d6a7f2d3d6d08976c64ffffffff91015c63e6f4516b6b62616c1cd83065854e0062f2ed95066c7ba5011bc1f408ffffffff8257c40ff5c6d9b06550e9b712eab8be8b7c88b9fcdf1ddd62492dda15f37cd3ffffc2ff8c654cd4fb5861b24dce2c3a7400bca3e230bbd441a5df4ad795ffffffb7d861c4d1a4fbf4d6d36ae96943fcd96e34468867add0b860da732d04ffadfeff44e51d03335f4c0aaac97c0ddd3c71aeaa41022710100bbeffffffff86200cc925b56857b3856f2009d466b99fe461ce0ef9de5e98c9d9292298d0b0ffffffffb4a8d7c7173db359810db42e3b5cbdb7ad6cbac02083b8edb6b3bf9a0ce2b603ffa5feff9ad2b1743947d5eaaf77d29d1526dba116dc73120b63e384ffffffff3b64943e6a6d0da85a6a7a0bcf0ee49dff099327ae000a
b19e077d44930ff0d2ffffffffa3088768f2011efec206695d5762f7cb67658071366c19e7066b6e761bd4fee0ffff5b10405a7ada10cc4add676fdfb9f9f9efbe8e43ffffffffbeb717d58eb060e8a3d6d67e93d1a1c4c2d83852f2df4ff167bbd16757bca6ddffffffff06b53f4b36b248da2b0dd84c1b0aaff64a0336607a0441c3ef60df55df67a8efffffffff8e6e3179be69468cb361cb1a8366bca0d26f2536e2685295770ccc03470bbbb95feaffff1602222f260555be3bbac5280bbdb2925ab42bf1b35ca7ffffffffffd7c231cfd0b58b9ed92c1daede5bb0c2649b26f263ec9ca36a750a936d02a906ffffffff099c3f360eeb8567077213570005824abf95147ab8e2ae2bb17b381bb60c9b8effffffffd2920dbed5e5b7efdc7c21dfdb0bd4d2d38642e2d4f1f8b3dd686e83da1fcd16ffffffffbe815b26b9f6e177b06f7747b718e65a0888706a0fffca3b06665c0b0111ff9effffffff658f69ae62f8d3ff6b6145cf6c1678e20aa0eed20dd75483044ec2b303396126ffffffff67a7f71660d04d476949db776e3e4a6ad1aedc5ad6d9660bdf40f03bd83753aeffffffffbca9c59ebbde7fcfb247e9ffb5301cf2bdbd8ac2baca3093b353a6a3b4240536ffffffffd0ba9306d7cd2957de54bf67d9232e7a66b3b84a61c4021b685d942b6f2a37bea66bfeff0bb4a18e0cc31bdf055a8def022d101103
129aae699a0008070917060a699aa669050b040c03a4699aa60d020e010f7befbd17232f3b474f4bf3bdf7de4743372b73134dd3345d177f1b1f232b33344dd3343b43536373ef344dd383a3c3e378326443600001030243322443030405b249332400704fef2d6159ff43efeb9aa669ba193f21314161816976dd69c1408103010203a6699aa60406080c10b0a6699a1820304060e338b291add3c3063021094b9fa3a74106f996ab030b0cb3b167930d6b140207c00e20210fd946ee0f0b0100b6a20a905e010809440045cb2ba2312e33235db49c5f0560780150a66b9ab300071054731f523b3bc8c91f0070300040c01f196490a6500a60208305a306a0c83f800d32c82040e00637c820831f5818907f32c8204d533b78383248d30cd0511168c820830c28b00820830c328848f0d30c36c80454071455830c3258e37f2b74340c32c820c80d6432c8208324a804c920830c8444e806196cb29f5c1f1c980619a46954537c3c061b8441d89f17ff6c196490412cb80c649041068c4cf89041061903521241061964a323720619649032c40b621964904122a402649041068242e490410619075a1a4106196494437a061964903ad4136a196490412ab40a649041068a4af49a410619055616c0904106690033764106196436cc0f061964906626ac06196490418646ec64904106095e1e904106199c637ec10619643e
dc1b1f061b64906e2ebc0f0e1a6490c11f8e4efc41066148ff51ff11904186a483ff713190418664c2612141061964a201814186649041e259418664901992794186649039d2690619649029b209898664904149f2554236bd411517ff0201480619e47535ca410619646525aa06196490058545ea061964485d1d9a061964487d3dda061964486d2dba196490410d8d4d19644806fa531319644806c3733319644806c6632364904106a603836448061943e65b644806191b967b644806193bd66b904106192bb60b480619648b4bf6640819645717776448061937ce679041061927ae07480619648747ee480619645f1f9e480619647f3fdecd06196c6f1f2fbe0f31c860939f8f1f4ffe0c254325ffc1a1c9503294e191940c2543d1b150325432f1c90c2543c9a9e999c9503294d9b925432543f9c55032940ca5e50c2543c995d5b532543294f5cd2543c950aded5032940c9ddd432543c9bdfdc332940c25a3e32543c95093d35432940cb3f343c95032cbabeb32940c259bdb2543c950bbfb940c2543c7a743c95032e797d783c40c25b78692a192f7cfaf6428194aef9f4a8692a1dfbfc73be91bff7f059f5707ef74ee69ba0f115b10df0f05d334cbd3590455415dce3dddd9403f030f5802af0f349d7b9a215c209f0f095af634cdf2085681c0607f21830c720281197272c8c91807066127879c1c6004033172
c8c921300d0c0da15872c1afa669baefc3670f031f3f7f9aa6699eff0103070f1f54b3a6693f7fff3fdfecf27351031ca208a20b03e0d78b6796a19c009fa302ffffdf6aef6f7273742070726f6772616d2065766572b5d6de6eff467265654578741063741d206973160cfcdffe7f696e672074686520636f6d7022737365642066696c6573b1b53bb76e172761726306422eafbdb5f6af651831112064692a47e666c3b679226f4836102987bd77d9455a46149f136fb743d66e565061588f4572ac7200b33bf7ca416e08bab46e747381d775db61c866076da76e58755b7f63e17e3a0a0a20257302135c9f6f66746fbbedb777af655c4d69632b730d5c5728646f77addbbaf6735c43755851565f73825c797bbbddda706c670c5c53fa6c6ca56f6c6419cf8dfc846a2e74744667106623e06e5e7973ee76508825ffd6c6d344136f4f46545741524508b6ab48e73a5c3e2e42b04b1325d939610ab7366047493b3a6e65553ffcb7c9a951f3636b204c61756e63eb4170701bfbb6854447614371176c1625afacdd0ef9637572251f7267430efe66ee6e530b747570cf08204661766ed858db5a6968e7660c2bb1827b6fb964546ffb089e4bc259d7b644186b0a3f6408250ff336365bcb576d307525003de186c32493944d532063446cb3a6c263c2546168c56465d7dbda850b176f757b6e27e8f965cfcb68b746870e
74700261cab0301e772bf0315a3b6f2b76312e348972ffc16ddbb774373a2f2f77002e3b73c97db72dc786cb2ed6b1413f2079688d606f5820737508241cb69ba9cdbaf06b836e666f6d98608ac50ea30f3aeeb542c0e9d6f8a7249dcd666fea2e2043ae3f0b44add09e862bdf43e6c9133423606c6820f967f759736ca37222fafb206e6f54d28c49b123893a4f6d1d09294bec2e0000b6e9bd8137a227ad666f94859f02d254b0e36c696b7ad8a150b679516ff10e3b3dd89b1bb4085346583b4974273ba1bd91306b746f2688d60aef050bdfc376ebb5e78cc23ae76f2424626b94ae6b440931ff530e1bc0354763742fab060d05c150de1f55524caf06c78b1ff6176f5646414c53bbf86d8d18051d6f90a9748dafd0622b23575f4e6b6eb6b99f65556e6e09bc41e92b68aef7ef6275671f6f4755493ba78a1f0fcddf635a697053697a61b877dd20257c4478a23257450f3d6ced2180731258651a2e84c712824b7d1badefae24302a7220626524afb5096f086d6f76671875374df6dec8757052190f8073e0b5d8a17fb92824d71d66384c057381966242ed84ed6fff75be00f1895af75bb44035786d6c00740368042bbbd6d6078f000e036466bac2d6685b1b6c5bae07623c740057b62f6f3fdf84918c916bdc53610069b01c74042e0572b477ba0a4265482026ffd11a984518383261e2b0b5c6edf30576614b4f
ad61fc7ffb8f29723a200a2f205c20062a203f2022203c203e207c2014518414092a2002a3d9581514c8aaa80029d23fbf349f0100ffff104008c880084b01decb068a9100080b014d20cd2d50740368d36c010bfe65c0ba4405675110503700c00018016203eb5ef5030055821b1f2be93a28094f1ffe28362a7e6450a6413222bac7969de81f80003c67420699b237dd63006b2bd8ebca9abb414e997800743e2bd70d72721201ed43536e55d134873dcba3300b0dd96cbb11f50201f5f7a31f516c454c1d0df245b4902de1f91f77ad15b13def5816bc6f3f6c737793fb174600726901459f84d0756f72cf076f09208783d07d930040670387c177fb02214f8bbd5075c3d10d605dd5fdf1a3801f763681bd5051d0008dc3001fa003d23db000cfe3f2400425796c040e0497140188ff30778593404b5b13011600f803d95cd77d4d43296f01732f3774ba6e73dd07661135640b720d79745fa46b7507771720056909a13b7307336f05654f3df3bd6149554d690dfb5cd79d2d2e07491520172b6413eb66cd991d6e744d1b730b72f798ac2c2563270d6200652cc84d3772b161650064ab0cf610bb2ee55f81f329c796d0dc0e00ea81e51be100d7f1718c3b8fec038d26e372431541be5877bb2703d8600d1de19c87f6274381f1be370befb1cd691916027b935779c1ed0006422e61df03009f1b644215501da5731b
3b732377098577af6cbbb06d2424a7096ed55f6cac9b61c9630d762b0f65497a4809f78b2e439ffbe67bc38f2d0c00ef036d49256cddd80877075f00706767477301336617f60c853215314249919bc9db240800faaf163e545c801bc0903e22c7e7168027cbad0041334989a40f1574ffd521d2f7186df95305c176db6fd9008f2800d39e333227d05cb690b71f182b7ba6e3cc871f430d704bdf8d6d6e6c688da903f530012d37d67583093113417b64a977b35e6ca4812177743599f746ee3ef363516e007462dfc724b23abab32ea50ae9dfb26027d3662151a1658737c208f973317275d170dd4f086b03508f0054a70e0b608f8a13c34fb5242f59e7611f49b7f18d6c06ba745f3a9101772e92f1de3b2d6bc37943b3e926fbdb6d4e287fa33801c993dfacf503120b09007d0f15ffc6c3c6008482841784038230f75cec7ba4020843004476c82332222327f208ac838c8300844461ef205b27809ea7e441e65200a783830e1072824444222240c8014244442507083922224444aa6c65572200a72ab0016c440043a7ecad02f9222228a9a7b6b2d3aaeda9a7ca862c55b243a7652b3baceda9a751d9ca56a9a78bb295bd97a9a70659207b96a9a7215b6107d0a7a91bb209b023a7847f8494a36c6529a9a7605376647f339333a92b5b655d3085a74a60207b12a9802d5b590abb23a7a9a76c853c024444a9d95b65
09a77f10eb4ed81b0ea927770072292be411d8a7444402837d67a94753741a802d43482f04a736330281b365a7d3302d6c641336a743d1a7424e81bc44442222c0dcb297a9a7a9a7cf0709a431777778378b30186b071d407d75b70542d74b7388008764826cac071d20a7446408eca553a72307811c20438333b2043608e033a8a70596840b84e0a72abbc0580128a7b0651316a94317a7f60283ab32792832a705d2b9105aa732ec5c2039838349588888cb5260cf36a7a93db7300e67a0f1f8d8c0ba21a0372098a7b061c956a908a758f686bc888838a7c15242482637a711d6859238392097ab2c65b30fb8a7662323c9838378a7616f9612602893a747d21248e72144c2de1b61a70793a7802d12ba800ca700791532324444447660a57e28a72f9207c83383834444ef254b2010a700ec9902068aea0ea7902b7b81a9a72219852c9700927f842d0c584ba70e56d91b53a777224b96922310a72403803c44443262cf15322232734e62b309c0a7432a9d023995cd58a72832213b02998843af43c200b043a758231bd901124733a74650f61299a709a7651029c883968a464805b2a70fac252424ef459208e441b6a783834861af434592a76055b6123ba72417b63248a7844c64c9466aa7322e0be6863fa70078836cd9c8a76ba73bb0659384005373a72547720a3832322107616f00a7842513c8143833235b6013
8310a754d8a46076f4a7d2ca5eb02ca700f45bb62a1ba700a708399221332200394806448403c904f2383823228b00322032874aa6b22b84a74426209b34f4a743269089403223c92919423383835b60bc513f9aa79c21902b2838e42a4b609aa743e402990833235c81bc823838289008249733370b64c88644a7845d21b19243372246240794a784b265812436b8a74032913c83838372403661f2a73250d98c149aa70239421e383833b009a9e438f2a765ac9283839aae4aa6b2a722843902e448442356813c0a8383950c6113f2a732d98c0132229aa7840c241343444a1ec844833838362089c9dea7150aec2e58723838a7902bb00b888383a73392a9902128430a1948a6432286e428b983448461279a4a778ba76521240636a72ab043bd009fa70a99a0e44483a3e488e4843881cde0922378b1a7c851c9137384443225532543284300c91432433342a805c933a55928ecb512a70f02092c1ba7626c4012cba736a7a96428b944238be4a890288457480392324830066c4012dea7443c42ae482222220e9203844444284b422d4221a70506ad0076bd4148bca7de215c65b3911318a784ad906895c895ad42e2a7dea8039298b0a930365c646f65a82da732ad0c6295100a835895a710940d48e280dea9ac42062ca78399b2901094a738c4041620a93059281b90dea9a7b6328854baa724ae6c55a980deb6ca5e
20a7a9250a6c85c1308ea7620b09401ca73365a1e4ca8622a9a540a20d20643606ee2a9ba7a940a78eec8db255a7a9a7abecc082cea9f612807ca73223a7c016255743a959822c5ea786a9402ec002a7444b22250f8383de2c84ad923300a732941c20838323240e3940383844def9565882a9a722843c0702383344a479413243838322cc0d4aa632a9a732851c2083802848267944383083990a19198444382a5b5920a8a7866c95b598a7332942064032099021904433122990913822de45b6845550a70d4858322250047210b2a7844496b0ca608e50b09085caa77ca7908990833228086464228443990819993823430b84402843a7b72c95c4dea7a9c05e20b0a726a96801d22da7228955b6a43fa7de5b59d12a7ea765216422228d42d9326058a7235a25cc7c4f874a5a652ba7320a5b49b002a7802db21a435ca7087bcb42a8a9a748aa96b0f8b8f6ca9225a78f8838a959912c44a7da40b6ca56a9a7b6ca56c23850a9a710d9b3508e4883a9124c2eb0a78203bd50d85423a78402994b84a918a7b341d81250a933c8171c61a788574844b6b00414a76c490b2ca990a7896c95b050a95d09b36511a7c08205a16c2549a9650bb28067a7a908cb0e2c0e40a7502b9bb241a922a7c896125e96f8a9887823b015a7a991054202b7a76153646f84a94090085b911d22a9a7f64661e788a90ea74189b055a9c25e207b
25a772a95d0a9b8d0ea7a9226cc842202533a7b5007b8180a9b2a5b0949fa7a988940c6a110929b20296ef2ff61a16b28321a783a983a1e4412888288f15d9920984a96c5921893783a90c8100abc7884204ca92a90f83b0241732a925d08eb088b830a5d414c683b0a98884b689b065c58057a984560c6433305750907495ada98460b295042fa79ba9621b24a560a788cb56b64a25a910d92a0378a92fa98b2eb256b430266c42950a1ba960a3842b80c4a99748204892a71224b18450ce40be60caa980ec82aeb090059da9302255b6326da96114b6a4f7a9569d5218c0d749a5305885a9b285c296043fa925410bec29a9e71512a1b0a95c44d8b2a27fa9a216d8a4f7a92c12614bc7a9066c09008307a936c8e2202806a71689906ea988b05c85bd44a7a98815f61259b0a7a9b2b748685aa7a9bd4442015aa704f64681a9a7a9803c0472882083830406104b182f092d406a1e32896c655788a7a9ec8dc06614a9a7a9a5c066a912a90442a9eca7a9dc5e2a5b7684a7a90ba3ec05c2a9a7a9b237087b0da9a7a9e220ec8d0aa9205920b9402388a90049045340860aec85b2a9a7a9ec28b037a7a98068c4c856d9a9b82123444607a740be006b9ba9308383822c8025baa709b095d4b6a98190ca66a7a96480d4826ca7422a7bab2ca9a7a98c8814c60e0781b059888ea944864b65a798b8d9a28c8807a9a9b020560f
05f64660a9a7a96f14d81ba7a9a781bd5160a9a7a91b05f646a7a9a7606f14d8a9a7a94681bd51a7a9d81b05f6a7a9a7a951606f14a7a9f64681bda7a9a7a914d81b05a7a9bd51606fa7a9a705f64681a9a7a946811d1a2fa9a3516087d7a97f026c14d8a9a7f65681bc3838a9a7ab405e05383820af027ba9a738385781bd55a9a738c0de2a9038a9a76015c8ab3838905c05763fa73030881615afdf6789682b2f2a08000038ef204ac9070093e1c7bd00ffedffffcdac9e00a7887c008c625a008b6158006b423b3505050020161400a5ff2dbefd7c7400845c53134a3e004f4000d9c3ba00513629ffffffff00b09b9100745747007a685e00ccb7ac0090786700f5f3f100fcfbfa00f9f8f77f6bffff00e4e1dd00dcd8d200f0eeeb00e9e7e4171800fafaf900fe7c675f6e0000fd00ce715f84330333b3354dd36699ccff17664dd3344d666666666699344dd3349999999999d3344dd3cccccccccc36db744dccff0fffffcc6a0137fb4c3766039913ff7e8393cd344d33333392ffd94cd37483333333a6ffcd344d3783333333ba4cd37493ff83333333a6e99cd9ceff13873333e9de9b6cdeff89018b66a6e9269bf2ff8b66669aa6699a6666666666669a6eb26016cc876666a673a66966661a0333334dd37413ccccffff839bd06ca66601ffcc4267dc7bba99010f003391079ce94ef80fff6a66133374a6e94c
d70317337b99995ca4699a99999999176b9aa6e97f999999999917105930dd8399b6ff3bbc77988901815bca1364c199a6333387daff70a773df87661787ccd8344dd345ee8bccccccccd3344dd3cccccccccc3af7344dccccccff178bccee34dd69cc87ffff839aa6699affffffffffff699aa669ffccffffcca6699aa6ffffffffff9aa6699affffffffffffa6eb9a69ff6e43ff13ffbd679e86ffa36666635b97dba5ed23072181005f000077008672b95c2e009600cb00b200d72e97cbe500dd00e300ea00f100f8a9fdb65473fb3ba4a0a0008000e5bd37e9de0206ff0112259757d6041b00191a191bd9dfa76c2b1c14180f0018141c2bdbdede4629120310000b10032d192bfb375d22291304040b0e0e1013122dfedd43d82b19170d290b04090f090a060edb76d9ca2d2b19530f2a0ad973efb713290202030a0b2c2d892b67aebd4d17a71311041b292b066d17f6bd0b2db71c2b1cfc11ccdc6cb6a609292b2c2d9b7d0333122b2a11532b64efd72d2c06890c13592bf742f6dc1955a72b002d08f903760b0f1a2b1a0f10110f0247c809f90e131811130604761821dd2bec30e12d280909035f0683867b638129032c2b11061b618f162b530f913db7ed050304270a002d2bf65e36c3175e2909120d0d2c2b842fd6ba2804002d2b1c33b936569e140d112901002c1b612cb657132a2d0bba1a2b369bfd38145e
0a03292c2ba35b385b2c0c7d172b1c34f77833c733292a6edab5d65e3259852d5a2bf84bb6add60c04290103110c0e08bbed256bd910862d2c1a2b100bbdd70e2b2a127f2900399e9bac1d2d04872b62f7350fbb2c13022913100700085bfb5cf7bd882a0c831cdb59138493b3e7dea854282c08af9de0662adb89105954dcefc0be0b7e072c10dc0c1055305798cd2b2c827f2bb0b92d16b37009162a17b209a584b7941dad6a053769089b00d3d7de105a424329d150bde036ced8131912004404121a612798922b6e490a2cb10831be53031f9b2c2e367bb3002aee2c2d543977788f041f2a9ece2ccd8610c12b617588730c21b22903a1202cf796b57719002a492b5ba81b13863d7d2a799800c6c01e1d9013762c4aee46976c2a8409fc031783be80d11a928f2a2c11d0fa58f62b29870d7d5897ec9d8b125a2db113ab25a9b9730d66296facc3b1ac5a2ddd790d29195ed6b903fb141a8dd6e00e9c2d0aac1f580229d20d093c0d1719531c2d11ba05032e54de340fa704f40610e82914d71ce40633e78e89171cc18da72f6e30ba05220f541a081e043278a787b042197cb3380819df217bb325061c63082b513f457d0090ef419c262baa7749df2e01b7196946ea0283204001b6406a160687009c3c7b1003fc0307fe0fd334cdf2ff1f173777af69ba6e67fb8bdb03dad8c09b21906f650300ff1e3979d9f80103
f000e07fffc099419a41800010429aee8b272f07033fff1a1a26e1f900022220330100618a6a346db21405645409b219a000c44060e8f6cbff016c7374726361744147657453790d656d6d6743aa1c1454117040cd0c15db0d5534b0092d23b5eeed6ff7616e64456e76136f6e6d1c53522a720b153e731aa82864916e50fc6cdb6d4b41742362751b736446756c6c7ee7a8e83711497342616452bdffaddd3b6450260d4d6170566965774f6639eb5cf786540b18706930530cdbee33776570065d54696d274c6fd86c73bfec610e546f18446f7344411613807223867a0761a9a24966411b6d707b312a32a62aaee66e6e3b50bc7641066f6613ef256c036c194874446cfb9bbd3edf114d6f64f3651316ba202c5c3c63187342785454893eb753456d576862086169706f6e6bea135378414f626a2190866043193eaf53beb9df264d777469427974fc4e6465436883e142d7f33a6c146548c76185d7babf8a6f6d6d0d4c5f9b7e93b0190c164c44656450b6cc01d46f69f9cc3e83f00e6f2eb922427054513b08f84c610db76bcd8d3801ba5e36a6616731707bcbb6d469f6343c6e415c15bd81eafb154cd63d3002546806649d413bcd466b4240dad10085acb5767b675175a079561a066097c3eeaa6867d44b65790e38cde662f60f36de105b8c214ceb5214566b5ec3c3610f53cb781e6cc6821bc1ed0d426bfc156c
20891b2ea016990264f166330e7b071f0b6b48f6cd330d6368426c2c717006ef63b7521446444359524f5032a4cc663b6c2f61e4a2e8886101a856de2a496e69d73205b7cd9c0d4013732942cd96ac7c1f5521dbf2f4430ca03e5348d07edc170128d9894897b7702b9c7bdd814944f1739fa6c70ba8096aa385d269b6417afe0454711650b30bb66e75ce29bb6007d749311110726f676a6fd74abc62817473476f1c1f11826963698adb191d37755363476e9e6c2a7346fb456a533d5a49630bb81d2fd86c34591ac973da6e366bb61c0b516869c5e310e3cd29bd92c1406df684b76f79a6c075620b1fdbac0d8c737336ae0a558eec30c3706440a753d842be58a135281a225c8a42166cb6d1dabd704216f89850da0e6f780f72cf0c09cfe078c752160d0aa55cb061b328258d37b5b646eb0c541368a37080c9618eefae5d610d95475d18962ce11ad17072edb981d0856639eb0d0abf1c8364f02a50454c0104004d9ae53fe4fab63ae0001f010b01065afad9d9344f733c1070000bc98210c10208073b73496e0c80011e341007b6b36403060078078c05187179f05886b9bc42a86ea798432e74960db633ca07a058905ac43abe8c0808602e7209610be90ed9fbc012275e4eaacfba40022e2600305f211ab96e53f62f27c04f7372945b49d39debf04f888a3b00c09f5bbc247af97501000100000000000020ff00
60be004041008dbe00d0feff5783cdffeb109090909090908a064688074701db75078b1e83eefc11db72edb80100000001db75078b1e83eefc11db11c001db73ef75098b1e83eefc11db73e431c983e803720dc1e0088a064683f0ff747489c501db75078b1e83eefc11db11c901db75078b1e83eefc11db11c975204101db75078b1e83eefc11db11c901db73ef75098b1e83eefc11db73e483c10281fd00f3ffff83d1018d142f83fdfc760f8a02428807474975f7e963ffffff908b0283c204890783c70483e90477f101cfe94cffffff5e89f7b9560100008a07472ce83c0177f7803f0275f28b078a5f0466c1e808c1c01086c429f880ebe801f0890783c70589d8e2d98dbe007001008b0709c0743c8b5f048d8430bc9b010001f35083c708ff96489c0100958a074708c074dc89f95748f2ae55ff964c9c010009c07407890383c304ebe1ff96509c010061e91fa9feff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000007000100000048000080020000008800008003000000f8000080040000003801008005000000780100800c000000780200800e000000b8020080000000000000000000000000000001000100000060000080000000000000000000000000000001000904000078000000486c01003401000000000000000000000000000000000000000000000000020067000000a80000807e000000d00000800000000000000000000000000000010009040000c000000018fa0000ae66000000000000000000000000000000000000000000000000010009040000e8000000c8600100600b00000000000000000000000000000000000000000000000001000200000010010080000000000000000000000000000001000904000028010000fca20100a80800000000000000000000000000000000000000000000000001007100000050010080000000000000000000000000000001000904000068010000286c01002000000000000000000000000000000000000000000000000000050066000000b001008068000000d801008069000000000200806a0000002802008081000000500200800000000000000000000000000000010009040000c801000000f300008201000000000000000000000000000000000000000000000000010009040000f001000088f40000a00000000000000000000000
00000000000000000000000000000100090400001802000028f50000dc010000000000000000000000000000000000000000000000000100090400004002000008f700002a010000000000000000000000000000000000000000000000000100090400006802000038f80000dc0100000000000000000000000000000000000000000000000001007b000000900200800000000000000000000000000000010009040000a8020000806d01001400000000000000000000000000000000000000000000000000010065000000d00200800000000000000000000000000000010009040000e8020000a8ab0100140000000000000000000000986d0100280000002000000040000000010008000000000080040000000000000000000000000000000000000000000039383900292c29003138390042414200100c0800292018004a30290010101000181c180063595200524d42002928290042454200101410003124210063494200845d520073554a004a383100423c3900393c390052454200c6b2a500d6b6b500bda69c009c8a840052300000634939007b5952008c655a008c6152007b594a004230290052494a00dec3bd00e7c3bd00debeb500ceb2ad00ad9a94004a4d4a0008101000211c1800523c39008c615a005a4539006b615a00dec7bd00e7c7bd00d6b6ad00ad9694004a494a005238310073514a0094695a00
846152008c757300d6baad00dec3b50094867b0031303100291c18004a3429008c696300b5968c00d6beb50018181800212021005a3c310084615a00845952009c756b00ceaaa500debab5006b6d6b0029202100734d42008c695a007b554a006345390052343100a5867b00c6a29c00d6b2ad00d6bab5008c7d7b0052595a0031201800211410002918100039242100ad8a8400c6a69c00debebd00b59e9400524d4a00949694006b49420063413900392821001814100018101000291818004a343100946d6300b5928c00ceaea50073656300634d4a007b514a00422c2900080808000804000010080800211818005a41390094797300bd9a8c008c797300292421006b656b0031282100b5a29c0094655a0000040000180c10006b45420021201800e7cfc600e7cbc600d6aea50094716b005a454200181810000808000042342900bda29c0052494200080c080021242100635d5a0039343100100c1000b59e9c00393029005a4942008c6552006b514200947d7b00c6aaa500101008007b6563008c5d5200b58e84006b4d4200846d6300bd9a94009c696300945d5200bd968c00a5756b00cea69c007b4d4a007349420031343100846d6b00ceaa9c00bd928c0094615a00c69e94009c716b00a5716b006b453900c6a29400b58a84008c595200ad7d73007b655a004a3c3900bd9e9400d6b2a5002118100029282100
63514a009c7d7300d6aaa500735d5200ad8e840052342900312c290063555200312021004238310094756b006b4139005a3831008c716b00ffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070707070707070707070707070701b1b1b1b70707070707070707070707070707070707070707070707070701b1b1bc4121b1b1b707070707070707070707070707070707070707070701b1b1bc19ca11eac441b1b1b7070707070707070707070707070707070700d1bbe5bada682a61e4cac4f501b1b1b70707070707070707070707070700d1bba5bada682a6a6a11e9aac9a9a4fbc1b1b0d70707070707070707070701bb7b89c4882a6a6a6a6a11e9aacacac9a4c4f6e1b0e707070707070707070a4b29cb482a6a6a6a6a6a6a11e9aacacacacac4c4c441b1b
70707070707070700db14848a6a6a6a6a6a6a6a11e9aacacacacacac7eac631b70707070707070700d9ba1a6a1a648a1a1a6a6a11e9aacacacacacac7eac5a1b70707070707070700da5ada1a6489998b0a1a6a1459aac9a9aacacac7e4f5a1b70707070707070700da552a69f7b7ba947a048a11e9a9a201d129a7eacac5a1b70707070707070700d9b9c9d9e9f2525257ba0a13f4c1d451e9211a24c4f5a1b7070707070707070159798994125232323417b996811921e1e1e1e37119a631b707070707070701b1b945d242323232323244195741e1e1e1e1e1e1e1e112d1b05157070700d1b6b418123242323232323418f5f8d9112111e1e1e1e1e922093670570701b1b5e2424242323232323257a8c8d2a642a0f2b20371e1e1e1e1e1e111d050e701b1b1981242323232448760b8b0e0505050e2a3e9a11921e1e1e1e37790e7070701b1b7a81248182c184790905707070700505643d6720921e1e37870e70707070700d1b7a315b7b44773d5805707070700505057d3d7e1e1e11137770707070707070706c47924e9a6e0f640570700505642a77632d74754076777070707070707070709a1e92111d124f63640505643d6367126840482541196b1b707070707070703e371e1e1e1111204c2b0f583d5a443f5b5231255d23415e5f1b70707070701b4c4d1e1e921e3737114e4f3e504551523125232323232341
555f1b7070701b441e1e1e1e1e1e1e453711111147482525252323232324242424263b1b701b3d3e121e361e1e1e1e1e1e92113f4041233a2323232323242323263b1b1b7070701b1b2b121e361e1e1e1e1e37123839232323242324243a263b1b1b707070707070281b1b2b201e1e1e1e1e112d2e2623232424242331321b1b707070707070707070700d1b1b1c1d1e921d201b1b192324232526271b1b70707070707070707070707070700d1b1b1011121b70701b1718191a1b1b7070707070707070707070707070707070700d1b1b1b707070701b0b1b1b70707070707070707070707070707070707070707070157070707070701b7070707070707070707070707070707070707070707070707070707070707070707070707070707070707070fffc3ffffff00fffffc003ffff0000fffc00003ff800001ff000000ff000000ff000000ff000000ff000000ff000000ff000000fe0000003800000010000000080000001c0000003e0000007f000000ff8000007f0000003e0000001c000000080000000e0000003f000000ffc00003fff0180ffffc3c3fffff7efffffffffff407601000000010001002020000001000800a8080000020000000000000000000000000080ac010048ac01000000000000000000000000008dac010058ac01000000000000000000000000009aac010060ac01000000000000000000
00000000a4ac010068ac0100000000000000000000000000aeac010070ac0100000000000000000000000000baac010078ac01000000000000000000000000000000000000000000c4ac0100d2ac0100e2ac010000000000f0ac010000000000feac01000000000008ad01000000000016ad01000000000026ad0100000000004b45524e454c33322e444c4c0041445641504933322e646c6c0047444933322e646c6c006f6c6533322e646c6c005348454c4c33322e646c6c005553455233322e646c6c00004c6f61644c69627261727941000047657450726f634164647265737300004578697450726f63657373000000526567436c6f73654b6579000000536574524f5032000000436f496e697469616c697a6500005368656c6c4578656375746541000000476574444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
} # 25.5 KB, hexdump via `od -A n -v -t x1 -w512`
# Ruby shame
def to_i(bool)
bool == true ? 1 : 0
end
end #class FPM::Package::Exe
|
Added lib/fpm/package/filter_appdata.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#
# api: fpm
# title: AppData/AppStream
# description: Generates a pkg.appdata.xml for distribution package managers
# type: template
# depends: erb
# category: meta
# doc: http://en.wikipedia.org/wiki/AppStream, http://people.freedesktop.org/~hughsient/appdata/
# version: 0.2
#
# Creates a /usr/share/appdata/PKGNAME.appdata.xml file for consumption by
# distribution package managers.
#
# → The point of which is to embed a shared screenshot and lookup user
# reviews/ratings. (At least should benefit appcenter listings.)
# → Only use this filter (-u appdata) if you're not already including a
# custom appdata.xml file.
# → See also the advised description style in the AppData spec.
# → Primarily meant for desktop applications.
#
# This plugin will write to the default usr/share/appdata/ location in the staging
# path regardless of --prefix.
#
# BUGS:
# - Does not yet escape XML properly.
# - Doesn't split up description into <p> and <ul> sections (or store lang=).
# - Stub screenshot used, we might need a new --screenshot flag.
#
require "fpm/package"
require "fpm/util"
require "fileutils"
require "erb"
# create appdata.xml file
class FPM::Package::Filter_appdata < FPM::Package
include ERB::Util
def update(opts=nil)
dest = "#{staging_path}/usr/share/appdata/#{name}.appdata.xml"
FileUtils.mkdir_p(File.dirname(dest))
File.open(dest, "w") do |xml|
xml.write template("appstream.erb").result(binding)
end
end
end
|
Added lib/fpm/package/filter_composer.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#
# api: fpm
# title: Composer.json stub
# description: injects/updates a composer.json from fpm attributes
# type: template
# depends: json
# category: meta
# doc: https://getcomposer.org/doc/04-schema.md
# version: 0.1
#
# Adapts or creates a composer.json from fpm/package attributes.
#
# → To be used in conjunction with -t phar plugin, to craft a whole
# composer bundle from plain scripts.
#
require "fpm/package"
require "fpm/util"
require "json"
require "fileutils"
require "time"
# composer.json
class FPM::Package::Filter_Composer < FPM::Package
def update(opts=nil)
# read existing data
dest = "#{staging_path}/#{@prefix}/composer.json"
if File.exist?(dest)
json = JSON.parse(File.read(dest))
# technically it could also become an input filter now,
# injecting known values for absent fpm attributes here
# (only had to be transferred back to @input in command.rb then..)
else
# create afresh
json = {
"name" => "#{@name}/#{@name}",
"description" => @description,
"license" => @license,
"homepage" => @url,
"type" => "library",
"extra" => {
"\$packaged-by" => "xpm/fpm",
"maintainer" => @maintainer,
"epoch" => @epoch,
"releases"=> []
},
"autoload" => {
"shared" => ["#{@name}.phar"]
}
}
end
# update current build information
json.merge!({
"version" => @version,
"time" => Time.now.strftime("%Y-%m-%d"),
"bin" => bin()
})
# save `composer.json` to staging path
File.write(dest, JSON.pretty_generate(json))
end
def bin
::Dir.chdir(staging_path) do
return ::Dir.glob("**").keep_if { |fn| File.executable?(fn) and not File.directory?(fn) }
end
end
end
|
Added lib/fpm/package/filter_deps.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# api: fpm
# title: Dependency matching
# description: resolves package names for other distributions, using distromatch/whohas
# type: filter
# category: dependency
# version: 0.1
# depends: bin:whohas | bin:distromatch
# license: MITL
#
# Package names in -d dependency lists aren't universal across distributions.
# Which kind of complicates building targetted RPMs (or sometimes DEBs).
#
# Distromatch or whohas.pl can resolve library and package names across
# different Linux systems. They're utilized in this filter to convert basenames
# between each other. Debian being the assumed reference point, you should
# standardize on theirs for specifying fpm -d lists.
#
# A flag `-u deps=opensuse` flag can be used to specify the desired target
# distro/family.
#
# → With `-u deps=fedora..debian` an inverse lookup is performed, only with
# Distromatch though.
#
# → Using whohas only plain package lists are fetched and distros and
# package names searched raw.
#
# Optimum version matches aren't performed. (This is where it's getting even
# more complicated.)
#
# Obviously as these lookups can be time-consuming, you may wish to use either
# of the mentioned tools manually, and prepare per-package --dependencies lists
# yourself. Whohas is likely most revealing for that.
require "fpm/package"
require "fpm/util"
# Resolve package names cross-distro for dependency, suggest, conflicts, .. lists
class FPM::Package::Filter_Deps < FPM::Package
def initialize()
@dm = `which distromatch`
@wh = `which whohas`
logger.warning("Neither distromatch nor whohas are available.") unless (@dm or @wh)
super
@source = "debian"
@target = "debian"
@opts = []
end
# traverse lists
def update(opts=nil)
# check for `-u deps=target` or `deps=source..target` options (option tokens are preseparated in command.rb)
if opts.count >= 2
@source, @target = opts
elsif opts.count == 1
@target = opts[0]
end
# replace all the things
@dependencies = translate(@dependencies)
@provides = translate(@provides)
@replaces = translate(@replaces)
@conflicts = translate(@conflicts)
end
# replace individual package references
def translate(deps)
renamed = []
deps.each do |name|
name =~ /([\w\.\+\-]+)(.*)/
name, ver = [$1, $2]
name = resolve(name)
renamed << "#{name}#{ver}"
end
return renamed
end
# lookup tools
def resolve(pkg)
if target == source
return pkg
elsif @dm
return distromatch(pkg)
else
return whohas(pkg)
end
end
def distromatch(pkg)
return pkg
end
def whohas(pkg)
ls = `#{@wh} --no-threads --shallow -d #{@target} #{pkg}`
if ls =~ /\w+\s+(\S+)/
return $1
else
return pkg
end
end
end
|
Added lib/fpm/package/filter_desktop.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#
# api: fpm
# title: .desktop files
# description: Generates a pkg.desktop files
# type: template
# depends: erb
# category: meta
# version: 0.1
#
# Creates a /usr/share/applications/PKGNAME.desktop file if absent.
#
require "fpm/package"
require "fpm/util"
require "fileutils"
require "erb"
# create .desktop file
class FPM::Package::Filter_desktop < FPM::Package
include ERB::Util
def update(opts=nil)
dest = "#{staging_path}/usr/share/applications/fpm:#{name}.desktop"
FileUtils.mkdir_p(File.dirname(dest))
File.open(dest, "w") do |ini|
ini.write template("desktop.erb").result(binding)
end
end
end
|
Added lib/fpm/package/filter_fixperms.rb.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Set file permissions to 644/755 maximum
require "fpm/package"
require "fpm/util"
require "fileutils"
class FPM::Package::Filter_fixperms < FPM::Package
def update(opts=nil)
::Dir["#{staging_path}/**/*"].each do |fn|
File.chmod(File.stat(fn).mode & 0777755, fn)
end
end
end
|
Added lib/fpm/package/filter_man.rb.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# api: fpm
# title: manpage compression
# description: Compresses any man/* pages in the build path
# type: delegate
# category: filter
# version: 0.1
# license: MITL
#
# Simply compresses any manpages in the build path.
# Only looks for files with uncategorized ….1 / ….5 suffixes.
#
require "fpm/package"
require "fpm/util"
# find manpages, compress them
class FPM::Package::Filter_man < FPM::Package
def update(opts=nil)
::Dir[staging_path + "/**/man/**/*.[12345678]"].each do |file|
safesystem("gzip", "-9", file)
end
end
end
|
Added lib/fpm/package/filter_strip.rb.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Strip debugging symbols from binaries,
# ignore shared libs
require "fpm/package"
require "fpm/util"
require "fileutils"
class FPM::Package::Filter_strip < FPM::Package
def update(opts=nil)
::Dir["#{staging_path}/**/*"].each do |fn|
unless File.directory?(fn)
# only work on ELF files
if File.read(fn, 4) != "\x7FELF"
next
elsif File.executable?(fn)
safesystem("strip", fn)
elsif fn =~ /\.so$/
# don't strip libs
#safesystem("strip", fn)
end
end
end
end
end
|
Added lib/fpm/package/filter_unprefix.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# Strips anything but the given prefix dir from staging_path.
# -u unprefix=/usr/share/pkg/
# Will move the contents of that folder into the top level path.
#
# (It's kind of like the --chdir option for input,
# except that it works after input package extraction.)
require "fpm/package"
require "fpm/util"
require "fileutils"
# find manpages, compress them
class FPM::Package::Filter_unprefix < FPM::Package
def update(opts=nil)
if opts and opts.count == 1
staging_from = "#{staging_path}/#{opts.first}"
if File.exist?(staging_from)
staging_keep = ::Dir.mktmpdir("package-#{type}-staging")
if File.directory?(staging_keep)
FileUtils.mv(::Dir.glob("#{staging_from}/*"), staging_keep)
logger.debug("Exchanging staging path", :path => staging_path, :new => staging_keep)
FileUtils.rm_r(staging_path)
FileUtils.mv(staging_keep, staging_path)
end
else
logger.error("Prefix directory doesn't exist in staging path", :path => opts.first)
end
end # opts
end # update
end # class
|
Changes to lib/fpm/package/gem.rb.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
def download_if_necessary(gem, gem_version)
path = gem
if !File.exists?(path)
path = download(gem, gem_version)
end
| | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
def download_if_necessary(gem, gem_version)
path = gem
if !File.exists?(path)
path = download(gem, gem_version)
end
logger.info("Using gem file", :path => path)
return path
end # def download_if_necessary
def download(gem_name, gem_version=nil)
logger.info("Trying to download", :gem => gem_name, :version => gem_version)
gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
gem_fetch += ["--version", gem_version] if gem_version
download_dir = build_path(gem_name)
FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
::Dir.chdir(download_dir) do |dir|
logger.debug("Downloading in directory #{dir}")
safesystem(*gem_fetch)
end
gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
if gem_files.length != 1
raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1"
|
| ︙ | ︙ | |||
199 200 201 202 203 204 205 |
# Delete bin_path if it's empty, and any empty parents (#612)
# Above, we mkdir_p bin_path because rubygems aborts if the parent
# directory doesn't exist, for example:
# ERROR: While executing gem ... (Errno::ENOENT)
# No such file or directory - /tmp/something/weird/bin
tmp = bin_path
while ::Dir.entries(tmp).size == 2 || tmp == "/" # just [ "..", "." ] is an empty directory
| | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# Delete bin_path if it's empty, and any empty parents (#612)
# Above, we mkdir_p bin_path because rubygems aborts if the parent
# directory doesn't exist, for example:
# ERROR: While executing gem ... (Errno::ENOENT)
# No such file or directory - /tmp/something/weird/bin
tmp = bin_path
while ::Dir.entries(tmp).size == 2 || tmp == "/" # just [ "..", "." ] is an empty directory
logger.info("Deleting empty bin_path", :path => tmp)
::Dir.rmdir(tmp)
tmp = File.dirname(tmp)
end
if attributes[:gem_version_bins?] and File.directory?(bin_path)
(::Dir.entries(bin_path) - ['.','..']).each do |bin|
FileUtils.mv("#{bin_path}/#{bin}", "#{bin_path}/#{bin}-#{self.version}")
end
end
end # def install_to_staging
# Sanitize package name.
|
| ︙ | ︙ |
Added lib/fpm/package/ipk.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# encoding: utf-8
# api: fpm
# title: Listaller IPK
# description: generates Listaller packages using lipkgen
# type: package
# category: target
# version: 0.1
# doc: http://listaller.tenstral.net/docs/chap-Listaller-Packaging.html
# depends: bin:lipkgen, erb
#
# Listaller uses .IPK files for cross-distro installations. It's well
# integrated with Freedesktop schemes and distro application managers.
#
# This module just chains to the generation tool currently, and builds
# static / unrelocatable packages. (Proper support would require using
# Listallers relaytool + ligcc when building the app binaries.)
#
require "fpm/package"
require "fpm/util"
require "fileutils"
require "erb"
require "time"
# Build Listaller package
class FPM::Package::IPK < FPM::Package
include ERB::Util
option "--relocatable", :bool, "Assume application was built relocatable."
# Create doap, files list, then package up
def output(output_path)
output_check(output_path)
# pre-generate files list
files = []
::Dir.chdir(staging_path) do
files = ::Dir["**/*"]
end
# set up build path
ipk = "#{staging_path}/ipkinstall"
::Dir.mkdir(ipk)
File.open("#{ipk}/pkoptions", "w") do |f|
f.write template("listaller/pkoptions.erb").result(binding)
end
File.open("#{ipk}/#{name}.doap", "w") do |f|
f.write template("listaller/doap.erb").result(binding)
end
File.open("#{ipk}/files-#{architecture}.list", "w") do |f|
f.write template("listaller/files.erb").result(binding)
end
File.open("#{ipk}/build.rules", "w") do |f|
end
File.open("#{ipk}/dependencies.list", "w") do |f|
end
# let the packaging be done
opts = ["-b", "--sourcedir=.", "--outdir=#{build_path}"]
if attributes[:deb_sign] || attributes[:rpm_sign]
opts << "--sign"
end
if @verbose || @debug
opts << "--verbose"
end
::Dir.chdir(staging_path) do
safesystem("lipkgen", *opts);
end
FileUtils.rm_rf(ipk) unless attributes[:debug?]
# move file
FileUtils.mv(::Dir["#{build_path}/*.ipk"].first, output_path)
end # output
end
|
Changes to lib/fpm/package/npm.rb.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
require "fpm/namespace"
require "fpm/package"
require "fpm/util"
require "fileutils"
class FPM::Package::NPM < FPM::Package
# Flags '--foo' will be accessable as attributes[:npm_foo]
option "--bin", "NPM_EXECUTABLE",
"The path to the npm executable you wish to run.", :default => "npm"
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
"name with.", :default => "node"
option "--registry", "NPM_REGISTRY",
"The npm registry to use instead of the default."
private
def input(package)
# Notes:
# * npm respects PREFIX
settings = {
"cache" => build_path("npm_cache"),
"loglevel" => "warn",
"global" => "true"
}
| > > > < | < > < | < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
require "fpm/namespace"
require "fpm/package"
require "fpm/util"
require "fileutils"
class FPM::Package::NPM < FPM::Package
class << self
include FPM::Util
end
# Flags '--foo' will be accessable as attributes[:npm_foo]
option "--bin", "NPM_EXECUTABLE",
"The path to the npm executable you wish to run.", :default => "npm"
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
"name with.", :default => "node"
option "--registry", "NPM_REGISTRY",
"The npm registry to use instead of the default."
private
def input(package)
# Notes:
# * npm respects PREFIX
settings = {
"cache" => build_path("npm_cache"),
"loglevel" => "warn",
"global" => "true"
}
settings["registry"] = attributes[:npm_registry] if attributes[:npm_registry_given?]
set_default_prefix unless attributes[:prefix_given?]
settings["prefix"] = staging_path(attributes[:prefix])
FileUtils.mkdir_p(settings["prefix"])
npm_flags = []
settings.each do |key, value|
# npm lets you specify settings in a .npmrc but the same key=value settings
# are valid as '--key value' command arguments to npm. Woo!
logger.debug("Configuring npm", key => value)
npm_flags += [ "--#{key}", value ]
end
install_args = [
attributes[:npm_bin],
"install",
# use 'package' or 'package@version'
|
| ︙ | ︙ | |||
100 101 102 103 104 105 106 107 108 109 |
# any automatic dependency information since every 'npm install'
# is fully self-contained. That's why you don't see any bother, yet,
# to include the package's dependencies in here.
#
# It's possible someone will want to decouple that in the future,
# but I will wait for that feature request.
end
public(:input)
end # class FPM::Package::NPM
| > > > > > > > > > > > > > > > > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# any automatic dependency information since every 'npm install'
# is fully self-contained. That's why you don't see any bother, yet,
# to include the package's dependencies in here.
#
# It's possible someone will want to decouple that in the future,
# but I will wait for that feature request.
end
def set_default_prefix
attributes[:prefix] = self.class.default_prefix
attributes[:prefix_given?] = true
end
def self.default_prefix
npm_prefix = safesystemout("npm", "prefix", "-g").chomp
if npm_prefix.count("\n") > 0
raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned unexpected output."
elsif !File.directory?(npm_prefix)
raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned a non-existent directory"
end
logger.info("Setting default npm install prefix", :prefix => npm_prefix)
npm_prefix
end
public(:input)
end # class FPM::Package::NPM
|
Changes to lib/fpm/package/osxpkg.rb.
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
end # def scripts_path
def write_scripts
SCRIPT_MAP.each do |scriptname, filename|
next unless script?(scriptname)
with(scripts_path(filename)) do |pkgscript|
| | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
end # def scripts_path
def write_scripts
SCRIPT_MAP.each do |scriptname, filename|
next unless script?(scriptname)
with(scripts_path(filename)) do |pkgscript|
logger.info("Writing pkg script", :source => filename, :target => pkgscript)
File.write(pkgscript, script(scriptname))
# scripts are required to be executable
File.chmod(0755, pkgscript)
end
end
end # def write_scripts
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
with(build_path("expand")) do |path|
doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
pkginfo_elem = doc.elements["pkg-info"]
identifier = pkginfo_elem.attribute("identifier").value
self.version = pkginfo_elem.attribute("version").value
# set name to the last dot element of the identifier
self.name = identifier.split(".").last
| | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
with(build_path("expand")) do |path|
doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
pkginfo_elem = doc.elements["pkg-info"]
identifier = pkginfo_elem.attribute("identifier").value
self.version = pkginfo_elem.attribute("version").value
# set name to the last dot element of the identifier
self.name = identifier.split(".").last
logger.info("inferring name #{self.name} from pkg-id #{identifier}")
end
end # def extract_info
# Take a flat package as input
def input(input_path)
# TODO: Fail if it's a Distribution pkg or old-fashioned
expand_dir = File.join(build_path, "expand")
|
| ︙ | ︙ |
Changes to lib/fpm/package/pear.rb.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
# * :pear_package_name_prefix - changes the
def input(input_package)
if !program_in_path?("pear")
raise ExecutableNotFound.new("pear")
end
# Create a temporary config file
| | | | | | | | | | | | | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# * :pear_package_name_prefix - changes the
def input(input_package)
if !program_in_path?("pear")
raise ExecutableNotFound.new("pear")
end
# Create a temporary config file
logger.debug("Creating pear config file")
config = File.expand_path(build_path("pear.config"))
installroot = attributes[:prefix] || "/usr/share"
safesystem("pear", "config-create", staging_path(installroot), config)
if attributes[:pear_php_dir]
logger.info("Setting php_dir", :php_dir => attributes[:pear_php_dir])
safesystem("pear", "-c", config, "config-set", "php_dir", "#{staging_path(installroot)}/#{attributes[:pear_php_dir]}")
end
if attributes[:pear_data_dir]
logger.info("Setting data_dir", :data_dir => attributes[:pear_data_dir])
safesystem("pear", "-c", config, "config-set", "data_dir", "#{staging_path(installroot)}/#{attributes[:pear_data_dir]}")
end
bin_dir = attributes[:pear_bin_dir] || "usr/bin"
logger.info("Setting bin_dir", :bin_dir => bin_dir)
safesystem("pear", "-c", config, "config-set", "bin_dir", bin_dir)
php_bin = attributes[:pear_php_bin] || "/usr/bin/php"
logger.info("Setting php_bin", :php_bin => php_bin)
safesystem("pear", "-c", config, "config-set", "php_bin", php_bin)
# do channel-discover if required
if !attributes[:pear_channel].nil?
logger.info("Custom channel specified", :channel => attributes[:pear_channel])
channel_list = safesystemout("pear", "-c", config, "list-channels")
if channel_list !~ /#{Regexp.quote(attributes[:pear_channel])}/
logger.info("Discovering new channel", :channel => attributes[:pear_channel])
safesystem("pear", "-c", config, "channel-discover", attributes[:pear_channel])
end
input_package = "#{attributes[:pear_channel]}/#{input_package}"
logger.info("Prefixing package name with channel", :package => input_package)
end
# do channel-update if requested
if attributes[:pear_channel_update?]
channel = attributes[:pear_channel] || "pear"
logger.info("Updating the channel", :channel => channel)
safesystem("pear", "-c", config, "channel-update", channel)
end
logger.info("Installing pear package", :package => input_package,
:directory => staging_path)
::Dir.chdir(staging_path) do
safesystem("pear", "-c", config, "install", "-n", "-f", input_package)
end
pear_cmd = "pear -c #{config} remote-info #{input_package}"
logger.info("Fetching package information", :package => input_package, :command => pear_cmd)
name = %x{#{pear_cmd} | sed -ne '/^Package\s*/s/^Package\s*//p'}.chomp
self.name = "#{attributes[:pear_package_name_prefix]}-#{name}"
self.version = %x{#{pear_cmd} | sed -ne '/^Installed\s*/s/^Installed\s*//p'}.chomp
self.description = %x{#{pear_cmd} | sed -ne '/^Summary\s*/s/^Summary\s*//p'}.chomp
logger.debug("Package info", :name => self.name, :version => self.version,
:description => self.description)
# Remove the stuff we don't want
delete_these = [".depdb", ".depdblock", ".filemap", ".lock", ".channel", "cache", "temp", "download", ".channels", ".registry"]
Find.find(staging_path) do |path|
if File.file?(path)
logger.info("replacing staging_path in file", :replace_in => path, :staging_path => staging_path)
begin
content = File.read(path).gsub(/#{Regexp.escape(staging_path)}/, "")
File.write(path, content)
rescue ArgumentError => e
logger.warn("error replacing staging_path in file", :replace_in => path, :error => e)
end
end
FileUtils.rm_r(path) if delete_these.include?(File.basename(path))
end
end # def input
end # class FPM::Package::PEAR
|
Added lib/fpm/package/phar.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# api: fpm
# title: PHP Phar target
# description: Chains to PHP for creating a .phar (native/tar/zip) archive
# type: package
# category: target
# version: 0.2
# state: very alpha
# license: MITL
# author: mario#include-once:org
#
# This packaging target generates simple PHP Phar assemblies. With its
# default stub assuming `__init__.php` for CLI applications, and `index.php`
# as web router. A custom --phar-stub can be set of course.
#
# It honors the output filename, but alternatively allows `--phar-format`
# overriding the packaging format. It flexibly recognizes any concatenation
# of „phar·zip·tar“ with „gz·bz2“, case-insensitively.
#
# ┌─────────────┬─────────────┬─────────────┬─────────────┬───────────────┐
# │ Extension │ Archive │ Compression │ Envelope │ Use │
# │ / Specifier │ Format │ Per File │ Compression │ Cases │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ phar │ Phar │ - │ - │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ phar.gz │ Phar │ gzip │ - │ general │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ phar·bz2 │ Phar │ bzip2 │ - │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ PHAZ │ Phar │ - │ gzip │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ - │ Phar │ gzip │ gzip │ (eschew) │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ zip │ Zip │ - │ - │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ zip.gz │ Zip │ gzip │ - │ distribution │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ zip…bz2 │ Zip │ bzip2 │ - │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ tar │ Pax │ - │ - │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ tgz │ Pax │ - │ gzip │ │
# ├─────────────┼─────────────┼─────────────┼─────────────┼───────────────┤
# │ tar+bz2 │ Pax │ - │ bzip2 │ data bundles │
# └─────────────┴─────────────┴─────────────┴─────────────┴───────────────┘
#
# ZIPs and TARs can be read by other languages, but contain PHP/phar-specific
# additions (.phar/stub and a signature).
#
require "fpm/package"
require "fpm/util"
require "fileutils"
require "json"
# Phar creation (output only)
class FPM::Package::Phar < FPM::Package
option "--format", "PHAR.gz/TGZ/ZIP", "Phar package type and compression (append .gz/.bz2 for method)", :default=>"PHAR.GZ"
option "--stub", "STUB.php", "Apply initialization/stub file", :default=>""
#option "--nocase", :flag, "Lowercase filenames within archive", :default=>false
option "--sign", "PEM_FILE", "Sign package with OpenSSL and key from .pem file", :default=>""
# Invoke PHP for actual packaging process
def output(output_path)
# Flags
o_nocase = attributes[:phar_nocase] || false
o_stub = attributes[:phar_stub] || ""
o_sign = attributes[:phar_sign] || ""
# Retain package meta information, either from fpm attributes, or collected :attr hash (src module, formerly :meta)
meta = attrs.merge({
"id" => @name,
"version" => @version.to_s,
"epoch" => @epoch.to_s,
"iteration" => @iteration.to_s,
"architecture" => @architecture,
"category" => @category == "none" ? nil : @category,
"author" => @maintainer,
"url" => @url,
"license" => @license,
}).delete_if{ |k,v| v.nil? || v==""}
# Match format specifier/extension onto type/settings
fmt = (attributes[:phar_format] + output_path).downcase
fmt, enc = fmt.match(/zip|phaz|tar|t[gb]z|pz/).to_s||"phar", fmt.match(/gz|bz2/).to_s
map2 = { "tgz" => ["tar", "gz"], "tbz" => ["tar", "bz2"], "pz" => ["phaz", ""] }
map = {
# fmt, enc extension format per-file-gz extns ← archive-compr
["phar", "" ] => [ ".phar", "Phar::PHAR", "Phar::NONE", "", "" ],
["phar", "gz" ] => [ ".phar", "Phar::PHAR", "Phar::GZ", "", "" ],
["phar", "bz2"] => [ ".phar", "Phar::PHAR", "Phar::BZ2", "", "" ],
["zip", "" ] => [ ".phar.zip", "Phar::ZIP", "Phar::NONE", "", "" ],
["zip", "gz" ] => [ ".phar.zip", "Phar::ZIP", "Phar::GZ", "", "" ],
["zip", "bz2" ] => [ ".phar.zip", "Phar::ZIP", "Phar::BZ2", "", "" ],
["tar", "" ] => [ ".phar.tar", "Phar::TAR", "Phar::NONE", "", "" ],
["tar", "gz" ] => [ ".phar.tar", "Phar::TAR", "Phar::GZ", ".gz", "(Phar::GZ)" ],
["tar", "bz2" ] => [ ".phar.tar", "Phar::TAR", "Phar::BZ2", ".bz2", "(Phar::BZ2)"],
["phaz", "" ] => [ ".phar", "Phar::PHAR", "Phar::GZ", ".gz", "(Phar::GZ)" ],
["phaz", "gz" ] => [ ".phar", "Phar::PHAR", "Phar::GZ", ".gz", "(Phar::GZ)" ],
["phaz", "bz2"] => [ ".phar", "Phar::PHAR", "Phar::GZ", ".bz2", "(Phar::BZ2)"],
}
opt = map[[fmt,enc]] || map[map2[fmt]] || map[["phar", ""]]
o_stdext, o_format, o_filegz, o_extout, o_hullgz = opt
# Prepare output / temp filename
output_check(output_path)
tmp_phar = ::Dir::Tmpname.create(['_\$fpm_phar_', o_stdext]) { }
# Have PHP generate the package
code = <<-PHP
#-- Create phar
$p = new Phar('#{tmp_phar}', 0, '#{name}');
$p->startBuffering();
#-- Add files
$p->buildFromDirectory('#{staging_path}');
#-- Stub
if (strlen('#{o_stub}') && file_exists('#{staging_path}/#{o_stub}')) {
$p->setStub(file_get_contents('#{staging_path}/#{o_stub}'));
}
elseif (#{o_format} == Phar::PHAR) {
$p->setDefaultStub("__init__.php", "index.php");
}
else {
$p->setDefaultStub();
}
#-- Carry packaging info over as meta data (in particular for `fpm -s src` module)
$p->setMetadata(json_decode($_SERVER["argv"][1]));
#-- Per-file compression
if (#{o_filegz}) {
$p->compressFiles(#{o_filegz});
}
#-- Signature
if (strlen('#{o_sign}')) {
$p->setSignatureAlgorithm(Phar::OPENSSL, file_get_contents('#{o_sign}'));
}
else {
$p->setSignatureAlgorithm(Phar::SHA256);
}
#-- Save all the things
$p->stopBuffering();
// Whole-archive compression; output goes to a different filename. (Cleaned up in Ruby...)
if ("#{o_extout}") {
$p->compress(#{o_hullgz});
}
PHP
safesystem("php", "-dphar.readonly=0", "-derror_reporing=~0", "-ddisplay_errors=1", "-r", code, JSON.generate(meta))
#-- but might end up with suffix, for whole-archive ->compress()ion
FileUtils.mv(tmp_phar + o_extout, output_path)
File.unlink(tmp_phar) if File.exists?(tmp_phar)
end
end # class FPM::Package::Phar
|
Changes to lib/fpm/package/puppet.rb.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
return @architecture
end # def architecture
# Default specfile generator just makes one specfile, whatever that is for
# this package.
def generate_specfile(builddir)
paths = []
| | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
return @architecture
end # def architecture
# Default specfile generator just makes one specfile, whatever that is for
# this package.
def generate_specfile(builddir)
paths = []
logger.info("PWD: #{File.join(builddir, unpack_data_to)}")
fileroot = File.join(builddir, unpack_data_to)
Dir.chdir(fileroot) do
Find.find(".") do |p|
next if p == "."
paths << p
end
end
logger.info(paths[-1])
manifests = %w{package.pp package/remove.pp}
::Dir.mkdir(File.join(builddir, "manifests"))
manifests.each do |manifest|
dir = File.join(builddir, "manifests", File.dirname(manifest))
logger.info("manifests targeting: #{dir}")
::Dir.mkdir(dir) if !File.directory?(dir)
File.open(File.join(builddir, "manifests", manifest), "w") do |f|
logger.info("manifest: #{f.path}")
template = template(File.join("puppet", "#{manifest}.erb"))
::Dir.chdir(fileroot) do
f.puts template.result(binding)
end
end
end
end # def generate_specfile
|
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
when "pre-uninstall"
when "post-uninstall"
end # case name
end # self.scripts.each
if File.exists?(params[:output])
# TODO(sissel): Allow folks to choose output?
| | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
when "pre-uninstall"
when "post-uninstall"
end # case name
end # self.scripts.each
if File.exists?(params[:output])
# TODO(sissel): Allow folks to choose output?
logger.error("Puppet module directory '#{params[:output]}' already " \
"exists. Delete it or choose another output (-p flag)")
end
::Dir.mkdir(params[:output])
builddir = ::Dir.pwd
# Copy 'files' from builddir to :output/files
Find.find("files", "manifests") do |path|
logger.info("Copying path: #{path}")
if File.directory?(path)
::Dir.mkdir(File.join(params[:output], path))
else
FileUtils.cp(path, File.join(params[:output], path))
end
end
end # def build!
|
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
# Helper for user lookup
def uid2user(uid)
begin
pwent = Etc.getpwuid(uid)
return pwent.name
rescue ArgumentError => e
# Invalid user id? No user? Return the uid.
| | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# Helper for user lookup
def uid2user(uid)
begin
pwent = Etc.getpwuid(uid)
return pwent.name
rescue ArgumentError => e
# Invalid user id? No user? Return the uid.
logger.warn("Failed to find username for uid #{uid}")
return uid.to_s
end
end # def uid2user
# Helper for group lookup
def gid2group(gid)
begin
grent = Etc.getgrgid(gid)
return grent.name
rescue ArgumentError => e
# Invalid user id? No user? Return the uid.
logger.warn("Failed to find group for gid #{gid}")
return gid.to_s
end
end # def uid2user
end # class FPM::Target::Puppet
|
Changes to lib/fpm/package/python.rb.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
"is used instead", :default => nil
option "--pypi", "PYPI_URL",
"PyPi Server uri for retrieving packages.",
:default => "http://pypi.python.org/simple"
option "--package-prefix", "NAMEPREFIX",
"(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
"name with." do |value|
| | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
"is used instead", :default => nil
option "--pypi", "PYPI_URL",
"PyPi Server uri for retrieving packages.",
:default => "http://pypi.python.org/simple"
option "--package-prefix", "NAMEPREFIX",
"(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
"name with." do |value|
logger.warn("Using deprecated flag: --package-prefix. Please use " \
"--package-name-prefix")
value
end
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
"name with.", :default => "python"
option "--fix-name", :flag, "Should the target package name be prefixed?",
:default => true
option "--fix-dependencies", :flag, "Should the package dependencies be " \
"prefixed?", :default => true
option "--downcase-name", :flag, "Should the target package name be in " \
"lowercase?", :default => true
option "--downcase-dependencies", :flag, "Should the package dependencies " \
"be in lowercase?", :default => true
option "--install-bin", "BIN_PATH", "The path to where python scripts " \
"should be installed to."
option "--install-lib", "LIB_PATH", "The path to where python libs " \
"should be installed to (default depends on your python installation). " \
"Want to find out what your target platform is using? Run this: " \
"python -c 'from distutils.sysconfig import get_python_lib; " \
"print get_python_lib()'"
option "--install-data", "DATA_PATH", "The path to where data should be " \
"installed to. This is equivalent to 'python setup.py --install-data " \
"DATA_PATH"
option "--dependencies", :flag, "Include requirements defined in setup.py" \
" as dependencies.", :default => true
|
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
if File.directory?(path_to_package)
setup_py = File.join(path_to_package, "setup.py")
else
setup_py = path_to_package
end
if !File.exists?(setup_py)
| | | | | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
if File.directory?(path_to_package)
setup_py = File.join(path_to_package, "setup.py")
else
setup_py = path_to_package
end
if !File.exists?(setup_py)
logger.error("Could not find 'setup.py'", :path => setup_py)
raise "Unable to find python package; tried #{setup_py}"
end
load_package_info(setup_py)
install_to_staging(setup_py)
end # def input
# Download the given package if necessary. If version is given, that version
# will be downloaded, otherwise the latest is fetched.
def download_if_necessary(package, version=nil)
# TODO(sissel): this should just be a 'download' method, the 'if_necessary'
# part should go elsewhere.
path = package
# If it's a path, assume local build.
if File.directory?(path) or (File.exists?(path) and File.basename(path) == "setup.py")
return path
end
logger.info("Trying to download", :package => package)
if version.nil?
want_pkg = "#{package}"
else
want_pkg = "#{package}==#{version}"
end
target = build_path(package)
FileUtils.mkdir(target) unless File.directory?(target)
if attributes[:python_pip].nil?
# no pip, use easy_install
logger.debug("no pip, defaulting to easy_install", :easy_install => attributes[:python_easyinstall])
safesystem(attributes[:python_easyinstall], "-i",
attributes[:python_pypi], "--editable", "-U",
"--build-directory", target, want_pkg)
else
logger.debug("using pip", :pip => attributes[:python_pip])
safesystem(attributes[:python_pip], "install", "--no-deps", "--no-install", "-i", attributes[:python_pypi], "-U", "--build", target, want_pkg)
end
# easy_install will put stuff in @tmpdir/packagename/, so find that:
# @tmpdir/somepackage/setup.py
dirs = ::Dir.glob(File.join(target, "*"))
if dirs.length != 1
raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{build_path}"
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
"try:",
" import json",
"except ImportError:",
" import simplejson as json"
].join("\n")
safesystem("#{attributes[:python_bin]} -c '#{json_test_code}'")
rescue FPM::Util::ProcessFailed => e
| | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
"try:",
" import json",
"except ImportError:",
" import simplejson as json"
].join("\n")
safesystem("#{attributes[:python_bin]} -c '#{json_test_code}'")
rescue FPM::Util::ProcessFailed => e
logger.error("Your python environment is missing json support (either json or simplejson python module). I cannot continue without this.", :python => attributes[:python_bin], :error => e)
raise FPM::Util::ProcessFailed, "Python (#{attributes[:python_bin]}) is missing simplejson or json modules."
end
begin
safesystem("#{attributes[:python_bin]} -c 'import pkg_resources'")
rescue FPM::Util::ProcessFailed => e
logger.error("Your python environment is missing a working setuptools module. I tried to find the 'pkg_resources' module but failed.", :python => attributes[:python_bin], :error => e)
raise FPM::Util::ProcessFailed, "Python (#{attributes[:python_bin]}) is missing pkg_resources module."
end
# Add ./pyfpm/ to the python library path
pylib = File.expand_path(File.dirname(__FILE__))
# chdir to the directory holding setup.py because some python setup.py's assume that you are
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 |
if attributes[:python_obey_requirements_txt?]
setup_cmd += " --load-requirements-txt"
end
# Capture the output, which will be JSON metadata describing this python
# package. See fpm/lib/fpm/package/pyfpm/get_metadata.py for more
# details.
| | | | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
if attributes[:python_obey_requirements_txt?]
setup_cmd += " --load-requirements-txt"
end
# Capture the output, which will be JSON metadata describing this python
# package. See fpm/lib/fpm/package/pyfpm/get_metadata.py for more
# details.
logger.info("fetching package metadata", :setup_cmd => setup_cmd)
success = safesystem(setup_cmd)
#%x{#{setup_cmd}}
if !success
logger.error("setup.py get_metadata failed", :command => setup_cmd,
:exitcode => $?.exitstatus)
raise "An unexpected error occurred while processing the setup.py file"
end
File.read(tmp)
end
logger.debug("result from `setup.py get_metadata`", :data => output)
metadata = JSON.parse(output)
logger.info("object output of get_metadata", :json => metadata)
self.architecture = metadata["architecture"]
self.description = metadata["description"]
# Sometimes the license field is multiple lines; do best-effort and just
# use the first line.
self.license = metadata["license"].split(/[\r\n]+/).first
self.version = metadata["version"]
|
| ︙ | ︙ | |||
219 220 221 222 223 224 225 |
self.name = self.name.downcase if attributes[:python_downcase_name?]
if !attributes[:no_auto_depends?] and attributes[:python_dependencies?]
self.dependencies = metadata["dependencies"].collect do |dep|
dep_re = /^([^<>!= ]+)\s*(?:([<>!=]{1,2})\s*(.*))?$/
match = dep_re.match(dep)
if match.nil?
| | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
self.name = self.name.downcase if attributes[:python_downcase_name?]
if !attributes[:no_auto_depends?] and attributes[:python_dependencies?]
self.dependencies = metadata["dependencies"].collect do |dep|
dep_re = /^([^<>!= ]+)\s*(?:([<>!=]{1,2})\s*(.*))?$/
match = dep_re.match(dep)
if match.nil?
logger.error("Unable to parse dependency", :dependency => dep)
raise FPM::InvalidPackageConfiguration, "Invalid dependency '#{dep}'"
end
name, cmp, version = match.captures
# convert == to =
if cmp == "=="
logger.info("Converting == dependency requirement to =", :dependency => dep )
cmp = "="
end
# dependency name prefixing is optional, if enabled, a name 'foo' will
# become 'python-foo' (depending on what the python_package_name_prefix
# is)
name = fix_name(name) if attributes[:python_fix_dependencies?]
|
| ︙ | ︙ |
Changes to lib/fpm/package/rpm.rb.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
"sha256" => 8,
"sha384" => 9,
"sha512" => 10
} unless defined?(DIGEST_ALGORITHM_MAP)
COMPRESSION_MAP = {
"none" => "w0.gzdio",
| | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
"sha256" => 8,
"sha384" => 9,
"sha512" => 10
} unless defined?(DIGEST_ALGORITHM_MAP)
COMPRESSION_MAP = {
"none" => "w0.gzdio",
"xz" => "w9.xzdio",
"gzip" => "w9.gzdio",
"bzip2" => "w9.bzdio"
} unless defined?(COMPRESSION_MAP)
option "--use-file-permissions", :flag,
"Use existing file permissions when defining ownership and modes."
|
| ︙ | ︙ | |||
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
option "--auto-add-exclude-directories", "DIRECTORIES",
"Additional directories ignored by '--rpm-auto-add-directories' flag",
:multivalued => true, :attribute_name => :auto_add_exclude_directories
option "--autoreqprov", :flag, "Enable RPM's AutoReqProv option"
option "--autoreq", :flag, "Enable RPM's AutoReq option"
option "--autoprov", :flag, "Enable RPM's AutoProv option"
rpmbuild_filter_from_provides = []
option "--filter-from-provides", "REGEX",
"Set %filter_from_provides to the supplied REGEX." do |filter_from_provides|
rpmbuild_filter_from_provides << filter_from_provides
next rpmbuild_filter_from_provides
end
rpmbuild_filter_from_requires = []
option "--filter-from-requires", "REGEX",
"Set %filter_from_requires to the supplied REGEX." do |filter_from_requires|
rpmbuild_filter_from_requires << filter_from_requires
next rpmbuild_filter_from_requires
end
option "--ignore-iteration-in-dependencies", :flag,
"For '=' (equal) dependencies, allow iterations on the specified " \
"version. Default is to be specific. This option allows the same " \
"version of a package but any iteration is permitted"
private
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
option "--auto-add-exclude-directories", "DIRECTORIES",
"Additional directories ignored by '--rpm-auto-add-directories' flag",
:multivalued => true, :attribute_name => :auto_add_exclude_directories
option "--autoreqprov", :flag, "Enable RPM's AutoReqProv option"
option "--autoreq", :flag, "Enable RPM's AutoReq option"
option "--autoprov", :flag, "Enable RPM's AutoProv option"
option "--attr", "ATTRFILE",
"Set the attribute for a file (%attr).",
:multivalued => true, :attribute_name => :attrs
option "--init", "FILEPATH", "Add FILEPATH as an init script",
:multivalued => true do |file|
next File.expand_path(file)
end
rpmbuild_filter_from_provides = []
option "--filter-from-provides", "REGEX",
"Set %filter_from_provides to the supplied REGEX." do |filter_from_provides|
rpmbuild_filter_from_provides << filter_from_provides
next rpmbuild_filter_from_provides
end
rpmbuild_filter_from_requires = []
option "--filter-from-requires", "REGEX",
"Set %filter_from_requires to the supplied REGEX." do |filter_from_requires|
rpmbuild_filter_from_requires << filter_from_requires
next rpmbuild_filter_from_requires
end
option "--ignore-iteration-in-dependencies", :flag,
"For '=' (equal) dependencies, allow iterations on the specified " \
"version. Default is to be specific. This option allows the same " \
"version of a package but any iteration is permitted"
option "--verbatim-gem-dependencies", :flag,
"When converting from a gem, leave the old (fpm 0.4.x) style " \
"dependency names. This flag will use the old 'rubygem-foo' " \
"names in rpm requires instead of the redhat style " \
"rubygem(foo).", :default => false
option "--verifyscript", "FILE",
"a script to be run on verification" do |val|
File.expand_path(val) # Get the full path to the script
end # --verifyscript
option "--pretrans", "FILE",
"pretrans script" do |val|
File.expand_path(val) # Get the full path to the script
end # --pretrans
option "--posttrans", "FILE",
"posttrans script" do |val|
File.expand_path(val) # Get the full path to the script
end # --posttrans
["before-install","after-install","before-uninstall","after-target-uninstall"].each do |trigger_type|
rpm_trigger = []
option "--trigger-#{trigger_type}", "'[OPT]PACKAGE: FILEPATH'", "Adds a rpm trigger script located in FILEPATH, " \
"having 'OPT' options and linking to 'PACKAGE'. PACKAGE can be a comma seperated list of packages. " \
"See: http://rpm.org/api/4.4.2.2/triggers.html" do |trigger|
match = trigger.match(/^(\[.*\]|)(.*): (.*)$/)
@logger.fatal("Trigger '#{trigger_type}' definition can't be parsed ('#{trigger}')") unless match
opt, pkg, file = match.captures
@logger.fatal("File given for --trigger-#{trigger_type} does not exist (#{file})") unless File.exists?(file)
rpm_trigger << [pkg, File.read(file), opt.tr('[]','')]
next rpm_trigger
end
end
private
# Fix path name
# Replace [ with [\[] to make rpm not use globs
# Replace * with [*] to make rpm not use globs
# Replace ? with [?] to make rpm not use globs
# Replace % with [%] to make rpm not expand macros
def rpm_fix_name(name)
name = "\"#{name}\"" if name[/\s/]
name = name.gsub("[", "[\\[]")
name = name.gsub("*", "[*]")
name = name.gsub("?", "[?]")
name = name.gsub("%", "[%]")
end
def rpm_file_entry(file)
original_file = file
file = rpm_fix_name(file)
if !attributes[:rpm_use_file_permissions?]
if attrs[file].nil?
return file
else
return sprintf("%%attr(%s) %s\n", attrs[file], file)
end
end
return sprintf("%%attr(%s) %s\n", attrs[file], file) unless attrs[file].nil?
# Stat the original filename in the relative staging path
::Dir.chdir(staging_path) do
stat = File.lstat(original_file.gsub(/\"/, '').sub(/^\//,''))
# rpm_user and rpm_group attribute should override file ownership
# otherwise use the current file user/group by name.
user = attributes[:rpm_user] || Etc.getpwuid(stat.uid).name
group = attributes[:rpm_group] || Etc.getgrgid(stat.gid).name
mode = stat.mode
return sprintf("%%attr(%o, %s, %s) %s\n", mode & 4095 , user, group, file)
|
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
def iteration
return @iteration ? @iteration : 1
end # def iteration
# See FPM::Package#converted_from
def converted_from(origin)
if origin == FPM::Package::Gem
| < < < < < < < > | < > | | | | | | | > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
def iteration
return @iteration ? @iteration : 1
end # def iteration
# See FPM::Package#converted_from
def converted_from(origin)
if origin == FPM::Package::Gem
fixed_deps = []
self.dependencies.collect do |dep|
# Gem dependency operator "~>" is not compatible with rpm. Translate any found.
fixed_deps = fixed_deps + expand_pessimistic_constraints(dep)
end
self.dependencies = fixed_deps
# Convert 'rubygem-foo' provides values to 'rubygem(foo)'
# since that's what most rpm packagers seem to do.
self.provides = self.provides.collect do |provides|
# Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present
# and return it in rubygem_prefix(gem_name) form
if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(provides)
"#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
else
provides
end
end
if !self.attributes[:rpm_verbatim_gem_dependencies?]
self.dependencies = self.dependencies.collect do |dependency|
# Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present
# and return it in rubygem_prefix(gem_name) form
if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(dependency)
"#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
else
dependency
end
end
end
end
# Convert != dependency as Conflict =, as rpm doesn't understand !=
self.dependencies = self.dependencies.select do |dep|
name, op, version = dep.split(/\s+/)
|
| ︙ | ︙ | |||
226 227 228 229 230 231 232 |
if self.attributes[:rpm_ignore_iteration_in_dependencies?]
self.dependencies = self.dependencies.collect do |dep|
name, op, version = dep.split(/\s+/)
if op == '='
nextversion = version.split('.').collect { |v| v.to_i }
nextversion[-1] += 1
nextversion = nextversion.join(".")
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
if self.attributes[:rpm_ignore_iteration_in_dependencies?]
self.dependencies = self.dependencies.collect do |dep|
name, op, version = dep.split(/\s+/)
if op == '='
nextversion = version.split('.').collect { |v| v.to_i }
nextversion[-1] += 1
nextversion = nextversion.join(".")
logger.warn("Converting dependency #{dep} to #{name} >= #{version}, #{name} < #{nextversion}")
["#{name} >= #{version}", "#{name} < #{nextversion}"]
else
dep
end
end.flatten
end
setscript = proc do |scriptname|
script_path = self.attributes[scriptname]
# Skip scripts not set
next if script_path.nil?
if !File.exists?(script_path)
logger.error("No such file (for #{scriptname.to_s}): #{script_path.inspect}")
script_errors << script_path
end
# Load the script into memory.
scripts[scriptname] = File.read(script_path)
end
setscript.call(:rpm_verifyscript)
setscript.call(:rpm_posttrans)
setscript.call(:rpm_pretrans)
end # def converted
def rpm_get_trigger_type(flag)
puts "#{flag.to_s(2)}"
if (flag & (1 << 25)) == (1 << 25)
:rpm_trigger_before_install
elsif (flag & (1 << 16)) == (1 << 16)
:rpm_trigger_after_install
elsif (flag & (1 << 17)) == (1 << 17)
:rpm_trigger_before_uninstall
elsif (flag & (1 << 18)) == (1 << 18)
:rpm_trigger_after_target_uninstall
else
@logger.fatal("I don't know about this triggerflag ('#{flag}')")
end
end # def rpm_get_trigger
def input(path)
rpm = ::RPM::File.new(path)
tags = {}
rpm.header.tags.each do |tag|
tags[tag.tag] = tag.value
|
| ︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 270 |
self.vendor = tags[:vendor]
self.version = tags[:version]
self.scripts[:before_install] = tags[:prein]
self.scripts[:after_install] = tags[:postin]
self.scripts[:before_remove] = tags[:preun]
self.scripts[:after_remove] = tags[:postun]
# TODO(sissel): prefix these scripts above with a shebang line if there isn't one?
# Also taking into account the value of tags[preinprog] etc, something like:
# #!#{tags[:preinprog]}
# #{tags[prein]}
| > > > | > > > > > > > > > > > > | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
self.vendor = tags[:vendor]
self.version = tags[:version]
self.scripts[:before_install] = tags[:prein]
self.scripts[:after_install] = tags[:postin]
self.scripts[:before_remove] = tags[:preun]
self.scripts[:after_remove] = tags[:postun]
self.scripts[:rpm_verifyscript] = tags[:verifyscript]
self.scripts[:rpm_posttrans] = tags[:posttrans]
self.scripts[:rpm_pretrans] = tags[:pretrans]
# TODO(sissel): prefix these scripts above with a shebang line if there isn't one?
# Also taking into account the value of tags[preinprog] etc, something like:
# #!#{tags[:preinprog]}
# #{tags[prein]}
if !tags[:triggerindex].nil?
val = tags[:triggerindex].zip(tags[:triggername],tags[:triggerflags],tags[:triggerversion]).group_by{ |x| x[0]}
val = val.collect do |order,data|
new_data = data.collect { |x| [ x[1], rpm.operator(x[2]), x[3]].join(" ").strip}.join(", ")
[order, rpm_get_trigger_type(data[0][2]), new_data]
end
val.each do |order, attr,data|
self.attributes[attr] = [] if self.attributes[attr].nil?
scriptprog = (tags[:triggerscriptprog][order] == '/bin/sh') ? "" : "-p #{tags[:triggerscriptprog][order]}"
self.attributes[attr] << [data,tags[:triggerscripts][order],scriptprog]
end
end
if !attributes[:no_auto_depends?]
self.dependencies += rpm.requires.collect do |name, operator, version|
[name, operator, version].join(" ")
end
end
|
| ︙ | ︙ | |||
305 306 307 308 309 310 311 |
def output(output_path)
output_check(output_path)
%w(BUILD RPMS SRPMS SOURCES SPECS).each { |d| FileUtils.mkdir_p(build_path(d)) }
args = ["rpmbuild", "-bb"]
if %x{uname -m}.chomp != self.architecture
| | > > > > > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
def output(output_path)
output_check(output_path)
%w(BUILD RPMS SRPMS SOURCES SPECS).each { |d| FileUtils.mkdir_p(build_path(d)) }
args = ["rpmbuild", "-bb"]
if %x{uname -m}.chomp != self.architecture
rpm_target = self.architecture
end
# issue #309
if !attributes[:rpm_os].nil?
rpm_target = "#{architecture}-unknown-#{attributes[:rpm_os]}"
end
# issue #707
if !rpm_target.nil?
args += ["--target", rpm_target]
end
args += [
"--define", "buildroot #{build_path}/BUILD",
"--define", "_topdir #{build_path}",
"--define", "_sourcedir #{build_path}",
"--define", "_rpmdir #{build_path}/RPMS",
"--define", "_tmppath #{attributes[:workdir]}"
]
args += ["--sign"] if attributes[:rpm_sign?]
if attributes[:rpm_auto_add_directories?]
fs_dirs_list = File.join(template_dir, "rpm", "filesystem_list")
fs_dirs = File.readlines(fs_dirs_list).reject { |x| x =~ /^\s*#/}.map { |x| x.chomp }
|
| ︙ | ︙ | |||
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
Find.find(cfg_path) do |p|
allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
end
end
allconfigs.sort!.uniq!
self.config_files = allconfigs.map { |x| File.join("/", x) }
(attributes[:rpm_rpmbuild_define] or []).each do |define|
args += ["--define", define]
end
# copy all files from staging to BUILD dir
Find.find(staging_path) do |path|
src = path.gsub(/^#{staging_path}/, '')
dst = File.join(build_path, build_sub_dir, src)
copy_entry(path, dst)
end
rpmspec = template("rpm.erb").result(binding)
specfile = File.join(build_path("SPECS"), "#{name}.spec")
File.write(specfile, rpmspec)
edit_file(specfile) if attributes[:edit?]
args << specfile
| > > > > > > > > > | | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
Find.find(cfg_path) do |p|
allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
end
end
allconfigs.sort!.uniq!
self.config_files = allconfigs.map { |x| File.join("/", x) }
# add init script if present
(attributes[:rpm_init_list] or []).each do |init|
name = File.basename(init, ".init")
dest_init = File.join(staging_path, "etc/init.d/#{name}")
FileUtils.mkdir_p(File.dirname(dest_init))
FileUtils.cp init, dest_init
File.chmod(0755, dest_init)
end
(attributes[:rpm_rpmbuild_define] or []).each do |define|
args += ["--define", define]
end
# copy all files from staging to BUILD dir
Find.find(staging_path) do |path|
src = path.gsub(/^#{staging_path}/, '')
dst = File.join(build_path, build_sub_dir, src)
copy_entry(path, dst)
end
rpmspec = template("rpm.erb").result(binding)
specfile = File.join(build_path("SPECS"), "#{name}.spec")
File.write(specfile, rpmspec)
edit_file(specfile) if attributes[:edit?]
args << specfile
logger.info("Running rpmbuild", :args => args)
safesystem(*args)
::Dir["#{build_path}/RPMS/**/*.rpm"].each do |rpmpath|
# This should only output one rpm, should we verify this?
FileUtils.cp(rpmpath, output_path)
end
end # def output
def prefix
return (attributes[:prefix] or "/")
end # def prefix
def build_sub_dir
return "BUILD"
#return File.join("BUILD", prefix)
end # def build_sub_dir
def version
if @version.kind_of?(String) and @version.include?("-")
logger.warn("Package version '#{@version}' includes dashes, converting" \
" to underscores")
@version = @version.gsub(/-/, "_")
end
return @version
end
# The default epoch value must be nil, see #381
def epoch
return @epoch if @epoch.is_a?(Numeric)
if @epoch.nil? or @epoch.empty?
logger.warn("no value for epoch is set, defaulting to nil")
return nil
end
return @epoch
end # def epoch
def to_s(format=nil)
|
| ︙ | ︙ |
Changes to lib/fpm/package/sh.rb.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 |
end
path
end
# Returns the path to the tar file containing the packed up staging directory
def payload
payload_tar = build_path("payload.tar")
| | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
end
path
end
# Returns the path to the tar file containing the packed up staging directory
def payload
payload_tar = build_path("payload.tar")
logger.info("Creating payload tar ", :path => payload_tar)
args = [ tar_cmd,
"-C",
staging_path,
"-cf",
payload_tar,
"--owner=0",
|
| ︙ | ︙ |
Added lib/fpm/package/src.rb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# encoding: ascii
# api: fpm
# title: generic script source
# description: Utilizes `pack:` specifier from meta/comment block in script files
# type: package
# category: source
# version: 0.7
# state: beta
# architecture: all
# license: MITL
# author: mario#include-once:org
# config: <arg name="src-only" description="Only apply main pack: spec, do not recurse.">
# depends:
# pack: src.rb, README=README.txt, src/*.png
#
# The "src" input is intended for packaging scripting language files.
# A top-level comment can hold per-file description fields, where the
# `pack:` lines simply reference other files to recurse and include.
#
# → Other documentation/meta fields from the origin file are used as
# package attributes (fpm --flag values still take precedence).
#
# → The pack: line gives a simple comma-separated list of other scripts
# or files to include.
#
# · For instance `pack: src.rb, readme.txt, install.sh` will package
# those files together with the main script.
#
# · RECURSION: referenced files can itself specify a `pack:` line
# onto other scripts/source/binary files.
#
# · With `pack: m.py=main.py, b=b.txt` files can be renamed.
#
# · Glob matching is also possible `pack: plugins/*.php`
# Limited glob-rewriting via `doc/*.txt=manual/` even.
# It's sort of working, use with care.
#
# · A file can even exclude itself with `pack: empty.rb=`
#
# Binary files can be referenced from a text/source file of course.
#
# Alternatively a concise spec.txt can be crafted to hold default
# packaging settings for fpm. Which is a nice alternative to a "dir"
# source or --inputs list for small projects.
#
# → `depends:` lines are not yet used for packaging. They aren't
# transcribed into system package (rpm/deb) control fields either.
# (They're mostly intended for in-application plugin management.)
#
require "fpm/package"
require "fpm/util"
require "backports"
require "fileutils"
require "find"
require "socket"
require "pathname"
# Source package.
#
# Reads in the meta data comment block from the first specified file,
# recursively includes all pack:-mentioned scripts, while honoring
# src=dest filename specifiers.
#
class FPM::Package::Src < FPM::Package # inheriting from ::Dir doesn't work
option "--depends", :flag, "Traverse source files as mentioned in depends: field.", :default => false
option "--only", :flag, "Only apply origin files pack: directive, do not recurse.", :default => false
# Start from first specified source file.
def input(path)
# The init/main file should contain usable package meta fields
if m = get_meta(path)
# copy attributes over, if not present/overriden
@name = m["id"] if not @name
@version = m["version"] if not @version
@epoch = m["epoch"] if not @epoch
@architecture = m["architecture"] if not attributes[:architecture_given?]
@description = "#{m['description']}\n#{m['comment']}" if not attributes[:description_given?]
@url = m["url"] || m["homepage"] if not attributes[:url_given?]
@category = m["category"] if not attributes[:category_given?]
@priority = m["priority"] if not attributes[:priority_given?]
@license = m["license"] if not attributes[:license_given?]
@vendor = m["author"] if not attributes[:maintainer_given?]
@maintainer = m["author"] if not attributes[:maintainer_given?]
# retain all available attributes (formerly :meta, now in shared :attrs)
@attrs.merge!(m)
#@todo preparse and collect config: structures
end
# Assemble rewrite map
# {
# rel/src1 => { src2=>dest, src3=>dest } ,
# rel/src2 => { src4=> .. }
# }
@map = {:spec_start => {path => path}}
::Dir.chdir(attributes[:chdir] || ".") do
rewrite_map(path)
logger.debug(@map)
#p @map
# Copy all referenced files.
copied = {}
@map.each do |from,pack|
pack.each do |src,dest|
# Each file can hold a primary declaration and override its target filename / make itself absent.
if dest and @map[dest] and @map[dest][dest]
dest = @map[dest][dest]
end
# References above the src/.. would also cross the staging_dir, give warning
if dest and dest.match(/^\.\.\//)
logger.warn("Referencing above ../ the basedir from #{from}")
end
# Else just apply pack{} map copying verbatim, possibly overwriting or duplicating files.
if not copied[src+">"+dest]
#p "cp #{src} to /#{dest}"
real_copy("#{src}", "#{staging_path}/#{attributes[:prefix]}/#{dest}") if dest != ""
copied[src+">"+dest] = 1
end
end
end
end
end
protected
# Scan files for file,src=dest mapping lists.
#
# Each source file can specify references as `pack: two.sh, sub/three.py`
# where each scripts´ subdirectory becomes the next base directory.
#
# The `dest` parameter is carried over from the previous old=new
# filename pack{} map.
#
def rewrite_map(from, dest="")
# lazy workaround to prevent neverending loops/references
return if @map[from]
# check if file is present, get meta data
if not File.exists?(from)
logger.warn("Skipping non-existent #{from} file")
return
elsif File.directory?(from)
@map[from] = {}
return
else
packlist = get_meta(from)["pack"] # adding [splitfn(from)[1]] itself would override renames
end
# relative dir and basename
dir, fn = splitfn(from) # fpm/ src.rb
# @example
# from = fpm/src.r
# `pack: exe.rb, ../README, foo=bar, test/*`
# @result
# pack[fpm/src.rb] = fpm/exe.rb=>fpm/exe.rb, README=>README, fpm/foo=>fpm/bar, fpm/test/123=>...}
# iterate over listed references
@map[from] = pack = {}
packlist.each do |fnspec|
# specifier can be one of: `src, src=dest, src*, dir/src*=dest/`
src, dest = fnspec.split("=", 2) # dest can be nil (=use src basename), or empty "" (=skip file)
# glob expansion
if src.match(/[\[\{\*\?]/)
src = ::Dir.glob(dir + src)
logger.warn("Nothing matched for '#{fnspec}' in '#{from}'") if src.empty?
else
src = [dir + src]
end
# combine with dest, and recursively scan each referenced source file
src.each do |src|
pack[consolidate(src)] = consolidate(dest ? dir+dest : src)
rewrite_map(consolidate(src)) if not @attributes[:src_only?]
end
end
end # def rewrite_map
# split dir/name/ from basename
def splitfn(path)
path =~ /\A ((?: .*\/ )?) ([^\/]+) \Z/x
return [$1, $2]
end
# remove ./ and dir/../ segments
def consolidate(path)
path = path.gsub(/(?<=^|\/)\.\//, "") # strip out "./" self-referenting dirs
path = path.gsub(/(?<=^|\/)(?!\.\.\/)[\w.-]+\/\.\.\//, "") # consolidate "sub/../" relative paths
end
# Extract meta: key/values from source file
def get_meta(path)
# read file (first 8K would suffice)
src = File.read(path, 1<<13, 0, :encoding=>'ASCII-8BIT')
fields = { "pack" => "" }
# extract first comment block,
if src and src =~ /( \/\*+ .+? \*\/ | (?:[ \t]* \* | \#(?!!) [^\n]*\n)+ )/mix
src = $1.gsub(/^[ \t\*\/\#]+/, "").to_s # does not honor indentation
# split meta block from comment (on first empty line)
src, fields["comment"] = src.split(/\r?\n\r?\n/, 2) # eh, Ruby, Y U NO PROVIDE \R ?
# split key: value fields
fields.merge!(Hash[
src.to_s.scan(/^([\w-]+): [[:blank:]]* ([^\n]* (?:\n (?![\w-]+:) [^\n]+)* )/x).map{ |k,v| [k.downcase, v] }
])
# use input basename or id: field as package name
fields["id"] = fields["id"] || path.match(/([^\/]+?)(\.\w+)?\Z/)[1]
# split up pack: comma-separated list, don't expand src=dest yet
#p fields
fields["pack"] = fields["pack"].split(/(?<!\\)[,\s]+/)
else
fields["pack"] = []
end
return fields
end # def meta
# automatically recurses for and creates subdirectories when copying
def real_copy(src, dest)
if dest[-1] == "/" # trailing slash indicates target dir
dest += splitfn(src)[1] # therefore copy src basename
end
if File.exists?(src)
FileUtils.mkdir_p(File.dirname(dest))
FileUtils.cp_r(src, dest)
else
logger.info("'#{src}' still missing, eh?")
end
end
end # class FPM::Package::Src
|
Changes to lib/fpm/package/tar.rb.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
# Output a tarball.
#
# If the output path ends predictably (like in .tar.gz) it will try to obey
# the compression type.
def output(output_path)
output_check(output_path)
| | < | < < | > | > | | | | | > > > > > > > > > > > > | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# Output a tarball.
#
# If the output path ends predictably (like in .tar.gz) it will try to obey
# the compression type.
def output(output_path)
output_check(output_path)
# Pack tarball in the staging path
args = tar_compression_flag(output_path).compact \
+ [File.absolute_path(output_path), "."]
::Dir::chdir(staging_path) do
safesystem(*args)
end
end # def output
# Generate the proper tar flags based on the path name.
def tar_compression_flag(path)
case path
when /\.tar\.bz2$|\.tbz2$/
return ["tar", "-cjf"]
when /\.tar\.gz$|\.tgz$/
return ["tar", "-czf"]
when /\.tar\.xz$|\.txz$/
return ["tar", "-cJf"]
when /\.pax$/
return ["pax", "-wf"]
when /\.pax\.gz$/
return ["pax", "-wzf"]
when /\.pax\.xz$/
return ["pax", "-wJf"]
when /\.pax\.bz2$/
return ["pax", "-wjf"]
when /\.cpio$/
return ["pax", "-x" "cpio", "-wf"]
when /\.cpio.gz$/
return ["pax", "-x" "cpio", "-wzf"]
else
return ["tar", "-cf"]
end
end # def tar_compression_flag
end # class FPM::Package::Tar
|
Changes to lib/fpm/package/zip.rb.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | end # def input # Output a tarball. # # If the output path ends predictably (like in .tar.gz) it will try to obey # the compression type. def output(output_path) | | | > | | > < < < < < < < < < < < < < | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
end # def input
# Output a tarball.
#
# If the output path ends predictably (like in .tar.gz) it will try to obey
# the compression type.
def output(output_path)
output_check( output_path = File.absolute_path(output_path) )
::Dir.chdir(staging_path) do
files = Find.find(".").to_a
safesystem("zip", output_path, *files)
end
end # def output
end # class FPM::Package::Zip
|
Changes to lib/fpm/util.rb.
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
end
program = args[0]
if !program_exists?(program)
raise ExecutableNotFound.new(program)
end
| | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
end
program = args[0]
if !program_exists?(program)
raise ExecutableNotFound.new(program)
end
logger.debug("Running command", :args => args)
# Create a pair of pipes to connect the
# invoked process to the cabin logger
stdout_r, stdout_w = IO.pipe
stderr_r, stderr_w = IO.pipe
process = ChildProcess.build(*args)
process.io.stdout = stdout_w
process.io.stderr = stderr_w
process.start
stdout_w.close; stderr_w.close
logger.debug('Process is running', :pid => process.pid)
# Log both stdout and stderr as 'info' because nobody uses stderr for
# actually reporting errors and as a result 'stderr' is a misnomer.
logger.pipe(stdout_r => :info, stderr_r => :info)
process.wait
success = (process.exit_code == 0)
if !success
raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
". Full command was:#{args.inspect}")
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
end
program = args[0]
if !program.include?("/") and !program_in_path?(program)
raise ExecutableNotFound.new(program)
end
| | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
end
program = args[0]
if !program.include?("/") and !program_in_path?(program)
raise ExecutableNotFound.new(program)
end
logger.debug("Running command", :args => args)
stdout_r, stdout_w = IO.pipe
stderr_r, stderr_w = IO.pipe
process = ChildProcess.build(*args)
process.io.stdout = stdout_w
process.io.stderr = stderr_w
process.start
stdout_w.close; stderr_w.close
stdout_r_str = stdout_r.read
stdout_r.close; stderr_r.close
logger.debug("Process is running", :pid => process.pid)
process.wait
success = (process.exit_code == 0)
if !success
raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
". Full command was:#{args.inspect}")
|
| ︙ | ︙ | |||
187 188 189 190 191 192 193 194 |
# pkg = FPM::Package::Dir...
# pkg.output()...
# pkg.output()...
# The 2nd output call will fail or behave weirdly because @copied_entries
# is already populated. even though this is anew round of copying.
return @copied_entries ||= {}
end # def copied_entries
end # module FPM::Util
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# pkg = FPM::Package::Dir...
# pkg.output()...
# pkg.output()...
# The 2nd output call will fail or behave weirdly because @copied_entries
# is already populated. even though this is anew round of copying.
return @copied_entries ||= {}
end # def copied_entries
def expand_pessimistic_constraints(constraint)
name, op, version = constraint.split(/\s+/)
if op == '~>'
new_lower_constraint = "#{name} >= #{version}"
version_components = version.split('.').collect { |v| v.to_i }
version_prefix = version_components[0..-3].join('.')
portion_to_work_with = version_components.last(2)
prefix = ''
unless version_prefix.empty?
prefix = version_prefix + '.'
end
one_to_increment = portion_to_work_with[0].to_i
incremented = one_to_increment + 1
new_version = ''+ incremented.to_s + '.0'
upper_version = prefix + new_version
new_upper_constraint = "#{name} < #{upper_version}"
return [new_lower_constraint,new_upper_constraint]
else
return [constraint]
end
end #def expand_pesimistic_constraints
def logger
@logger ||= Cabin::Channel.get
end # def logger
end # module FPM::Util
|
Changes to lib/fpm/version.rb.
1 | module FPM | | | 1 2 3 | module FPM VERSION = "1.3.3.4" end |
Added spec/fixtures/deb/meta_test.
> | 1 | asdf |
Added spec/fixtures/deb/triggers.
> | 1 | interest from-meta-file |
Added spec/fixtures/gem/example/bin/example.
> | 1 | #!/usr/bin/env ruby |
Changes to spec/fixtures/gem/example/example-1.0.gem.
cannot compute difference between binary files
Changes to spec/fixtures/gem/example/example.gemspec.
1 2 3 4 5 6 7 8 9 10 |
Gem::Specification.new do |spec|
spec.name = "example"
spec.version = "1.0"
spec.summary = "sample summary"
spec.description = "sample description"
spec.add_dependency("dependency1") # license: Ruby License
spec.add_dependency("dependency2")
#spec.files = ["hello.txt"]
| | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Gem::Specification.new do |spec|
spec.name = "example"
spec.version = "1.0"
spec.summary = "sample summary"
spec.description = "sample description"
spec.add_dependency("dependency1") # license: Ruby License
spec.add_dependency("dependency2")
#spec.files = ["hello.txt"]
spec.files = [ "bin/example" ]
spec.executables = "example"
spec.bindir = "bin"
#spec.require_paths << "lib"
spec.author = "sample author"
spec.email = "sample email"
spec.homepage = "http://sample-url/"
end
|
Added spec/fixtures/mockpackage.rb.
> > > > > > > | 1 2 3 4 5 6 7 | require "fpm/namespace" require "fpm/package" class FPM::Package::Mock < FPM::Package def input(*args); end def output(*args); end end |
Changes to spec/fpm/command_spec.rb.
1 2 3 4 5 6 7 8 9 10 11 | require "spec_setup" require "stud/temporary" require "fpm" # local require "fpm/command" # local describe FPM::Command do describe "--prefix" describe "-C" describe "-p / --package" describe "-f" describe "-n" | > | 1 2 3 4 5 6 7 8 9 10 11 12 | require "spec_setup" require "stud/temporary" require "fpm" # local require "fpm/command" # local require "fixtures/mockpackage" describe FPM::Command do describe "--prefix" describe "-C" describe "-p / --package" describe "-f" describe "-n" |
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
end
end
end
context "when not set" do
it "should write the package to the current directory." do
Stud::Temporary.directory do |path|
| < < < > > > > > > > > | > > > > > > > > > > > > > > > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
end
end
end
context "when not set" do
it "should write the package to the current directory." do
Stud::Temporary.directory do |path|
Dir.chdir(path) do
cmd = FPM::Command.new("fpm")
cmd.run(["-s", "empty", "-t", "deb", "-n", "example"])
end
files = Dir.new(path).to_a - ['.', '..']
insist { files.size } == 1
insist { files[0] } =~ /example_/
end
end
end
end
describe "--log" do
subject { FPM::Command.new("fpm") }
let (:args) { [ "-s", "mock", "-t", "mock" ] }
context "when not given" do
it "should not raise an exception" do
subject.parse(args)
end
end
context "when given a valid log level" do
it "should not raise an exception" do
subject.parse(args + ["--log", "error"])
subject.parse(args + ["--log", "warn"])
subject.parse(args + ["--log", "info"])
subject.parse(args + ["--log", "debug"])
end
end
context "when given an invalid log level" do
it "should raise an exception" do
insist { subject.parse(args + ["--log", ""]) }.raises FPM::Package::InvalidArgument
insist { subject.parse(args + ["--log", "whatever"]) }.raises FPM::Package::InvalidArgument
insist { subject.parse(args + ["--log", "fatal"]) }.raises FPM::Package::InvalidArgument
end
end
end
end
|
Changes to spec/fpm/package/deb_spec.rb.
| ︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
it "should lowercase the package name" do
insist { subject.name } == subject.name.downcase
end
it "should replace underscores with dashes in the package name" do
reject { subject.name }.include?("_")
end
end
describe "#output" do
before :all do
# output a package, use it as the input, set the subject to that input
# package. This helps ensure that we can write and read packages
# properly.
| > > > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
it "should lowercase the package name" do
insist { subject.name } == subject.name.downcase
end
it "should replace underscores with dashes in the package name" do
reject { subject.name }.include?("_")
end
it "should replace spaces with dashes in the package name" do
reject { subject.name }.include?(" ")
end
end
describe "#output" do
before :all do
# output a package, use it as the input, set the subject to that input
# package. This helps ensure that we can write and read packages
# properly.
|
| ︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
@original.attributes[:deb_build_depends] << 'something-else > 0.0.0'
@original.attributes[:deb_build_depends] << 'something-else < 1.0.0'
@original.attributes[:deb_priority] = "fizzle"
@original.attributes[:deb_field_given?] = true
@original.attributes[:deb_field] = { "foo" => "bar" }
@original.output(@target)
@input = FPM::Package::Deb.new
@input.input(@target)
end
after :all do
@original.cleanup
@input.cleanup
end # after
context "package attributes" do
it "should have the correct name" do
insist { @input.name } == @original.name
end
it "should have the correct version" do
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
@original.attributes[:deb_build_depends] << 'something-else > 0.0.0'
@original.attributes[:deb_build_depends] << 'something-else < 1.0.0'
@original.attributes[:deb_priority] = "fizzle"
@original.attributes[:deb_field_given?] = true
@original.attributes[:deb_field] = { "foo" => "bar" }
@original.attributes[:deb_meta_files] = %w[meta_test triggers].map { |fn|
File.expand_path("../../../fixtures/deb/#{fn}", __FILE__)
}
@original.attributes[:deb_interest] = ['asdf', 'hjkl']
@original.attributes[:deb_activate] = ['qwer', 'uiop']
@original.output(@target)
@input = FPM::Package::Deb.new
@input.input(@target)
end
after :all do
@original.cleanup
@input.cleanup
end # after
context "when the deb's control section is extracted" do
before :all do
tmp_control = Tempfile.new("fpm-test-deb-control")
@control_extracted = tmp_control.path
tmp_control.unlink
system("dpkg-deb -e '#{@target}' '#{@control_extracted}'") or \
raise "couldn't extract test deb"
end
it "should have the requested meta file in the control archive" do
File.open(File.join(@control_extracted, 'meta_test')) do |f|
insist { f.read.chomp } == "asdf"
end
end
it "should have the requested triggers in the triggers file" do
triggers = File.open(File.join(@control_extracted, 'triggers')) do |f|
f.read
end
reject { triggers =~ /^interest from-meta-file$/ }.nil?
reject { triggers =~ /^interest asdf$/ }.nil?
reject { triggers =~ /^interest hjkl$/ }.nil?
reject { triggers =~ /^activate qwer$/ }.nil?
reject { triggers =~ /^activate uiop$/ }.nil?
insist { triggers[-1] } == ?\n
end
after :all do
FileUtils.rm_rf @control_extracted
end
end
context "package attributes" do
it "should have the correct name" do
insist { @input.name } == @original.name
end
it "should have the correct version" do
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
@deb.cleanup
FileUtils.rm_r @staging_path if File.exists? @staging_path
end # after
context "when run against lintian", :if => have_lintian do
lintian_errors_to_ignore = [
"no-copyright-file",
"non-standard-file-permissions-for-etc-init.d-script"
]
it "should return no errors" do
lintian_output = %x{lintian #{@target} --suppress-tags #{lintian_errors_to_ignore.join(",")}}
expect($?).to eq(0), lintian_output
end
end
end
end # describe FPM::Package::Deb
| > | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
@deb.cleanup
FileUtils.rm_r @staging_path if File.exists? @staging_path
end # after
context "when run against lintian", :if => have_lintian do
lintian_errors_to_ignore = [
"no-copyright-file",
"init.d-script-missing-lsb-section",
"non-standard-file-permissions-for-etc-init.d-script"
]
it "should return no errors" do
lintian_output = %x{lintian #{@target} --suppress-tags #{lintian_errors_to_ignore.join(",")}}
expect($?).to eq(0), lintian_output
end
end
end
end # describe FPM::Package::Deb
|
Changes to spec/fpm/package/dir_spec.rb.
| ︙ | ︙ | |||
123 124 125 126 127 128 129 130 |
subject.attributes[:chdir] = tmpdir
subject.attributes[:prefix] = "/foo"
subject.input(File.join("a", "a=b"))
subject.output(output)
insist { File }.exist?(File.join(output, "foo", "a", "a=b"))
end
end
end # describe FPM::Package::Dir
| > > > > > > > > > > > > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
subject.attributes[:chdir] = tmpdir
subject.attributes[:prefix] = "/foo"
subject.input(File.join("a", "a=b"))
subject.output(output)
insist { File }.exist?(File.join(output, "foo", "a", "a=b"))
end
end
context "SYMLINKS." do
let(:path) { Stud::Temporary.pathname }
let(:broken_target) { File.join("no", "such", "path", "here", rand(1000).to_s, rand(1000).to_s) }
before do
File.symlink(broken_target, path)
end
after do
File.unlink(path)
end
it "should copy a broken symlink because it shouldn't be following symlinks to begin with" do
subject.input(path)
end
end
end # describe FPM::Package::Dir
|
Changes to spec/fpm/package/gem_spec.rb.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
let (:example_gem) do
File.expand_path("../../fixtures/gem/example/example-1.0.gem", File.dirname(__FILE__))
end
after :each do
subject.cleanup
end
| | > > > > > > > > > > > > > > > > > > > > > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
let (:example_gem) do
File.expand_path("../../fixtures/gem/example/example-1.0.gem", File.dirname(__FILE__))
end
after :each do
subject.cleanup
end
context "when :gem_version_bins? is true" do
before :each do
subject.attributes[:gem_version_bins?] = true
subject.attributes[:gem_bin_path] = '/usr/bin'
end
it "it should append the version to binaries" do
subject.input(example_gem)
insist { ::Dir.entries(File.join(subject.staging_path, "/usr/bin")) }.include?("example-1.0.0")
end
end
context "when :gem_version_bins? is false" do
before :each do
subject.attributes[:gem_version_bins?] = false
subject.attributes[:gem_bin_path] = '/usr/bin'
end
it "it should not append the version to binaries" do
subject.input(example_gem)
insist { ::Dir.entries(File.join(subject.staging_path, "/usr/bin")) }.include?("example")
end
end
context "when :gem_fix_name? is true" do
before :each do
subject.attributes[:gem_fix_name?] = true
end
context "and :gem_package_name_prefix is nil/default" do
it "should prefix the package with 'gem-'" do
|
| ︙ | ︙ |
Added spec/fpm/package/npm_spec.rb.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
require "spec_setup"
require "fpm" # local
require "fpm/package/npm" # local
have_npm = program_exists?("npm")
if !have_npm
Cabin::Channel.get("rspec") \
.warn("Skipping NPM tests because 'npm' isn't in your PATH")
end
describe FPM::Package::NPM do
after do
subject.cleanup
end
describe "::default_prefix", :if => have_npm do
it "should provide a valid default_prefix" do
stat = File.stat(FPM::Package::NPM.default_prefix)
insist { stat }.directory?
end
end
end # describe FPM::Package::NPM
|
Changes to spec/fpm/package/python_spec.rb.
| ︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 136 137 |
insist { subject.dependencies.sort } == ["rtxt-dep1 > 0.1", "rtxt-dep2 = 0.1"]
end
end
end
context "python_scripts_executable is set" do
it "should have scripts with a custom hashbang line" do
subject.attributes[:python_scripts_executable] = "fancypants"
subject.input("django")
# Get the default scripts install directory and use it to find django-admin.py from Django
# Then let's make sure the scripts executable setting worked!
| > | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
insist { subject.dependencies.sort } == ["rtxt-dep1 > 0.1", "rtxt-dep2 = 0.1"]
end
end
end
context "python_scripts_executable is set" do
it "should have scripts with a custom hashbang line" do
subject.attributes[:python_install_bin] = '/usr/bin'
subject.attributes[:python_scripts_executable] = "fancypants"
subject.input("django")
# Get the default scripts install directory and use it to find django-admin.py from Django
# Then let's make sure the scripts executable setting worked!
python_bindir = %x{python -c 'from distutils.sysconfig import get_config_var; print(get_config_var("BINDIR"))'}.chomp
path = subject.staging_path(File.join(python_bindir, "django-admin.py"))
# Read the first line (the hashbang line) of the django-admin.py script
fd = File.new(path, "r")
topline = fd.readline
fd.close
insist { topline.chomp } == "#!fancypants"
end
end
end # describe FPM::Package::Python
|
Changes to spec/fpm/package/rpm_spec.rb.
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
end
after :each do
subject.cleanup
end
it "should set the user and group of each file in the RPM" do
| | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
end
after :each do
subject.cleanup
end
it "should set the user and group of each file in the RPM" do
expect(subject.rpmspec).to include('%defattr(-,root,root,-')
end
end # context
context "non-default user and group" do
before :each do
subject.attributes[:rpm_user] = "some_user"
subject.attributes[:rpm_group] = "some_group"
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
end
after :each do
subject.cleanup
end
it "should set the user and group of each file in the RPM" do
| | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
end
after :each do
subject.cleanup
end
it "should set the user and group of each file in the RPM" do
expect(subject.rpmspec).to include('%defattr(-,some_user,some_group,-')
end
end # context
end
describe "#output", :if => program_exists?("rpmbuild") do
context "architecture" do
it "can be basically anything" do
|
| ︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
subject.provides << "bacon = 1.0"
# TODO(sissel): This api sucks, yo.
subject.scripts[:before_install] = "example before_install"
subject.scripts[:after_install] = "example after_install"
subject.scripts[:before_remove] = "example before_remove"
subject.scripts[:after_remove] = "example after_remove"
# Write the rpm out
subject.output(@target)
# Read the rpm
@rpm = ::RPM::File.new(@target)
| > > > > > > > > > > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
subject.provides << "bacon = 1.0"
# TODO(sissel): This api sucks, yo.
subject.scripts[:before_install] = "example before_install"
subject.scripts[:after_install] = "example after_install"
subject.scripts[:before_remove] = "example before_remove"
subject.scripts[:after_remove] = "example after_remove"
subject.scripts[:rpm_verifyscript] = "example rpm_verifyscript"
subject.scripts[:rpm_posttrans] = "example rpm_posttrans"
subject.scripts[:rpm_pretrans] = "example rpm_pretrans"
# Test for triggers #626
subject.attributes[:rpm_trigger_before_install] = [["test","#!/bin/sh\necho before_install trigger executed\n"]]
subject.attributes[:rpm_trigger_after_install] = [["test","#!/bin/sh\necho after_install trigger executed\n"]]
subject.attributes[:rpm_trigger_before_uninstall] = [["test","#!/bin/sh\necho before_uninstall trigger executed\n"]]
subject.attributes[:rpm_trigger_after_target_uninstall] = [["test","#!/bin/sh\necho after_target_uninstall trigger executed\n"]]
# Write the rpm out
subject.output(@target)
# Read the rpm
@rpm = ::RPM::File.new(@target)
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
insist { @rpm.tags[:preunprog] } == "/bin/sh"
end
it "should have the correct 'postun' script" do
insist { @rpm.tags[:postun] } == "example after_remove"
insist { @rpm.tags[:postunprog] } == "/bin/sh"
end
it "should have the correct 'prein' script" do
insist { @rpm.tags[:prein] } == "example before_install"
insist { @rpm.tags[:preinprog] } == "/bin/sh"
end
it "should have the correct 'postin' script" do
insist { @rpm.tags[:postin] } == "example after_install"
insist { @rpm.tags[:postinprog] } == "/bin/sh"
end
it "should use md5/gzip by default" do
insist { @rpmtags[:payloadcompressor] } == "gzip"
# For whatever reason, the 'filedigestalgo' tag is an array of numbers.
# I only ever see one element in this array, so just do value.first
#
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
insist { @rpm.tags[:preunprog] } == "/bin/sh"
end
it "should have the correct 'postun' script" do
insist { @rpm.tags[:postun] } == "example after_remove"
insist { @rpm.tags[:postunprog] } == "/bin/sh"
end
it "should have the correct 'verify' script" do
insist { @rpm.tags[:verifyscript] } == "example rpm_verifyscript"
insist { @rpm.tags[:verifyscriptprog] } == "/bin/sh"
end
it "should have the correct 'pretrans' script" do
insist { @rpm.tags[:pretrans] } == "example rpm_pretrans"
insist { @rpm.tags[:pretransprog] } == "/bin/sh"
end
it "should have the correct 'posttrans' script" do
insist { @rpm.tags[:posttrans] } == "example rpm_posttrans"
insist { @rpm.tags[:posttransprog] } == "/bin/sh"
end
it "should have the correct 'prein' script" do
insist { @rpm.tags[:prein] } == "example before_install"
insist { @rpm.tags[:preinprog] } == "/bin/sh"
end
it "should have the correct 'postin' script" do
insist { @rpm.tags[:postin] } == "example after_install"
insist { @rpm.tags[:postinprog] } == "/bin/sh"
end
it "should have the correct 'before_install' trigger script" do
insist { @rpm.tags[:triggername][0] } == "test"
insist { @rpm.tags[:triggerversion][0] } == ""
insist { @rpm.tags[:triggerflags][0] & (1 << 25)} == ( 1 << 25) # See FPM::Package::RPM#rpm_get_trigger_type
insist { @rpm.tags[:triggerindex][0] } == 0
insist { @rpm.tags[:triggerscriptprog][0] } == "/bin/sh"
insist { @rpm.tags[:triggerscripts][0] } == "#!/bin/sh\necho before_install trigger executed"
end
it "should have the correct 'after_install' trigger script" do
insist { @rpm.tags[:triggername][1] } == "test"
insist { @rpm.tags[:triggerversion][1] } == ""
insist { @rpm.tags[:triggerflags][1] & (1 << 16)} == ( 1 << 16) # See FPM::Package::RPM#rpm_get_trigger_type
insist { @rpm.tags[:triggerindex][1] } == 1
insist { @rpm.tags[:triggerscriptprog][1] } == "/bin/sh"
insist { @rpm.tags[:triggerscripts][1] } == "#!/bin/sh\necho after_install trigger executed"
end
it "should have the correct 'before_uninstall' trigger script" do
insist { @rpm.tags[:triggername][2] } == "test"
insist { @rpm.tags[:triggerversion][2] } == ""
insist { @rpm.tags[:triggerflags][2] & (1 << 17)} == ( 1 << 17) # See FPM::Package::RPM#rpm_get_trigger_type
insist { @rpm.tags[:triggerindex][2] } == 2
insist { @rpm.tags[:triggerscriptprog][2] } == "/bin/sh"
insist { @rpm.tags[:triggerscripts][2] } == "#!/bin/sh\necho before_uninstall trigger executed"
end
it "should have the correct 'after_target_uninstall' trigger script" do
insist { @rpm.tags[:triggername][3] } == "test"
insist { @rpm.tags[:triggerversion][3] } == ""
insist { @rpm.tags[:triggerflags][3] & (1 << 18)} == ( 1 << 18) # See FPM::Package::RPM#rpm_get_trigger_type
insist { @rpm.tags[:triggerindex][3] } == 3
insist { @rpm.tags[:triggerscriptprog][3] } == "/bin/sh"
insist { @rpm.tags[:triggerscripts][3] } == "#!/bin/sh\necho after_target_uninstall trigger executed"
end
it "should use md5/gzip by default" do
insist { @rpmtags[:payloadcompressor] } == "gzip"
# For whatever reason, the 'filedigestalgo' tag is an array of numbers.
# I only ever see one element in this array, so just do value.first
#
|
| ︙ | ︙ |
Changes to spec/fpm/util_spec.rb.
1 2 3 4 5 6 7 8 9 10 |
require "spec_setup"
require "fpm" # local
require "fpm/util" # local
require "stud/temporary"
describe FPM::Util do
subject do
Class.new do
include FPM::Util
| < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
require "spec_setup"
require "fpm" # local
require "fpm/util" # local
require "stud/temporary"
describe FPM::Util do
subject do
Class.new do
include FPM::Util
end.new
end
context "#copy_entry" do
context "when given files that are hardlinks" do
it "should keep those files as hardlinks" do
Stud::Temporary.directory do |path|
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
it "should still run commands correctly" do
# This will raise an exception if we can't run it at all.
subject.safesystem("true")
end
end
end
| > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
it "should still run commands correctly" do
# This will raise an exception if we can't run it at all.
subject.safesystem("true")
end
end
end
describe "#expand_pessimistic_constraints" do
it 'convert 2 piece versions' do
constraint = 'bundle ~> 1.2'
expected_lower = 'bundle >= 1.2'
expected_upper = 'bundle < 2.0'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include expected_lower
expect(derived_constraint).to include expected_upper
end
it 'convert 3 piece versions' do
constraint = 'zippy ~> 1.2.3'
expected_lower = 'zippy >= 1.2.3'
expected_upper = 'zippy < 1.3.0'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include expected_lower
expect(derived_constraint).to include expected_upper
end
it 'does not convert where not needed when the operator is > ' do
constraint = 'zippy > 1.2.3'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include constraint
end
it 'does not convert where not needed when when the operator is < ' do
constraint = 'zippy < 1.2.3'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include constraint
end
it 'does not convert where not needed when when the operator is <= ' do
constraint = 'zippy <= 1.2.3'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include constraint
end
it 'does not convert where not needed when when the operator is >= ' do
constraint = 'zippy >= 1.2.3'
derived_constraint = subject.expand_pessimistic_constraints(constraint)
expect(derived_constraint).to include constraint
end
end
end
|
Changes to spec/spec_setup.rb.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 | require "fpm/util" include FPM::Util # Enable debug logs if requested. if $DEBUG or ENV["DEBUG"] Cabin::Channel.get.level = :debug Cabin::Channel.get.subscribe(STDOUT) | > > > > > > > | > > > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
require "fpm/util"
include FPM::Util
# Enable debug logs if requested.
if $DEBUG or ENV["DEBUG"]
Cabin::Channel.get.level = :debug
Cabin::Channel.get.subscribe(STDOUT)
else
class << Cabin::Channel.get
alias_method :subscribe_, :subscribe
def subscribe(io)
return if io == STDOUT
subscribe_(io)
#puts caller.join("\n")
end
end
end
Cabin::Channel.get.level = :error
spec_logger = Cabin::Channel.get("rspec")
spec_logger.subscribe(STDOUT)
spec_logger.level = :error
# Quiet the output of all system() calls
module Kernel
alias_method :orig_system, :system
def system(*args)
old_stdout = $stdout.clone
old_stderr = $stderr.clone
|
| ︙ | ︙ |
Added templates/appstream.erb.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="UTF-8"?>
<!-- Maintainer: <%=h maintainer %>, autogenerated by `fpm -u appdata` -->
<component type="desktop">
<id><%=h name %>.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license><%=h license %></project_license>
<name><%=h name %></name>
<% summary, *description = ((description) or "no description given").split("\n") -%>
<summary><%=h summary %></summary>
<description>
<p><%=
h description[0,25].join("\n") # should actually generate mini-html with <p> and <ul>+<li>
%></p>
</description>
<screenshots>
<screenshot type="default">
<image>http://freshcode.club/img/nopreview.png</image>
<caption>Main window..</caption>
</screenshot>
</screenshots>
<url type="homepage"><%=h (url or "data:,") %></url>
<updatecontact><%=h maintainer %></updatecontact>
<!--project_group><%=h category %></project_group-->
</component>
|
Changes to templates/deb.erb.
1 2 3 4 5 6 7 8 9 10 11 |
Package: <%= name %>
Version: <%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>
License: <%= license %>
Vendor: <%= vendor %>
Architecture: <%= architecture %>
Maintainer: <%= maintainer %>
Installed-Size: <%= attributes[:deb_installed_size] %>
<% if !dependencies.empty? and !attributes[:no_depends?] -%>
Depends: <%= dependencies.collect { |d| fix_dependency(d) }.flatten.join(", ") %>
<% end -%>
<% if !conflicts.empty? -%>
| > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
Package: <%= name %>
Version: <%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>
License: <%= license %>
<% if !vendor.nil? and !vendor.empty? -%>
Vendor: <%= vendor %>
<% end -%>
Architecture: <%= architecture %>
Maintainer: <%= maintainer %>
Installed-Size: <%= attributes[:deb_installed_size] %>
<% if !dependencies.empty? and !attributes[:no_depends?] -%>
Depends: <%= dependencies.collect { |d| fix_dependency(d) }.flatten.join(", ") %>
<% end -%>
<% if !conflicts.empty? -%>
|
| ︙ | ︙ |
Added templates/deb/changelog.erb.
> > > > > | 1 2 3 4 5 |
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; urgency=medium
* Package created with FPM.
-- <%= maintainer %> <%= Time.now.strftime("%a, %d %b %Y %T %z") %>
|
Added templates/deb/postinst_upgrade.sh.erb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
after_upgrade() {
<% if script?(:after_upgrade) -%>
<%= script(:after_upgrade) %>
<% end -%>
}
after_install() {
<% if script?(:after_install) -%>
<%= script(:after_install) %>
<% end -%>
}
if [ "${1}" = "configure" -a -z "${2}" ]
then
# "after install" here
after_install
elif [ "${1}" = "configure" -a -n "${2}" ]
then
upgradeFromVersion="${2}"
# "after upgrade" here
# NOTE: This slot is also used when deb packages are removed,
# but their config files aren't, but a newer version of the
# package is installed later, called "Config-Files" state.
# basically, that still looks a _lot_ like an upgrade to me.
after_upgrade "${2}"
elif echo "${1}" | grep -E -q "(abort|fail)"
then
echo "Failed to install before the post-installation script was run." >&2
exit 1
fi
|
Added templates/deb/postrm_upgrade.sh.erb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
after_remove() {
<% if script?(:after_remove) -%>
<%= script(:after_remove) %>
<% end -%>
}
dummy() {
}
if [ "${1}" = "remove" ]
then
# "after remove" goes here
after_remove
elif [ "${1}" = "purge" -a -z "${2}" ]
then
# like "on remove", but executes after dpkg deletes config files
# 'apt-get purge' runs 'on remove' section, then this section.
# Maybe we ignore this; it seems really fine-grained.
# There is no equivalent in RPM or ARCH. A debian-specific argument
# might perhaps be used here, but most people
# probably don't need it.
dummy
elif [ "${1}" = "upgrade" ]
then
# This represents the case where the old package's postrm is called after
# the 'preinst' script is called.
# We should ignore this and just use 'preinst upgrade' and
# 'postinst configure'. The newly installed package should do the
# upgrade, not the uninstalled one, since it can't anticipate what new
# things it will have to do to upgrade for the new version.
dummy
elif echo "${1}" | grep -E -q '(fail|abort)'
then
echo "Failed to install before the post-removal script was run." >&2
exit 1
fi
|
Added templates/deb/preinst_upgrade.sh.erb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
before_upgrade() {
<% if script?(:before_upgrade) -%>
<%= script(:before_upgrade) %>
<% end -%>
}
before_install() {
<% if script?(:before_install) -%>
<%= script(:before_install) %>
<% end -%>
}
if [ "${1}" = "install" -a -z "${2}" ]
then
before_install
elif [ "${1}" = "upgrade" -a -n "${2}" ]
then
upgradeFromVersion="${2}"
before_upgrade "${2}"
elif [ "${1}" = "install" -a -n "${2}" ]
then
upgradeFromVersion="${2}"
# Executed when a package is removed but its config files aren't,
# and a new version is installed.
# Looks a _lot_ like an upgrade case, I say we just execute the
# same "before upgrade" script as in the previous case
before_upgrade "${2}"
elif echo "${1}" | grep -E -q '(fail|abort)'
then
echo "Failed to install before the pre-installation script was run." >&2
exit 1
fi
|
Added templates/deb/prerm_upgrade.sh.erb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
before_remove() {
<% if script?(:before_remove) -%>
<%= script(:before_remove) %>
<% end -%>
}
dummy() {
}
if [ "${1}" = "remove" -a -z "${2}" ]
then
# "before remove" goes here
before_remove
elif [ "${1}" = "upgrade" ]
then
upgradeVersionTo="${2}"
# Executed before the old version is removed
# upon upgrade.
# We should generally not do anything here. The newly installed package
# should do the upgrade, not the uninstalled one, since it can't anticipate
# what new things it will have to do to upgrade for the new version.
dummy "${2}"
elif echo "${1}" | grep -E -q "(fail|abort)"
then
echo "Failed to install before the pre-removal script was run." >&2
exit 1
fi
|
Added templates/desktop.erb.
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 |
[Desktop Entry]
Name=<%= @name %>
;GenericName=<%= @name %>
Comment=<%= @description.split("\n").first or "" %>
Categories=<%= @category %>
Exec=<%= @name %>
Icon=<%= @name %>
Terminal=false
Type=Application
MimeType=x-scheme-handler/<%= @name %>;
|
Added templates/listaller/doap.erb.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns="http://usefulinc.com/ns/doap#">
<name><%=h @name %></name>
<homepage rdf:resource="<%=h @url %>" />
<created><%= Time.now.strftime("%Y-%m-%d") %></created>
<shortdesc xml:lang="en">
<%=h (description.split("\n") or ["no description"]).first %>
</shortdesc>
<description xml:lang="en">
<%=h (description.split("\n") or ["", "no details available"])[1,50].join("\n ") %>
</description>
<maintainer>
<foaf:Person>
<foaf:name><%=h @maintainer %></foaf:name>
</foaf:Person>
</maintainer>
<release>
<Version>
<name>stable</name>
<created><%=h Time.now.strftime("%Y-%m-%d") %></created>
<revision><%=h @version %></revision>
</Version>
</release>
<license rdf:resource="data:,<%=h @license %>" />
</Project>
|
Added templates/listaller/files.erb.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# IPK file list for <%= name %>
# very crude, does not separate data files yet
:: %APP%
<%= files.grep(/\.desktop$/).join("\n") %>
:: %INST%
<% files.each do |fn| %><%= fn %>
<% end %>
:: %ICON-16%
# none
:: %ICON-32%
# none
:: %ICON-64%
# none
:: %ICON-128%
# none
# could probably just .grep() for icons here too..?
|
Added templates/listaller/pkoptions.erb.
> > > > | 1 2 3 4 | Version: 1.1 AutoFindDeps: <%= attributes[:ipk_relocatable] ? "true" : "false" %> |
Changes to templates/rpm.erb.
| ︙ | ︙ | |||
104 105 106 107 108 109 110 | %install # noop %clean # noop | | | | | | < < > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
%install
# noop
%clean
# noop
<%# This next section puts any %pre, %post, %preun, %postun, %verifyscript, %pretrans or %posttrans scripts %>
<%
scriptmap = {
:rpm_verifyscript => "verifyscript",
:rpm_posttrans => "posttrans",
:rpm_pretrans => "pretrans"
}
-%>
<% if script?(:before_upgrade) or script?(:after_upgrade) -%>
<% if script?(:before_upgrade) or script?(:before_install) -%>
%pre
upgrade() {
<% if script?(:before_upgrade) -%>
<%= script(:before_upgrade) %>
<% end -%>
}
install() {
<% if script?(:before_install) -%>
<%= script(:before_install) %>
<% end -%>
}
if [ "${1}" -eq 1 ]
then
install
# "before install" goes here
elif [ "${1}" -gt 1 ]
then
upgrade
fi
<% end -%>
<% if script?(:after_upgrade) or script?(:after_install) -%>
%pre
upgrade() {
<% if script?(:after_upgrade) -%>
<%= script(:after_upgrade) %>
<% end -%>
}
install() {
<% if script?(:after_install) -%>
<%= script(:after_install) %>
<% end -%>
}
if [ "${1}" -eq 1 ]
then
install
# "before install" goes here
elif [ "${1}" -gt 1 ]
then
upgrade
fi
<% end -%>
<% if script?(:before_remove) -%>
%preun
if [ "${1}" -eq 0 ]
then
<%= script(:before_remove) %>
fi
<% end -%>
<% if script?(:after_remove) -%>
%postun
if [ "${1}" -eq 0 ]
then
<%= script(:after_remove) %>
fi
<% end -%>
<% else
other_scriptmap = {
:before_install => "pre",
:after_install => "post",
:before_remove => "preun",
:after_remove => "postun"
}
scriptmap.merge!(other_scriptmap)
end
-%>
<% scriptmap.each do |name, rpmname| -%>
<% if script?(name) -%>
%<%= rpmname %>
<%= script(name) %>
<% end -%>
<% end -%>
<%# This section adds any triggers, as ordered in the command line -%>
<%
triggermap = {
:before_install => "prein",
:after_install => "in",
:before_uninstall => "un",
:after_target_uninstall => "postun"
}
triggermap.each do |name, rpmtype|
(attributes["rpm_trigger_#{name}".to_sym] or []).each do |trigger_name, trigger_script, trigger_scriptprog| -%>
%trigger<%= rpmtype -%> <%= trigger_scriptprog -%> -- <%= trigger_name %>
<%= trigger_script %>
<% end -%>
<% end -%>
%files
%defattr(<%= attributes[:rpm_defattrfile] %>,<%= attributes[:rpm_user] || "root" %>,<%= attributes[:rpm_group] || "root" %>,<%= attributes[:rpm_defattrdir] %>)
<%# Output config files and then regular files. -%>
<% config_files.each do |path| -%>
|
| ︙ | ︙ |
Changes to templates/sh.erb.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
# is used. Then it is $INSTALL_ROOT/releases/<datestamp>
# CURRENT_DIR = if -c argument is used, this is set to the $INSTALL_ROOT/current which is
# symlinked to INSTALL_DIR
# VERBOSE = is set if the package was called with -v for verbose output
function main() {
set_install_dir
| | > | < | < | < | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# is used. Then it is $INSTALL_ROOT/releases/<datestamp>
# CURRENT_DIR = if -c argument is used, this is set to the $INSTALL_ROOT/current which is
# symlinked to INSTALL_DIR
# VERBOSE = is set if the package was called with -v for verbose output
function main() {
set_install_dir
if ! slug_already_current ; then
create_pid
wait_for_others
kill_others
set_owner
unpack_payload
if [ "$UNPACK_ONLY" == "1" ] ; then
echo "Unpacking complete, not moving symlinks or restarting because unpack only was specified."
else
create_symlinks
set +e # don't exit on errors to allow us to clean up
if ! run_post_install ; then
revert_symlinks
log "Installation failed."
exit 1
else
clean_out_old_releases
log "Installation complete."
fi
fi
else
echo "This slug is already installed in 'current'. Specify -f to force reinstall. Exiting."
fi
}
# check if this slug is already running and exit unless `force` specified
# Note: this only works with RELEASE_ID is used
function slug_already_current(){
local this_slug=$(basename $0 .slug)
local current=$(basename "$(readlink ${INSTALL_ROOT}/current)")
log "'current' symlink points to slug: ${current}"
if [ "$this_slug" == "$current" ] ; then
if [ "$FORCE" == "1" ] ; then
log "Force was specified. Proceeding with install after renaming live directory to allow running service to shutdown correctly."
local real_dir=$(readlink ${INSTALL_ROOT}/current)
if [ -f ${real_dir}.old ] ; then
# remove that .old directory, if needed
rm -rf ${real_dir}.old
fi
mv ${real_dir} ${real_dir}.old
mkdir -p ${real_dir}
else
return 0;
fi
fi
return 1;
}
# deletes the PID file for this installation
function delete_pid(){
rm -f ${INSTALL_ROOT}/$$.pid 2> /dev/null
}
|
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
# write out metadata for future installs
function save_environment(){
local METADATA=$1
echo -n "" > ${METADATA} # empty file
# just piping env to a file doesn't quote the variables. This does
| | | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# write out metadata for future installs
function save_environment(){
local METADATA=$1
echo -n "" > ${METADATA} # empty file
# just piping env to a file doesn't quote the variables. This does
# filter out multiline junk, _, and functions. _ is a readonly variable.
env | grep -v "^_=" | grep -v "^[^=(]*()=" | egrep "^[^ ]+=" | while read ENVVAR ; do
local NAME=${ENVVAR%%=*}
# sed is to preserve variable values with dollars (for escaped variables or $() style command replacement),
# and command replacement backticks
# Escaped parens captures backward reference \1 which gets replaced with backslash and \1 to esape them in the saved
# variable value
local VALUE=$(eval echo '$'$NAME | sed 's/\([$`]\)/\\\1/g')
echo "export $NAME=\"$VALUE\"" >> ${METADATA}
|
| ︙ | ︙ |
Added xpm.gemspec.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
require File.join(File.dirname(__FILE__), "lib/fpm/version")
Gem::Specification.new do |spec|
files = []
files += Dir["{bin,lib,templates}/**/*"]
files << "LICENSE"
files << "CONTRIBUTORS"
files << "CHANGELIST"
spec.name = "xpm"
spec.version = FPM::VERSION
spec.summary = "fpm - package building and mangling"
spec.description = "Convert directories, rpms, python eggs, rubygems, and " \
"more to rpms, debs, solaris packages and more. Win at package " \
"management without wasting pointless hours debugging bad rpm specs!"
spec.license = "MIT-like"
# For parsing JSON (required for some Python support, etc)
# http://flori.github.com/json/doc/index.html
spec.add_dependency("json", ">= 1.7.7") # license: Ruby License
# For logging
# https://github.com/jordansissel/ruby-cabin
spec.add_dependency("cabin", ">= 0.6.0") # license: Apache 2
# For backports to older rubies
# https://github.com/marcandre/backports
spec.add_dependency("backports", ">= 2.6.2") # license: MIT
# For reading and writing rpms
spec.add_dependency("arr-pm", "~> 0.0.9") # license: Apache 2
# For command-line flag support
# https://github.com/mdub/clamp/blob/master/README.markdown
spec.add_dependency("clamp", "~> 0.6") # license: MIT
# For starting external processes across various ruby interpreters
spec.add_dependency("childprocess") # license: ???
# For calling functions in dynamic libraries
spec.add_dependency("ffi") # license: GPL3/LGPL3
spec.add_development_dependency("rspec", "~> 3.0.0") # license: MIT (according to wikipedia)
spec.add_development_dependency("insist", "~> 0.0.5") # license: ???
spec.add_development_dependency("pry")
spec.add_development_dependency("stud")
spec.files = files
spec.require_paths << "lib"
spec.bindir = "bin"
spec.executables << "xpm"
spec.author = "Mario Salzer"
spec.email = "mario#include-once:org"
spec.homepage = "http://fossil.include-once.org/xpm/"
end
|