Compare commits

...

173 Commits

Author SHA1 Message Date
Jordan Sissel e69cdca562 v1.17.0 :) 2025-10-02 16:37:32 -07:00
Jordan Sissel 5b9d922c84
Merge pull request #2109 from jordansissel/cleanup/remove-old-unused-files
Remove some older unused things
2025-10-02 13:33:29 -07:00
Jordan Sissel 93eaefd383 Remove some older unused things 2025-10-02 13:29:24 -07:00
Jordan Sissel 4cf32c5bc7 Update version in docs 2025-10-02 12:41:05 -07:00
Jordan Sissel 6115fe4c44
Merge pull request #2108 from jordansissel/issue/fix-readthedocs
Make readthedocs.org build the docs once again.
2025-10-02 12:30:06 -07:00
Jordan Sissel 781108fbf6 Update sphinx's project version to match fpm's just in case it's still used anywhere 2025-10-02 12:27:25 -07:00
Jordan Sissel 9c78876415 Fix?
> ModuleNotFoundError: No module named 'sphinx_rtd_theme'
2025-10-02 12:22:15 -07:00
Jordan Sissel a066c4447d Try to fix another error
>     sphinx.errors.ThemeError: no theme named 'sphinx_rtd_theme' found (missing theme.toml?)
2025-10-02 12:20:32 -07:00
Jordan Sissel e42a069c4f Update copyright text 2025-10-02 12:12:55 -07:00
Jordan Sissel 829b897456 A new error -
> ImportError: cannot import name 'Meta' from 'docutils.parsers.rst.directives.misc' (

This commit tries to resolve this by removing the 'docutils<0.18'
requirements entry.
2025-10-02 12:11:03 -07:00
Jordan Sissel 08e663916d Another missing required setting that wasn't required previously..
> Missing Sphinx configuration key The sphinx.configuration key is missing.

Reference: https://about.readthedocs.com/blog/2024/12/deprecate-config-files-without-sphinx-or-mkdocs-config/
2025-10-02 12:07:51 -07:00
Jordan Sissel e15d2c3a1f `build.tools` setting is also required. 2025-10-02 12:05:27 -07:00
Jordan Sissel 249d3b997f Try to fix readthedocs build error
> Config file validation error Config validation error in build.os. Value build not found.
2025-10-02 12:01:04 -07:00
Jordan Sissel bdbe351796
Merge pull request #2107 from jordansissel/release-prep/1.17.0
Release prep for v1.17.0
2025-10-02 11:56:31 -07:00
Jordan Sissel 45f78973e7 Set pre-release version 2025-10-02 11:56:05 -07:00
Jordan Sissel db535710f7 Fix link syntax 2025-10-02 11:53:58 -07:00
Jordan Sissel 6bf2885139 Generate docs 2025-10-02 11:49:16 -07:00
Jordan Sissel 644e538311 Add docs building to release-prep 2025-10-02 11:48:59 -07:00
Jordan Sissel 9c415246f2 Fix reST syntax 2025-10-02 11:48:37 -07:00
Jordan Sissel e8cd0920d7 s/podman/docker/ 2025-10-02 11:43:22 -07:00
Jordan Sissel 22d44e1c49 Use debian/apt packages for python deps instead of pip... mainly because Debian's pip3 errors when you try to use it warning to use venv instead... 2025-10-02 11:43:04 -07:00
Jordan Sissel ef63628054 Fix Sphinx warning that language needs to be set to something 2025-10-02 11:42:22 -07:00
Jordan Sissel e0c3a1fc1a Only show the general options in the 'general options' docs entry. Before this, *all* flags were included and that was not intentional. 2025-10-02 11:38:19 -07:00
Jordan Sissel e1113c24e0 Release prep - generate docs and bump version 2025-10-02 11:04:42 -07:00
Jordan Sissel 290fe0741c Reformat changelog entries slightly (no wrapping). Also found an identical PR for #2011 in #2054 and noted that w/ contributor details 2025-10-02 10:12:25 -07:00
Jordan Sissel b69b7e5787 More changelog updates 2025-10-02 10:02:20 -07:00
Jordan Sissel feecdfe87b WIP release changelog updates. 2025-10-02 10:02:20 -07:00
Jordan Sissel de7a214bf5 Work in progress - Prepare v1.17.0 release 2025-10-02 10:02:20 -07:00
Jordan Sissel c6069f5584 Replace ostruct with a custom class that acts almost the same.
This removes a warning in 3.4.0 and prepares for Ruby 3.5.0:
> warning: ostruct was loaded from the standard library, but will no longer
> be part of the default gems since Ruby 3.5.0. Add ostruct to your Gemfile or gemspec.

This seems preferable to adding ostruct as a gem dependency especially
since older rubies may not support the gem version of ostruct.
2025-10-01 22:36:03 -07:00
Jordan Sissel 583f7e4ba0 If the action runs with debug logging, run fpm specs with debug logging enabled 2025-10-01 22:13:44 -07:00
Jordan Sissel aa55aaca93 Python pyproject.toml and requirements.txt improvements
* --python-obey-requirements-txt behavior and tests. I swear this was
  working previously but the tests failed.
* Now supports packaging local python project directories containing
  a pyproject.toml
2025-10-01 22:13:44 -07:00
Jordan Sissel 23e7e0f554 Remove unused code and comments (fpm uses python to process dependency env markers) 2025-10-01 22:13:44 -07:00
Alexandr Zarubkin dd58d6714e Copy config file to staging if it doesn't exist there.
Fix #1823.
2025-10-01 17:36:56 -07:00
Jordan Sissel c76917a4e1 Add support for some uncommon package downloads.
* amqplib 1.0.2 downloads a file ending in .tgz instead of expected .tar.gz
* dnspython 1.15.0 has METADATA which includes a description body, but
  the metadata version is 2.0, and python core-metadata docs indicate
  v2.1 added support for description body. In this case, if the metadata
  version is 2.0, fpm now accepts a description body.

Reference: python core-metadata:
> Changed in version 2.1: This field may be specified in the message body instead.

Original report and fixes for this by @bugfood in #2002.
2025-09-30 15:25:44 -07:00
Jordan Sissel 17bc375e0b Add line continuation to keep ruby-lsp from warning about useless void context. 2025-09-30 15:25:44 -07:00
Jordan Sissel 42808ab943 Fix failing virtualenv tests
I hope this works? It works on my machine... :\

virtualenv and virtualenv-tools seem to be lesser-used these days. I
wonder if it'll be more useful to move to a different tool like venv...
2025-09-30 15:25:44 -07:00
Jordan Sissel 8e54e5ca49 Update rspec in order to use satisfy(description, &block)
Older rspec didn't allow a description to be given to satisfy(), but
newer ones do. I think I forgot to include this rspec version update
in PR #2103
2025-09-30 15:25:44 -07:00
Jordan Sissel 8b24206e19 Fix typo 2025-09-30 15:25:44 -07:00
Jordan Sissel f0e5b8abc0 Use python to parse python requires syntax and evaluate markers.
Not supported yet:
* Custom environment for marker evaluation.
* "extras" markers

Restore function to `--python-obey-requirements-txt`. This flag will
only work on python inputs that are provided as a directory, not a
wheel/tar.gz/etc.

Tests pass including the obey requirements.txt ones.

* Fix bug 'else File.directory?` -> `elsif File.directory?...` lol oops.
* Use Dir.entries instead of Dir.glob to allow python package extra
  syntax, with names like `django[bcrypt]` (Note, extras in requirements
  aren't evaluated yet)
2025-09-30 15:25:44 -07:00
Jordan Sissel b328c15ff6 Fix typo 2025-09-30 15:25:44 -07:00
Jordan Sissel 010093698a Work continues to center python package work around `pip` and away from setup.py
Working: Most python tests except for the --obey-requirements-text one
Breaking: Broke support for requirements.txt, for now.

* Deprecate --python-install-{bin,data,lib} flags since those are only
  supported by setup.py. `pip` just supports `--prefix` and maybe that's ok.
* Deprecate --python-scripts-executable for similar reasons. I don't
  think pip supports this?
* Add notes about unsupported Python dependency markers
* PythonMetadata is now a class with accessors providing name, version,
  requirements, homepage, maintainer, etc.
* Moved package url/homepage selection into PythonMetadata
* Fix bug where dependency list would have trailing spaces.
* Updated test suite with new python package name prefix selection
  (detecting python3, etc)
2025-09-30 15:25:44 -07:00
Jordan Sissel 002b42e98e Add accessors for parts of python metadata that we can use with fpm.
(Note: Not all parts of the metadata are used, and some could be used
that are not yet used...)

* Add more tests for parsing and PythonMetadata members
* maintainer: Use Maintainer-email if present, otherwise use Author-email
* license: Try the best field for using as the license label
  (License-Expression, License, or a Classifier entry)
* homepage: Try to pick the right value for the homepage, or close
  enough, since python packages can have multiple urls as project urls.
2025-09-30 15:25:44 -07:00
Jordan Sissel bc75218f37 Improvements to python wheel processing
Parsing Python METADATA files:
* Treat any 'multiple use' fields (as documented in the python
  core-metadata spec) as arrays at all times.
* If METADATA has a body, use it as the description (per spec)
* If Description comes from a header, then strip leading space-pipe text
  as per spec.
* Allow the last header (field: value) to end without a newline.
  (Using \Z regex)
  This allows the parser to process both METADATA and WHEEL files.
* Only use Project-URL as package url if one is given in METADATA
* Set package maintainer based on Maintainer-Email field

Python Environment:
* Move default python detection to a separate method

Also:
* Fix typo in `pip wheel` invocation with wrong variable name
* Remove 'json' require since it's no longer used.
2025-09-30 15:25:44 -07:00
Jordan Sissel fcee26e979 WIP to update python support to rely more on `pip`
Breaking:
* Removed calls to `setup.py` since this seems deprecated/unusable in
  almost any modern python package. [2]
* No longer work: `--python-install-data`, `--python-install-lib`, and
  `--python-install-bin`. The reason: `pip install ...` doesn't seem to
  support such flags, so there's no way to use these flags. Instead,
  you must set `--prefix` if you want to choose where the package
  installs if not the default location

New Behavior:
* When --python-bin isn't given to fpm, automatically try to find a
  correct python executable name[1]
* When --python-package-name-prefix isn't given, set a default based on
  the default --python-bin. That is, if python3 is being used, packages
  should default to a name prefix of "python3-"
* Package 'architecture' now will be "all" or "native" depending on the
  wheel file's `Root-Is-Purelib` field. It's unclear if this is the correct
  way to determine this, though.

Implementation Details:
* Parse package metadata from the wheel (METADATA and WHEEL files)
* Download pypi packages as wheels when available. Previously, fpm would
  specify `--no-binary :all:`, but no longer does.
* When source packages are only available, build a wheel locally.

Not yet tested:
* Not yet tested: Package a python module that exists in a local directory

[1] Python is sometimes available as python3, etc.
[2] https://packaging.python.org/en/latest/discussions/setup-py-deprecated/
2025-09-30 15:25:44 -07:00
Jordan Sissel 9a77831902 Move command running to verbose/info log instead of debug. 2025-09-30 15:25:44 -07:00
Jordan Sissel 873bff3820 Update cabin to fix Ruby ostruct deprecation/stdlib warning 2025-09-30 15:25:44 -07:00
Jordan Sissel 1c3cfe741e Change log message to note hardlink 2025-09-26 20:15:54 -07:00
Jordan Sissel 6025469d52 Only hardlink if the source file appears to be a hardlink and we have already copied one of it into the destination. This should fix #2102 2025-09-26 20:15:54 -07:00
Jordan Sissel fdfc384e71 Rely on copy_entry's logic to handle copying vs hardlinking. 2025-09-26 20:15:54 -07:00
Jordan Sissel 82ddba7a8c remove 'ostruct' require. ostruct's being moved out of ruby core, so this will help remove deprecation/move warnings 2025-09-26 20:15:54 -07:00
Jordan Sissel 7cc4aa7509 Add two tests to cover parts of #2102. The 'normal files' test covers the reported issue in #2102 and fails in this commit. 2025-09-26 20:15:54 -07:00
Jordan Sissel 207bd73577 Add ruby 3.4 and also run apt-get update to try and work around "apt-get install ..." failing sometimes unpredictably? 2025-09-22 13:38:06 -07:00
André Kelpe 7ef007ef5f fix RPM documentation links in docs and code (#2092)
The previous versions where all returning 404. This change points to the
new correct locations.
2025-03-05 20:42:25 -08:00
Markson Hon 3f044a426f
pacman.rb: fix aarch64 & arm7hf (#2017) 2025-03-05 20:18:15 -08:00
Jordan Sissel 4beb98f193
Merge pull request #2088 from jordansissel/issue/2087
rpm: Escape { and } characters in filenames
2025-03-05 20:15:07 -08:00
Jordan Sissel 2c301c733d Try to make {} file names work in RPM v4.18 and older.
RPM 4.19 added some changes to make it easier or at least more
consistent to escape 'unusual' filename characters. However, RPM v4.18
and earlier do not benefit from this change, so I'm hoping that
replacing {} with ? (wild-card single) characters will work on those
versions. It works fine on v4.19.
2024-12-20 20:52:45 -08:00
Jordan Sissel b10350b913 Fix bug causing DEBUG logging not to work in rspec 2024-12-19 13:34:08 -08:00
Jordan Sissel 93ac15729b Escape { and } characters in filenames
* Add test coverage for escaping { and } filename characters

Fixes #2087
2024-12-19 12:59:07 -08:00
Jordan Sissel 08dc21b8f1 Version bump and generate docs 2024-12-08 23:22:18 -08:00
Jordan Sissel f817f7a5da Update changelog prep for release. 2024-12-08 23:21:12 -08:00
Jordan Sissel f714f4b4f1
Merge pull request #2084 from jordansissel/pull/2009
When using zstd, use tar flags that are compatible with both gnu tar and bsdtar (#2009)
2024-12-08 22:53:24 -08:00
Jordan Sissel 28fea4c930
Merge pull request #2085 from jordansissel/feature/add-rpm-old-perl-flag
Add flag to allow using older 'perl' rpm dependency name for systems which do not have 'perl-interpreter' dependency available. (#2066)
2024-12-08 22:53:09 -08:00
Jordan Sissel b2f2fc6624
Merge branch 'main' into pull/2009 2024-12-08 22:45:49 -08:00
Jordan Sissel e7e7449815
Merge pull request #2053 from JordanStopford/main
Fix for #1627
2024-12-08 22:42:37 -08:00
Jordan Sissel 862a6863cd Add flag to allow using older 'perl' rpm dependency name for systems which do not have 'perl-interpreter' dependency available. (#2066) 2024-12-08 22:21:30 -08:00
Jordan Sissel 858d1c1419
Merge pull request #2066 from kduret/fix-rpm-perl-dependency-name
fix(rpm): replace perl dependency by perl-interpreter
2024-12-08 22:20:34 -08:00
Jordan Sissel 98cc060ede Merge branch 'chouquette/add_compression_level' (#2036) 2024-12-07 23:28:55 -08:00
Jordan Sissel 512f4a9eb3 Fix syntax I broke when doing a merge conflict 2024-12-07 23:28:42 -08:00
Jordan Sissel 8acb2f51d7
Merge branch 'main' into chouquette/add_compression_level 2024-12-07 23:24:39 -08:00
Jordan Sissel 468f455862
Merge pull request #2041 from UiP9AV6Y/feature_rpm_changelog
rpm: generate changelog if none is provided
2024-12-07 23:17:00 -08:00
Jordan Sissel e6b83c4788
Merge pull request #2062 from willaerk/main
Escape the pylib path when using it in a shell command
2024-12-07 23:12:58 -08:00
Jordan Sissel 13a3c6992e
Merge pull request #2063 from skoef/systemd-path-flag
Add option --deb-systemd-path
2024-12-07 23:11:12 -08:00
Jordan Sissel 572cf390a7
Merge branch 'main' into systemd-path-flag 2024-12-07 23:09:53 -08:00
Jordan Sissel d30e641bb8 Update changelog 2024-12-07 23:00:04 -08:00
Jordan Sissel ad4402f3bf
Merge pull request #2068 from mattaezell/pip_zip
Support pip download returning a zip file
2024-12-07 22:56:43 -08:00
Jordan Sissel c326b39d95 When using zstd, use tar flags that are compatible with both gnu tar and bsdtar (#2009) 2024-12-07 22:43:00 -08:00
Jordan Sissel e66a359a10
Merge pull request #2009 from SaltwaterC/main
Add support for zstd compression for deb packages.
2024-12-07 22:42:46 -08:00
Jordan Sissel c028e71206
Merge pull request #2082 from wheaney/patch-1
Update rpm.rb
2024-12-02 10:17:07 -08:00
Wayne Heaney 0fb0d26152
Update rpm.rb
Leave old `--define` argument in place for `buildroot`
2024-11-04 22:02:31 -08:00
Wayne Heaney b852c59b02
Update rpm.rb
Fix how buildroot is specified to better conform with the rpmbuild CLI. The old way was failing with `File not found` errors.
2024-10-29 13:45:47 -07:00
Matt Ezell 790f53c766 Support pip download returning a zip file
Signed-off-by: Matt Ezell <ezellma@ornl.gov>
2024-09-23 10:33:57 -04:00
Jordan Sissel d81a1ad1ac
Merge pull request #2064 from TwitchCaptain/main
Create FreeBSD packages with correct architecture
2024-09-12 10:56:52 -07:00
Jordan Sissel fd06d93533
Merge pull request #2067 from jordansissel/improvement/errors-not-exceptions
Configuration problems should print errors, not stack traces.
2024-09-12 10:52:50 -07:00
Jordan Sissel e94f56c01e Flag errors should use FPM::Package::InvalidArgument 2024-09-12 10:48:55 -07:00
Jordan Sissel 843c729b16 Configuration errors should raise a specific exception
Raising FPM::InvalidPackageConfiguration will allow the fpm command-line
to print an error message and exit non-zero.

Raising other exceptions will often produce a crash-like behavior which
prints the exception and a stack trace. Stack traces are not helpful
content in situations where a user has made a configuration error.
2024-09-12 10:39:55 -07:00
Jordan Sissel 18e04a70e1
Merge pull request #2065 from phillipp/main
Add support for .timer units to --deb-systemd
2024-09-12 10:20:56 -07:00
Kevin Duret e7903804af fix(rpm): replace perl dependency by perl-interpreter 2024-09-06 09:22:28 +02:00
Phillipp Röll 6957220642 Add support for .timer units to --deb-systemd
Before the change, when using an option --deb-systemd myunit.timer,
the actual file created would be
/lib/systemd/system/myunit.timer.service

Because of that, systemd would then not find the timer.

With this change, the correct extension is extracted from the file,
defaulting to .service.

Typically, there would be two files for installing a timer:

--deb-systemd myunit.service (where .service could be omitted) and
--deb-systemd myunit.timer

So that the .timer references a .service file.
2024-08-12 14:47:47 +02:00
David Newhall II bf0aa55655 build proper freebsd package 2024-08-08 06:07:24 -07:00
Reinier Schoof 5092c5f659 Add option --deb-systemd-path
This option will allow you to override the path within the staging directory
systemd service files are moved to in debian packages.

On some systems systemd should not go in the hardcoded /lib/systemd/system but
in /usr/lib/systemd/system instead. Many systems have the both symlinked, but
not all so assuming /lib/systemd/system can be dangerous.

Signed-off-by: Reinier Schoof <reinier@skoef.nl>
2024-06-26 15:54:02 +02:00
Kristof Willaert 5960c8c4a7 Escape the pylib path when using it in a shell command 2024-05-16 18:13:54 +02:00
Jordan Stopford eb56972540 Fix for #1627
Strip rpmlib dependencies
2024-04-23 17:30:48 +01:00
Jordan Stopford 20aa78c72c Fix for #1627
When packaging a deb sometimes a path to a binary comes into the requires field i.e. /bin/sh but this isn't valid for control files. Strip this out
2024-04-23 16:33:39 +01:00
Jordan Stopford ae8205261a Fix for #1627
When packaging a deb sometimes a path to a binary comes into the requires field i.e. /bin/sh but this isn't valid for control files. Strip this out
2024-04-23 16:01:42 +01:00
Jordan Stopford 0cba81fbe4
Merge branch 'jordansissel:main' into main 2024-04-17 16:31:26 +01:00
Jordan Sissel 9d3d96a93d
Merge pull request #2057 from brad/patch-1
fix the link to ruby installation instructions
2024-04-11 08:54:05 -07:00
Brad Pitcher 86be3cc802
fix the link to ruby installation instructions 2024-04-11 08:06:02 -06:00
Jordan Sissel 13228bf566
Merge pull request #2023 from cwegener/main
chore: relax 'clamp' version pin
2024-04-08 21:28:38 -07:00
Christoph Wegener d0884e8903 chore: relax 'clamp' version pin
Ubuntu 22.04 LTS comes with ruby-clamp 1.1.1 which seems to work just
fine with fpm 1.15.1

Relaxing the version pin seems to be the right thing to do.
2024-04-09 10:31:22 +10:00
Jordan Stopford 3b2b258020 Fix for #1627
When packaging a deb sometimes the architecture comes through as part of the provides (not sure why) but this causes problems with the deb package as it seems to only ever expect one set of round brackets which is the version indicator
2024-03-08 10:37:34 +00:00
Natanael Arndt 847ded9eba Update dir.rst
Fix formatting for `--prefix`
2024-01-02 07:08:01 -08:00
Gordon Bleux c9d749a3a7 rpm: generate changelog if none is provided
`rpmlint` considers a lack of a changelog an error.

this is a similar behaviour to the DEB packager, where a generic
default changelog is generated unless one is provided explicitly.
2023-12-14 21:21:33 +01:00
Hugo Beauzée-Luyssen 49093c3533
deb: add a --compression-level option
Fix #2006
2023-11-24 09:17:18 +01:00
SaltwaterC e0edc7e559 Add support for zstd compression for deb packages. 2023-04-28 20:25:00 +01:00
Jordan Sissel b085edc49c Check if an option has a default value before we try to look it up.
This fixes fpm when used with clamp 1.3.0 or above.

Fixes #1543
2023-03-25 23:17:34 -07:00
Jordan Sissel ad6b18a0aa Fix failing test when run on arm64/aarch64 systems - this was a bug in the test suite 2023-02-07 00:27:29 -08:00
Jordan Sissel 066b9b561a Use a temporary path when testing archive commands.
Previously, fpm would write to the current directory when using methods to check for the correct `ar` and `tar` commands.

This now uses Stud::Temporary to get a random path in the system temporary directory.

I found this error in the test suite when the fpm git workspace was not
writable -- for example, when mounted read-only through a container.

Error message, for posterity:

    1) FPM::Command -p | --package when given a directory should write the package to the given directory.
       Failure/Error: cmd.run(["-s", "empty", "-t", "deb", "-n", "example", "-p", path])
       Errno::EACCES:
         Permission denied @ rb_sysopen - fpm-dummy.tmp
2023-02-07 00:13:16 -08:00
Jordan Sissel 18233c334d Version bump 1.15.1 2023-01-31 19:49:10 -08:00
Nicholas Hubbard d0c139aea4 Replace deprecated {File,Dir}.exists? with {File,Dir}.exist? 2023-01-31 18:08:38 -08:00
Jordan Sissel 31506109ff Add test coverage for debian version field validation
For #1847
2022-12-03 18:26:30 -08:00
Jordan Sissel f898ef8232 Reject invalid Debian version values.
A hopefully-actionable error message is provided when an invalid version
is given when making a Debian package.

To aid readability, rewrote the relationship pattern as a multiline regex. Added separate pattern for version field.

Test coverage added for #1969's "v" prefix removal.

For #1847
2022-12-03 18:26:30 -08:00
Gábor Lipták c9a5cb4628 Update GHA build
Signed-off-by: Gábor Lipták <gliptak@gmail.com>
2022-12-03 18:25:54 -08:00
Gábor Lipták ada6012b5b Drop leading v from version for Debian
Signed-off-by: Gábor Lipták <gliptak@gmail.com>
2022-12-03 16:46:29 -08:00
Jordan Sissel db06af3c03 Only try splitting the license text if there's actually a license given. This avoids a crash. 2022-11-28 17:48:15 -08:00
Jordan Sissel dcbe0425db Close the file to make Python flush any pending writes. Without this, some versions of python will /not/ write anything to our file which makes it empty and causes a failure in fpm. 2022-11-28 17:48:15 -08:00
Jordan Sissel df26ed08f7 Fix typo in changelog 2022-11-13 23:48:41 -08:00
Jordan Sissel 906cf8054a Release 1.15.0 2022-11-13 23:25:55 -08:00
Jordan Sissel fdd5e559ac Version bump 2022-11-13 23:25:07 -08:00
Jordan Sissel 10c8471179 Change docs tooling to use podman instead of docker 2022-11-13 23:24:21 -08:00
Jordan Sissel 42f2660a58 Regenerate docs with new flag sorting method. 2022-11-13 23:07:42 -08:00
Jordan Sissel d8bc03a63c Try to more-reliably sort options by name
New feature here is that --foo and --[no-]foo are both sorted as "foo",
so flags should be reasonably grouped together now.
2022-11-13 23:07:42 -08:00
Jordan Sissel 15aaf1cfce Add LOAD_PATH flag for cli generator 2022-11-13 23:07:42 -08:00
Jordan Sissel 601cfda057 Update changelog 2022-11-13 23:07:42 -08:00
C. Cooke 88c1e6ab0a Fix Debain package name relation regex
- Package names must start with an alphanumeric
- Package names must be at least two characters
- Package names may include a '.'
2022-11-13 21:14:33 -08:00
Gnought 72ccedc45d chore: refactor Dockerfile
- change base image to ubuntu:20.04
- support ruby 2.7
- docker lint
2022-11-13 21:11:51 -08:00
Jordan Sissel 5b104bccf0 Error if --workdir flag points somewhere that doesn't exist or is not a directory.
Fixes #1938
2022-11-13 20:23:59 -08:00
Nicholas Hubbard 3ba61bc0bd Test that the logger pipe method is invoked during safesystem 2022-11-13 17:54:10 -08:00
Nicholas Hubbard 8a6460d0d3 Test to ensure safesystem does not prompt user for input 2022-11-13 17:54:10 -08:00
Nicholas Hubbard b0ca926d49 execmd: close stdin for writing if called non-interactively 2022-11-13 17:54:10 -08:00
Jordan Sissel 3653f1a10c Use pkgbuild's --install-location flag when fpm's --prefix is given.
A user reported that on macOS Catalina, the default pkgbuild install
location may result in a .pkg file which cannot be installed.

This change makes fpm's `--prefix` option pass through to `pkgbuild`'s
`--install-location` flag

> With macOS Catalina, you can no longer store files or data in the read-only system volume, nor can you write to the "root" directory ( / ) from the command line, such as with Terminal.

References:
* macOS Catalina's new read-only root filesystem: https://support.apple.com/en-us/HT210650

Fixes #1908
2022-11-09 22:43:35 -08:00
Jordan Sissel 63d4ac8dfc Move `require` closer to where it is used.
This helps avoid a startup crash on Ruby 1.9.3 where the `rexml` gem
crashes when loaded due to syntax errors.

Fixes #1798, #1800, #1784
2022-11-02 21:49:17 -07:00
Jordan Sissel ad2b1a8966 Call String#dup on RUBY_VERSION to avoid crashing on older ruby versions
On older versions of rubygems, `Gem::Version.new(...)` calls
`String#strip!` on the argument in the constructor. This causes a
problem on Ruby 1.9.3 where the RUBY_VERSION constant is a frozen
string.

The workaround is to make a copy of this string that is unfrozen, and
`String#dup` seems to work :)
2022-11-02 21:49:17 -07:00
Jordan Sissel d9ba0b4545 Remove 'json' gem dependency.
The original `json` gem dependency was added in the original fpm.gemspec
because, at the time, Ruby 1.8.7 was common and required an external
`json` dependency for parsing JSON.

Later, Ruby releases since 1.9.1 have bundled `json`[1].

Therefore, it feels safe to remove this dependency. As a bonus, the
rubygems `json` gem places requirements on the minimum version of Ruby.
At this time, the latest `json` gem requires Ruby >= 2.3.

If the `json` gem dependency is removed, fpm will still retain the
ability to process JSON while lowering the minimum required Ruby version
to Ruby 1.9.x -- It's not perfect, but it's a start! :)

[1] https://docs.ruby-lang.org/en/2.3.0/NEWS-1_9_1.html

The idea for this change change came originally from a discussion
with @edolnx in #1949

Fixes #1741, #1264, #1949
2022-11-02 21:49:17 -07:00
Nicholas Hubbard 56a97c43be Fixed typo: stdin_w_close -> stdin_w.close 2022-10-31 15:17:48 -07:00
Jordan Sissel cd7a6855d3 Try to improve the flag description 2022-10-28 23:11:25 -07:00
Jordan Sissel c6b85be6a2 Improve tests and raise exception when the flags file is too large. 2022-10-28 23:11:25 -07:00
Jordan Sissel ce8fd6cb64 Remove debug print 2022-10-28 23:11:25 -07:00
Jordan Sissel 16c7e280a7 Support single-character flags in option files. 2022-10-28 23:11:25 -07:00
Jordan Sissel 7d6b5cd58c Add test coverage for --fpm-options-file
Cases:
* Option ordering (the flag operates in-place)
* Multiple --long-flags on a single line
* Multiple single flags on a single line (both like '-ff' and '-f -f')
* File self-reference errors
2022-10-28 23:11:25 -07:00
Jordan Sissel ec82bc649b Add option --fpm-options-file
This option flag will cause fpm to load additional flags, in place, from
the given file.

For example, if a file "foo" contains one line, "--version 5.10", then
`fpm -s empty -t deb -n example --fpm-options-file foo` act as if
`--version 5.10` was given on the command-line in the same position as
the `--fpm-options-file` flag.

Error conditions checked:
* Has the file already been loaded?
* Does the file exist?
* Is the file readable?

No tests included at this time. This code is likely missing some edge
cases (combined single-letter flags, multiple flag entries on a line,
etc).
2022-10-28 23:11:25 -07:00
Nicholas Hubbard 110419b695 Test unpack method directly 2022-10-28 16:06:34 -07:00
Nicholas Hubbard 11175cc47f Add test for building Alien::astyle version 0.010000 2022-10-28 16:06:34 -07:00
Nicholas Hubbard c8d364b0ab Fix broken tar command 2022-10-28 16:06:34 -07:00
Jordan Sissel 3fcaefb9fd Remove `git` gem dependency.
Folks are reporting that fpm cannot be installed easily (or at all) on
older systems because a transitive dependency(1) rejects ruby versions
older than 2.6.

(1) rubygem git depends on addressable which depends on public_suffix

Since the `git` dependency is only used in the `gem` source when
using a git repo as a installation source, and that usage seems pretty
simple -- clone a repo, checkout a branch, etc -- it feels safe to
remove this dependency while still keeping the same functionality.

Fixes #1923
2022-10-26 14:48:12 -07:00
Jordan Sissel d16b531a2f Trying GitHub Actions for testing
Attempting to figure out why rspec passes but the action still fails.
2022-10-24 20:38:08 -07:00
Jordan Sissel 6c87589f32
Update ruby.yml 2022-10-24 19:07:08 -07:00
Jordan Sissel 72b2f26ad3
Create ruby.yml 2022-10-24 19:04:51 -07:00
Nicholas Hubbard bfb192c417 Add test for CPAN dist with missing author 2022-10-21 17:23:36 -07:00
Jordan Sissel f168571abf Support newer version of lintian in the test suite.
Fixes #1907
2022-10-16 17:44:17 -07:00
Nicholas Hubbard 409a3b27df Prevent crash if CPAN distribution has blank author field 2022-10-16 16:23:07 -07:00
loic.chabert 788387c0d2 Add space on changes.erb to conform with debian specs 2022-10-03 13:34:22 -07:00
loic.chabert 37f56269c3 Replace whatever in changelog.erb and deb.changes.erb
In order to support packaging upload, the distribution must be
set/aligned in changelog and changes files.
Required for example inside dput-ng repo upload app.
2022-10-03 13:34:22 -07:00
Evgeny Stambulchik c40f6818f8 Recognize "--pleaserun-user" option 2022-07-05 16:26:24 -07:00
Chris Novakovic 9b32d9a116 Change default Debian package priority to optional
Since Debian Policy version 4.0.1, the "extra" priority has been
deprecated and replaced with "optional"; this triggers Lintian warnings
for Debian packages built by FPM. Make this the new default for Debian
package outputs when no value of --deb-priority is given.

Fixes #1398.
2022-06-24 13:49:32 -07:00
Jordan Sissel 40795d4d85 Load gem specifications using YAML's safe loading
On older rubies, YAML.load _is_ the unsafe load method. At some point,
Ruby 3.1.0 / Psych 4(?) made two renames:

* YAML.load -> YAML.unsafe_load
* YAML.safe_load -> YAML.load

A quick test is to try converting a gem. This would fail if `YAML.load`
was the "safe" method because it would fail with this message:

    Tried to load unspecified class: Gem::Specification (Psych::DisallowedClass

`fpm -s gem -t empty rails` will crash on Ruby 3.1.0 prior to this
commit.

Fixes #1895

Add necessary classes to safely load yaml from gem specs
2022-05-19 20:19:44 -07:00
Jordan Sissel 7881705985 Call ERB.new correctly depending on the RUBY_VERSION
* On Ruby 3.0.x and older, call ERB.new(template_code, nil, "-")
* On Ruby 3.1.0 and newer, call ERB.new(template_code, trim_mode: "-")

Fixes #1894
2022-05-01 22:39:04 -07:00
Jordan Sissel 5d2b4854d3 Fix failing test.
Looks like at some point django changed the admin tool from
"django-admin" to "django-admin.py"

Possibly this changed in upstream in Django on this commit, though I
don't know for certain:
85efc14a2e
2022-05-01 21:53:54 -07:00
Jordan Sissel 7f4718f9c2 Run `pip` without the `--build` flag
Previously, fpm would use `pip download ... --build ...` to instruct pip
to unpack a given python package to a specific directory for the purpose
of running something like `python setup.py` from it.

However, somewhere in 2021, pip removed this flag. First, I think, it
was deprecated and ignored, then finally removed. One reference to
this removal in the upstream pip project is this issue:
https://github.com/pypa/pip/issues/8333

Without `--build`, pip will place a single tarball in the destination
directory. Fpm cannot easily predict the name of this file because we
don't know the "real" name of the python package nor do we know the
version number being downloaded.

For example:

```
% python3 -m pip download --no-binary :all: --no-deps --no-clean django
...
Successfully downloaded django
% ls
Django-4.0.4.tar.gz
```

Best guess:
* we can expect exactly one file in the previously-empty target directory
* we can also expect that it is a .tar.gz

I don't know if these guesses are always correct, but it's a start.

As of this commit, the following command generates a Debian package:

`fpm -s python --python-bin python3 -t deb django`

Prior to this commit, with a newer version of pip, the command would
fail.

Fixes #1831
2022-05-01 21:53:54 -07:00
Jordan Sissel 82284c7a4b Rerun `make release-prep` 2022-03-31 00:15:23 -07:00
Jordan Sissel 60b4bd30b3 Add additional changelog entries 2022-03-31 00:09:25 -07:00
Jordan Sissel 377fcaf871 Add note about new documentation pages 2022-03-31 00:09:25 -07:00
Jordan Sissel 2943d82581 Version bump and update changelog 2022-03-31 00:09:25 -07:00
mszprejda f5d07520b7 pacman.rb: Fix typo in compression type matching 2022-03-30 23:52:51 -07:00
Jordan Sissel 0dfe21bab7 Add test case for files with spaces and quotatation marks.
Testing, this new test case fails on v1.14.1 and *succeeds* as expected
after #1882 was merged.

Fixes #1886
2022-03-30 23:50:55 -07:00
John Bollinger b2ba5c4a59 Fix filename mangling for RPM targets
Fixes the mangling FPM performs on the contents of RPM %files lists
to better match RPM's idiosyncratic filename handling.  FPM now
recognizes more cases that require special handling, and it
correctly distinguishes between the glob and non-glob cases,
which RPM itself treates differently.

Fixes #1385
2022-03-30 23:47:02 -07:00
Jordan Sissel 521d030420 Fix reStructuredText header length syntax 2022-03-30 23:15:59 -07:00
Jordan Sissel 106828fc12 Add description in header 2022-03-30 23:15:59 -07:00
Jordan Sissel 0c84173de0 Add description in header 2022-03-30 23:15:59 -07:00
Jordan Sissel 9b9a1b365f Add very basic package docs for each type of package.
Thanks to @alanc and _msw_ for quickly helping me figure out good words
to write to describe Solaris' p5p format.

For #1884.
2022-03-30 23:15:59 -07:00
John Howard eb5370d16e Fix `--deb-compression none`
Previously, we would pass the literal `""` as an argument to `tar`.
`tar` would interpret this as a file name, which does not exist, and
fail.

This fixes the command to just pass no compression flag at all to tar
when `--deb-compression none` is set.

I did not add tests since I couldn't figure out how to execute them -
this is my first time working in ruby.
2022-03-21 15:20:08 -07:00
Jordan Sissel 92886d922d Restore development_dependency on `rake`
A prior commit removed `rake` as a development dependency on the
assumption that nothing in fpm's development process actually required
the use of `rake`.

However, I had forgotten about #756 (year 2014) which adds FPM::RakeTask
and some test coverage. FPM::RakeTask was added to allow folks to more
easily invoke FPM from within a Rake task.

At this time, I see no reason to remove FPM::RakeTask. Further, because
`rake` is typically (in my experience) used only in development
environments. Therefore, I believe the right solution is to restore
`rake` as a development_dependency in order to allow the test suite to
pass. For users of FPM::RakeTask, I would assume (hopefully correctly!)
that they already have `rake` installed as a dependency in their own
project, so the `fpm` gem has no need to specify `rake` as a general-use
dependency.

When studying newer versions of Rake, I found:
* Rake v13 requires Ruby 2.2 or newer.
* Rake v12.xx and older are flagged as having security vulnerabilities.

In order to minimize chaos, this commit adds an unversioned dependency
on rake. This is to help fpm service more versions of Ruby and resist
efforts by any dependency to dictate which version of Ruby is used.

This reverts db9db670c3.

Fixes #1877
2022-02-23 18:04:51 -08:00
Corey Quinn 9aa1c1cb64 Update README to modernize the given example. 2022-02-07 15:26:36 -08:00
Geoff Beier 7a1302d1e8 fix typo in "perl" 2022-01-26 12:22:04 -08:00
97 changed files with 2379 additions and 1607 deletions

View File

@ -1,8 +0,0 @@
workflow "New workflow" {
on = "push"
resolves = ["GitHub Action for Docker"]
}
action "GitHub Action for Docker" {
uses = "actions/docker/cli@76ff57a"
}

30
.github/workflows/ruby.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Ruby
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-22.04
strategy:
matrix:
ruby-version: ['2.7', '3.0', '3.1', '3.4']
steps:
- run: |
sudo apt-get update
sudo apt install -y libarchive-tools lintian cpanminus
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- run: |
if [ ! -z "$RUNNER_DEBUG" ] ; then
DEBUG=1 bundle exec rspec -fd
else
bundle exec rspec
fi
env:
SHELL: /usr/bin/bash

View File

@ -1,6 +1,14 @@
# https://blog.readthedocs.com/build-errors-docutils-0-18/
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.13"
sphinx:
configuration: docs/conf.py
python:
install:
- requirements: docs/requirements.txt

View File

@ -1,6 +1,71 @@
Release Notes and Change Log
============================
1.17.0 (October 2, 2025)
^^^^^^^^^^^^^^^^^^^^^^^^
* python: Support modern Python project features: pyproject.toml, wheels, etc. Now, any project that can be built or installed with ``pip`` can be packaged by fpm. Previously, fpm relied on a long-deprecated features in setup.py to see a python project's metadata such as name, version, and dependencies. Fpm now uses python's package tools to identify the project's name, version, dependencies, and other information. (`#2104`_, `#2105`_, `#2040`_, `#1982`_, `#2029`_; Jordan Sissel, cwegener, amdei, gmabey)
* dir: When copying files, only use hardlinks if the original files were also hardlinks. (`#2103`_, `#2102`_; Michael Telatynski, Matthew Rathbone, Jordan Sissel). :w
Related: https://github.com/electron-userland/electron-builder/issues/5721
* deb: bug fix: when a file given with ``--config-files <path>`` copied into the package, fpm was forgetting to mark the file as being a config file in the package, aka Debian "conffiles" (`#2027`_, `#1823`_; Alexandr Zarubkin, Kientz Arnaud)
* pacman: Now can build packages with aarch64 and arm7hf architecture (`#2017`_; Markson Hon)
* rpm: Paths with '{' and '}' characters can now be included in rpms (`#2088`_ `#2087`_; Jordan Sissel, Manish2481983)
* docs: Updated urls which pointed at rpm documentation (`#2092`_, `#2011`_, `#2054`_; André Kelpe, Natanael Arndt)
* Ruby 3.4.0 no longer gives warnings related to `ostruct` (`#2106`_, also `#2104`_ and `#2103`_; Jordan Sissel)
1.16.0 (December 8, 2024)
^^^^^^^^^^^^^^^^^^^^^^^^^
* deb: Add support for zstd compression (`#2009`_, `#2084`_; Ștefan Rusu)
* deb: Add compression level setting, ``--deb-compression-level`` (`#2036`_; Hugo Beauzée-Luyssen)
* rpm: Generate an empty rpm changelog if none is given. This should help cases where ``rpmlint`` would complain about missing a changelog entry in the rpm. (`#2041`_; Gordon Bleux)
* deb: When converting from an rpm, remove package information that is not valid on Debian systems. (`#2053`_, `#1627`; Jordan Stopford, Jamesits)
* python: Fix bug when PYTHONPATH has spaces in it (`#2062`_; Kristof Willaert)
* deb: You can now choose a different systemd directory with ``--deb-systemd-path`` (`#2063`_; Reinier Schoof)
* freebsd: OS version can now be specified with ``--freebsd-osversion``: (`#2064`_; David Newhall II)
* freebsd: Improve the 'architecture' value used by fpm to generate freebsd packages (`#2064`_, `#1880`_; David Newhall II, Matthew R Kasun)
* deb: Timer units can be given to ``--deb-systemd`` now (`#2065`_, `#1978`_; phillipp, Robert Schneider)
* rpm: When converting cpan packages, use newer ``perl-interpreter`` dependency name. To use the old dependency name ``perl``, use the flag ``--rpm-old-perl-dependency-name`` (`#2066`_, `#2085`_; Kevin Duret, Nicholas Hubbard, William N. Braswell, Jr., Jordan Sissel))
* Some errors now correctly print just an error message instead of dumping a ruby stack trace (`#2067`_; Jordan Sissel)
* python: Support modules which download as zip files (`#2068`_, `#2074`_, `#2072`_; Matt Ezell, hussainbani, hbani)
* rpm: Fix rpm build failures on Fedora 41 (`#2082`_, `#2076`_; Wayne Heaney, Antheas Kapenekakis, Romain Geissler)
1.15.1 (January 31, 2023)
^^^^^^^^^^^^^^^^^^^^^^^^^
* Ruby 3.2.0 now supported. This fixes error 'undefined method exists? for File' '(`#1981`_, `#1988`_; Nicholas Hubbard, romulasry)
1.15.0 (November 13, 2022)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* New flag ``--fpm-options-file path/to/file`` which allows you to specify additional fpm flags in an external file of your choosing. (`#1905`_, `#1902`_, `#1827`_; Jordan Sissel, Will Furnell, hjpotter92)
* deb: Periods are now accepted in package names (`#1899`_; C. Cooke)
* Fix bug where fpm would crash if ``--workdir`` pointed at a path that didn't
exist. (`#1959`_; Jordan Sissel)
* osxpkg: this package format now supports the fpm ``--prefix`` flag(`#1909`_, `#1908`_; Jordan Sissel, mcataga)
* cpan: Fix bug where fpm would fail on certain Perl modules due to their source tarball structure (`#1940`_; Nicholas Hubbard, William N. Braswell, Jr.)
* cpan: Fix crash on certain CPAN modules where the author field was blank (`#1942`_, `#1937`_, `#1523`_, `#1528`_; Nicholas Hubbard, William N. Braswell, Jr.)
* deb: The distribution field of the debian changelog and changes files will now use the value set by ``--deb-dist`` (default is "unstable") (`#1934`_; Chabert Loïc)
* python: Fix errors in how fpm invoked ``pip``. Previously, fpm would use pip's ``--build`` flag, but this was removed a while ago, and fpm is now aware! (`#1896`_, `#1831`_, `#1893`_, `#1916`_; Jordan Sissel, Svyatogor Chuykov)
* pleaserun: Add ``--pleaserun-user`` flag. (`#1912`_; Evgeny Stambulchik)
* deb: The default ``--deb-priority`` is now "optional" instead of "extra" (`#1913`_; Chris Novakovic)
* Enable installation of fpm on older versions of ruby. This change removed ``git`` rubygem dependency. (`#1946`_, `#1923`_; Jordan Sissel, Andreas Wirooks, Ruslan Kuprieiev, jamshid, Lorenzo Castellino, Sam Hughes)
* Enable operation of fpm on very old versions of ruby (as old as Ruby 1.9.3). This changed removed ``json`` rubygem dependency. (`#1950`_, `#1949`_, `#1741`_, `#1264`_, `#1798`_, `#1800`_, `#1784`_; Jordan Sissel and many others)
* Fix bug where subprocesses could hang waiting for input (`#1955`_, `#1519`_, `#1522`_; Nicholas Hubbard, William N. Braswell, Jr.)
* Update Dockerfile to use ubuntu:20.04 (`#1935`_; Gnought)
* internal: Fix a code typo (`#1948`_; Nicholas Hubbard)
* internal tests: Support newer versions of lintian (`#1939`_, `#1907`_; Jordan Sissel)
* Improve support for Ruby 3.1.0 and newer that would previously crash with an error mentioning Psych::DisallowedClass (`#1898`_, `#1895`_; Jordan Sissel, Alexandre ZANNI)
* Improve support for Ruby 3.1.0 and newer that changed the API for ERB (`#1897`_; Jordan Sissel)
1.14.2 (March 30, 2022)
^^^^^^^^^^^^^^^^^^^^^^^
* deb: fix bug causing ``--deb-compression none`` to invoke ``tar`` incorrectly (`#1879`_; John Howard)
* rpm: Better support for paths that have spaces and mixed quotation marks in them. (`#1882`_, `#1886`_, `#1385`_; John Bollinger and Jordan Sissel)
* pacman: Fix typo preventing the use of ``--pacman-compression xz`` (`#1876`_; mszprejda)
* docs: All supported package types now have dedicated documentation pages. Some pages are small stubs and would benefit from future improvement. (`#1884`_; mcandre, Jordan Sissel)
* docs: Small but lovely documentation fixes (`#1875`_ by Corey Quinn, `#1864`_ by Geoff Beier)
* Fixed mistake causing the test suite to fail when ``rake`` wasn't available. (`#1877`_; Jordan Sissel)
1.14.1 (November 10, 2021)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fix a bug that impacted fpm api usage (from other ruby programs) that caused an error "NameError: uninitialized constant FPM::Package::CPAN" when trying to output a Deb package. (`#1854`_, `#1856`_; Karol Bucek, Jordan Sissel)

View File

@ -1,3 +1,5 @@
# syntax=docker/dockerfile:1
# Are we running against the minimal container, or the everything
# container? Minimal is mostly the compiled package tools. Everything
# pulls in scripting langauges.
@ -8,87 +10,98 @@ ARG BASE_ENV=everything
ARG TARGET=test
# Container to throw an error if called with a bare `docker build .`
FROM ubuntu:18.04 as error
RUN echo "\n\n\nHey! Use buildkit. See the Makefile or docs\n\n\n"
RUN false
FROM ubuntu:20.04 as error
RUN <<EOF
printf '\n\n\n%s\n\n\n' "Hey! Use buildkit. See the Makefile or docs"
false
EOF
# Base container is used for various release and test things
FROM ubuntu:18.04 as minimal-base
FROM ubuntu:20.04 as minimal-base
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Etc/UTC
# Runtime deps. Build deps go in the build or test containers
RUN apt-get update \
&& apt-get -y dist-upgrade \
&& apt-get install --no-install-recommends -y \
ruby rubygems rubygems-integration \
bsdtar \
cpio \
debsigs \
pacman \
rpm \
squashfs-tools \
xz-utils \
zip \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
RUN adduser fpm
# hadolint ignore=DL3009
RUN <<EOF
apt-get update
apt-get install --no-install-recommends --no-install-suggests -y \
'ruby=*' \
'ruby-dev=*' \
'libarchive-tools=*' \
'cpio=*' \
'debsigs=*' \
'pacman=*' \
'rpm=*' \
'squashfs-tools=*' \
'xz-utils=*' \
'zip=*' \
'gcc=*' \
'libc6-dev=*' \
'make=*' \
'lintian=*' \
'git=*'
useradd -ms /bin/bash fpm
EOF
# everything container includes all the scripting languages. These
# greatly embiggen the underlying docker container, so they're
# conditionalized.
FROM minimal-base AS everything-base
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
cpanminus \
npm \
perl \
python3-pip \
&& pip3 --no-cache-dir install setuptools \
&& pip3 --no-cache-dir install wheel \
&& pip3 --no-cache-dir install virtualenv virtualenv-tools3 \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
&& rm -rf /var/lib/apt/lists/*
FROM minimal-base as everything-base
RUN <<EOF
apt-get install --no-install-recommends --no-install-suggests -y \
'cpanminus=*' \
'npm=*' \
'perl=*' \
'python3-pip=*'
pip3 --no-cache-dir install 'setuptools>=45' 'wheel>=0.34' 'virtualenv>=20' 'virtualenv-tools3>=2'
update-alternatives --install /usr/bin/python python /usr/bin/python3 10
EOF
# hadolint ignore=DL3006
FROM ${BASE_ENV}-base as base
RUN <<EOF
rm -rf /var/lib/apt/lists/*
apt-get clean
EOF
# Run tests against the current working directory. This is a bit
# orthogonal to the container release process, but it has a lot of
# same dependancies, so we reuse it. This uses COPY to allow rspect to
# initall the gems, but runtime usage expects you to mount a volume
# into /src
FROM ${BASE_ENV}-base AS test
WORKDIR /src
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
gcc make ruby-dev libc-dev lintian git
FROM base AS test
# installing ffi here is a bit of an optimization for how COPY and layer reuse works
RUN gem install --no-ri --no-rdoc ffi
RUN install -d -o fpm /origsrc
COPY --chown=fpm . /origsrc
RUN gem install --no-document ffi:*
USER fpm
WORKDIR /origsrc
ENV HOME=/origsrc
ENV BUNDLE_PATH=/origsrc/.bundle
# Install a specific version of bundler
WORKDIR /origsrc
RUN gem install -v "$(grep -A1 '^BUNDLED WITH' Gemfile.lock | tail -1)" bundler
USER fpm
RUN bundle install
CMD bundle exec rspec
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN <<EOF
# Install a specific version of bundler
install -d -o fpm /origsrc
gem install -v "$(grep -A1 '^BUNDLED WITH' Gemfile.lock | tail -1)" bundler:*
bundle install
EOF
CMD ["bundle", "exec", "rspec"]
# build a container from a released gem. install build deps here, so
# we can omit them from the final release package
FROM ${BASE_ENV}-base AS build
RUN apt-get update
RUN apt-get install --no-install-recommends -y \
gcc make ruby-dev libc-dev
ENV GEM_PATH /fpm
ENV PATH "/fpm/bin:${PATH}"
RUN gem install --no-ri --no-rdoc --install-dir=/fpm fpm
FROM base AS build
ENV GEM_PATH=/fpm
ENV PATH="/fpm/bin:${PATH}"
# hadolint ignore=DL3028
RUN gem install --no-document --install-dir=/fpm fpm
FROM build as release
FROM base as release
COPY --from=build /fpm /fpm
ENV GEM_PATH /fpm
ENV PATH "/fpm/bin:${PATH}"
ENV GEM_PATH=/fpm
ENV PATH="/fpm/bin:${PATH}"
USER fpm
WORKDIR /src
ENTRYPOINT ["/fpm/bin/fpm"]
# This target is to help docker buildkit in resolving things.
FROM ${TARGET} as final
# hadolint ignore=DL3006
FROM ${TARGET}

View File

@ -53,9 +53,9 @@ clean:
publish-docs:
$(MAKE) -C docs publish
release-prep:
release-prep: package
rm -f docs/changelog_links.rst docs/cli-reference.rst
make -C docs changelog_links.rst cli-reference.rst package-type-cli
make -C docs build package-type-cli
# Testing in docker.
# The dot file is a sentinal file that will built a docker image, and tag it.

View File

@ -1,53 +0,0 @@
# Debian notes
## C libraries
Linux seems to require 'ldconfig' runs after shared libraries are installed. I
haven't bothered digging into why, but many debian C library packages run
ldconfig as a postinstall step.
I'd like to avoid postinstall actions, so this needs research to see if this is
possible.
## Ruby
rubygems on Debian/Ubuntu is not very recent in most cases, and some gems have
a requirement of rubygems >= a version you have available.
Further, debian blocks 'gem update --system' which you can get around by doing:
% gem install rubygems-update
% ruby /var/lib/gems/1.8/gems/rubygems-update-1.3.1/bin/update_rubygems
I recommend packaging 'rubygems-update' (fpm -s gem -t deb rubygems-update) and
possibly running the update_rubygems as a postinstall, even though I don't like
postinstalls. I haven't looked yet to see what is required to mimic (if
possible) the actions of that script simply in a tarball.
## Python
https://www.debian.org/doc/packaging-manuals/python-policy/ap-packaging_tools.html
Debian python packages all rely on some form of python-central or
python-support (different tools that do similar/same things? I don't know)
As I found, disabling postinst scripts in Debian causes Python to stop working.
The postinst scripts generally look like this:
if which update-python-modules >/dev/null 2>&1; then
update-python-modules SOMEPACKAGENAME.public
fi
I don't believe in postinst scripts, and I also feel like requiring a
postinstall step to make a python module work is quite silly - though I'm sure
(I hope) Debian had good reason.
So, I'm going to try working on a howto for recommended ways to build python
packages with fpm in debian. It will likely require a one-time addition to
site.py (/usr/lib/python2.6/site.py) or some other PYTHONPATH hackery, though
I don't know just yet.
It will also require special setup.py invocations as Debian has patched distutils to
install python packages, by default, to a place that requires again the
python-central/support tools to run to make them work.

View File

@ -1,7 +1,7 @@
fpm
===
|Chat| |Gem|
|Gem|
The goal of fpm is to make it easy and quick to build packages such as rpms,
debs, OSX packages, etc.
@ -41,7 +41,7 @@ situations), but small tweaks can fix it.
And sometimes, there isn't a package available for the tool you need.
And sometimes if you ask "How do I get python 3 on CentOS 5?" some unhelpful
And sometimes if you ask "How do I get python 3.9 on RHEL 8?" some unhelpful
trolls will tell you to "Use another distro"
Further, job switches have me flipping between Ubuntu and CentOS. These use
@ -97,7 +97,5 @@ Targets:
.. include: docs/contributing
.. |Chat| image:: https://img.shields.io/badge/irc-%23fpm%20on%20freenode-brightgreen.svg
:target: https://webchat.freenode.net/?channels=fpm
.. |Gem| image:: https://img.shields.io/gem/v/fpm.svg
:target: https://rubygems.org/gems/fpm

View File

@ -2,10 +2,8 @@
FROM debian:latest
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3-pip
RUN pip3 install Sphinx
RUN apt-get install -y python3-sphinx #pip3 install Sphinx
#==1.8
RUN pip3 install sphinx_rtd_theme
RUN pip3 install alabaster
RUN pip3 install sphinx-autobuild
RUN apt-get install -y python3-sphinx python3-sphinx-rtd-theme python3-sphinx-autobuild
CMD ["/bin/bash"]

View File

@ -20,7 +20,7 @@ changelog_links.rst: ../CHANGELOG.rst Makefile
# CLI reference is generated based on the the command line flags
cli-reference.rst: generate-cli-reference.rb Makefile
cli-reference.rst: ../lib/fpm/package/*.rb ../lib/fpm/package.rb
ruby generate-cli-reference.rb > $@
ruby -I ../lib generate-cli-reference.rb > $@
package-type-cli:
$(MAKE) $(addprefix packages/cli/,$(addsuffix .rst,$(notdir $(basename $(wildcard ../lib/fpm/package/*.rb)))))
@ -28,8 +28,8 @@ package-type-cli:
packages/cli:
mkdir $@
packages/cli/%.rst: ../lib/fpm/package/%.rb packages/cli
ruby generate-cli-reference.rb $* > $@
packages/cli/%.rst: ../lib/fpm/package/%.rb packages/cli generate-cli-reference.rb Makefile
ruby -I ../lib generate-cli-reference.rb $* > $@
.PHONY: docker-prep
@ -38,7 +38,7 @@ docker-prep: Dockerfile
|| docker build -t $(IMAGE) .
.PHONY: build
build: $(GENERATED_FILES) | docker-prep
build: $(GENERATED_FILES) | docker-prep package-type-cli
docker run -it -v $$PWD/../:/project:z $(IMAGE) sh -xc 'make -C /project/docs html && chown -R 1000:1000 /project/docs'

View File

@ -37,6 +37,7 @@
.. _#1253: https://github.com/jordansissel/fpm/issues/1253
.. _#1259: https://github.com/jordansissel/fpm/issues/1259
.. _#1262: https://github.com/jordansissel/fpm/issues/1262
.. _#1264: https://github.com/jordansissel/fpm/issues/1264
.. _#1266: https://github.com/jordansissel/fpm/issues/1266
.. _#1287: https://github.com/jordansissel/fpm/issues/1287
.. _#1291: https://github.com/jordansissel/fpm/issues/1291
@ -68,6 +69,7 @@
.. _#1376: https://github.com/jordansissel/fpm/issues/1376
.. _#1379: https://github.com/jordansissel/fpm/issues/1379
.. _#1384: https://github.com/jordansissel/fpm/issues/1384
.. _#1385: https://github.com/jordansissel/fpm/issues/1385
.. _#1390: https://github.com/jordansissel/fpm/issues/1390
.. _#1391: https://github.com/jordansissel/fpm/issues/1391
.. _#1395: https://github.com/jordansissel/fpm/issues/1395
@ -89,7 +91,12 @@
.. _#1511: https://github.com/jordansissel/fpm/issues/1511
.. _#1514: https://github.com/jordansissel/fpm/issues/1514
.. _#1515: https://github.com/jordansissel/fpm/issues/1515
.. _#1519: https://github.com/jordansissel/fpm/issues/1519
.. _#1522: https://github.com/jordansissel/fpm/issues/1522
.. _#1523: https://github.com/jordansissel/fpm/issues/1523
.. _#1528: https://github.com/jordansissel/fpm/issues/1528
.. _#1592: https://github.com/jordansissel/fpm/issues/1592
.. _#1627: https://github.com/jordansissel/fpm/issues/1627
.. _#1636: https://github.com/jordansissel/fpm/issues/1636
.. _#1642: https://github.com/jordansissel/fpm/issues/1642
.. _#1667: https://github.com/jordansissel/fpm/issues/1667
@ -108,6 +115,7 @@
.. _#1738: https://github.com/jordansissel/fpm/issues/1738
.. _#1739: https://github.com/jordansissel/fpm/issues/1739
.. _#1740: https://github.com/jordansissel/fpm/issues/1740
.. _#1741: https://github.com/jordansissel/fpm/issues/1741
.. _#1745: https://github.com/jordansissel/fpm/issues/1745
.. _#1748: https://github.com/jordansissel/fpm/issues/1748
.. _#1749: https://github.com/jordansissel/fpm/issues/1749
@ -121,11 +129,14 @@
.. _#1772: https://github.com/jordansissel/fpm/issues/1772
.. _#1774: https://github.com/jordansissel/fpm/issues/1774
.. _#1775: https://github.com/jordansissel/fpm/issues/1775
.. _#1784: https://github.com/jordansissel/fpm/issues/1784
.. _#1785: https://github.com/jordansissel/fpm/issues/1785
.. _#1786: https://github.com/jordansissel/fpm/issues/1786
.. _#1788: https://github.com/jordansissel/fpm/issues/1788
.. _#1794: https://github.com/jordansissel/fpm/issues/1794
.. _#1797: https://github.com/jordansissel/fpm/issues/1797
.. _#1798: https://github.com/jordansissel/fpm/issues/1798
.. _#1800: https://github.com/jordansissel/fpm/issues/1800
.. _#1803: https://github.com/jordansissel/fpm/issues/1803
.. _#1811: https://github.com/jordansissel/fpm/issues/1811
.. _#1812: https://github.com/jordansissel/fpm/issues/1812
@ -135,8 +146,11 @@
.. _#1818: https://github.com/jordansissel/fpm/issues/1818
.. _#1820: https://github.com/jordansissel/fpm/issues/1820
.. _#1821: https://github.com/jordansissel/fpm/issues/1821
.. _#1823: https://github.com/jordansissel/fpm/issues/1823
.. _#1825: https://github.com/jordansissel/fpm/issues/1825
.. _#1827: https://github.com/jordansissel/fpm/issues/1827
.. _#1829: https://github.com/jordansissel/fpm/issues/1829
.. _#1831: https://github.com/jordansissel/fpm/issues/1831
.. _#1832: https://github.com/jordansissel/fpm/issues/1832
.. _#1833: https://github.com/jordansissel/fpm/issues/1833
.. _#1834: https://github.com/jordansissel/fpm/issues/1834
@ -151,20 +165,91 @@
.. _#1854: https://github.com/jordansissel/fpm/issues/1854
.. _#1856: https://github.com/jordansissel/fpm/issues/1856
.. _#185: https://github.com/jordansissel/fpm/issues/185
.. _#1864: https://github.com/jordansissel/fpm/issues/1864
.. _#186: https://github.com/jordansissel/fpm/issues/186
.. _#1875: https://github.com/jordansissel/fpm/issues/1875
.. _#1876: https://github.com/jordansissel/fpm/issues/1876
.. _#1877: https://github.com/jordansissel/fpm/issues/1877
.. _#1879: https://github.com/jordansissel/fpm/issues/1879
.. _#187: https://github.com/jordansissel/fpm/issues/187
.. _#1880: https://github.com/jordansissel/fpm/issues/1880
.. _#1882: https://github.com/jordansissel/fpm/issues/1882
.. _#1884: https://github.com/jordansissel/fpm/issues/1884
.. _#1886: https://github.com/jordansissel/fpm/issues/1886
.. _#1893: https://github.com/jordansissel/fpm/issues/1893
.. _#1895: https://github.com/jordansissel/fpm/issues/1895
.. _#1896: https://github.com/jordansissel/fpm/issues/1896
.. _#1897: https://github.com/jordansissel/fpm/issues/1897
.. _#1898: https://github.com/jordansissel/fpm/issues/1898
.. _#1899: https://github.com/jordansissel/fpm/issues/1899
.. _#1902: https://github.com/jordansissel/fpm/issues/1902
.. _#1905: https://github.com/jordansissel/fpm/issues/1905
.. _#1907: https://github.com/jordansissel/fpm/issues/1907
.. _#1908: https://github.com/jordansissel/fpm/issues/1908
.. _#1909: https://github.com/jordansissel/fpm/issues/1909
.. _#190: https://github.com/jordansissel/fpm/issues/190
.. _#1912: https://github.com/jordansissel/fpm/issues/1912
.. _#1913: https://github.com/jordansissel/fpm/issues/1913
.. _#1916: https://github.com/jordansissel/fpm/issues/1916
.. _#191: https://github.com/jordansissel/fpm/issues/191
.. _#1923: https://github.com/jordansissel/fpm/issues/1923
.. _#1934: https://github.com/jordansissel/fpm/issues/1934
.. _#1935: https://github.com/jordansissel/fpm/issues/1935
.. _#1937: https://github.com/jordansissel/fpm/issues/1937
.. _#1939: https://github.com/jordansissel/fpm/issues/1939
.. _#193: https://github.com/jordansissel/fpm/issues/193
.. _#1940: https://github.com/jordansissel/fpm/issues/1940
.. _#1942: https://github.com/jordansissel/fpm/issues/1942
.. _#1946: https://github.com/jordansissel/fpm/issues/1946
.. _#1948: https://github.com/jordansissel/fpm/issues/1948
.. _#1949: https://github.com/jordansissel/fpm/issues/1949
.. _#194: https://github.com/jordansissel/fpm/issues/194
.. _#1950: https://github.com/jordansissel/fpm/issues/1950
.. _#1955: https://github.com/jordansissel/fpm/issues/1955
.. _#1959: https://github.com/jordansissel/fpm/issues/1959
.. _#196: https://github.com/jordansissel/fpm/issues/196
.. _#1978: https://github.com/jordansissel/fpm/issues/1978
.. _#1981: https://github.com/jordansissel/fpm/issues/1981
.. _#1982: https://github.com/jordansissel/fpm/issues/1982
.. _#1988: https://github.com/jordansissel/fpm/issues/1988
.. _#198: https://github.com/jordansissel/fpm/issues/198
.. _#2009: https://github.com/jordansissel/fpm/issues/2009
.. _#2011: https://github.com/jordansissel/fpm/issues/2011
.. _#2017: https://github.com/jordansissel/fpm/issues/2017
.. _#2027: https://github.com/jordansissel/fpm/issues/2027
.. _#2029: https://github.com/jordansissel/fpm/issues/2029
.. _#202: https://github.com/jordansissel/fpm/issues/202
.. _#2036: https://github.com/jordansissel/fpm/issues/2036
.. _#2040: https://github.com/jordansissel/fpm/issues/2040
.. _#2041: https://github.com/jordansissel/fpm/issues/2041
.. _#204: https://github.com/jordansissel/fpm/issues/204
.. _#2053: https://github.com/jordansissel/fpm/issues/2053
.. _#2054: https://github.com/jordansissel/fpm/issues/2054
.. _#205: https://github.com/jordansissel/fpm/issues/205
.. _#2062: https://github.com/jordansissel/fpm/issues/2062
.. _#2063: https://github.com/jordansissel/fpm/issues/2063
.. _#2064: https://github.com/jordansissel/fpm/issues/2064
.. _#2065: https://github.com/jordansissel/fpm/issues/2065
.. _#2066: https://github.com/jordansissel/fpm/issues/2066
.. _#2067: https://github.com/jordansissel/fpm/issues/2067
.. _#2068: https://github.com/jordansissel/fpm/issues/2068
.. _#206: https://github.com/jordansissel/fpm/issues/206
.. _#2072: https://github.com/jordansissel/fpm/issues/2072
.. _#2074: https://github.com/jordansissel/fpm/issues/2074
.. _#2076: https://github.com/jordansissel/fpm/issues/2076
.. _#207: https://github.com/jordansissel/fpm/issues/207
.. _#2082: https://github.com/jordansissel/fpm/issues/2082
.. _#2084: https://github.com/jordansissel/fpm/issues/2084
.. _#2085: https://github.com/jordansissel/fpm/issues/2085
.. _#2087: https://github.com/jordansissel/fpm/issues/2087
.. _#2088: https://github.com/jordansissel/fpm/issues/2088
.. _#208: https://github.com/jordansissel/fpm/issues/208
.. _#2092: https://github.com/jordansissel/fpm/issues/2092
.. _#2102: https://github.com/jordansissel/fpm/issues/2102
.. _#2103: https://github.com/jordansissel/fpm/issues/2103
.. _#2104: https://github.com/jordansissel/fpm/issues/2104
.. _#2105: https://github.com/jordansissel/fpm/issues/2105
.. _#2106: https://github.com/jordansissel/fpm/issues/2106
.. _#212: https://github.com/jordansissel/fpm/issues/212
.. _#213: https://github.com/jordansissel/fpm/issues/213
.. _#215: https://github.com/jordansissel/fpm/issues/215

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,9 @@ import os
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = [
'sphinx_rtd_theme'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -46,7 +48,7 @@ master_doc = 'index'
# General information about the project.
project = u'fpm - packaging made simple'
copyright = u'2016, Jordan Sissel'
copyright = u'2025, Jordan Sissel and contributors'
author = u'Jordan Sissel'
# The version info for the project you're documenting, acts as replacement for
@ -54,16 +56,16 @@ author = u'Jordan Sissel'
# built documents.
#
# The short X.Y version.
version = u'1.9'
version = u'1.17'
# The full version, including alpha/beta/rc tags.
release = u'1.9.0'
release = u'1.17.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = "en"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:

View File

@ -56,10 +56,15 @@ when it lists the FPM gem:
If your system doesn't have `bsdtar` by default, make sure to install it or some
tests will fail:
apt-get install bsdtar
apt-get install bsdtar || apt install libarchive-tools
yum install bsdtar
You also need these tools:
apt-get install lintian cpanminus
Next, run make in root of the FPM repo. If there are any problems (such as
missing dependencies) you should receive an error

View File

@ -2,6 +2,8 @@
require_relative "../lib/fpm/command"
flagsort = lambda { |x| x.sub(/^--(?:\[no-\])?/, "") }
if ARGV.length == 0
puts "Command-line Reference"
puts "=========================="
@ -13,7 +15,8 @@ if ARGV.length == 0
puts "General Options"
puts "---------------"
FPM::Command.instance_variable_get(:@declared_options).each do |option|
#FPM::Command.instance_variable_get(:@declared_options).sort_by { |o| flagsort.call(o.switches.first) }.each do |option|
FPM::Command::GENERAL_OPTIONS.sort_by { |o| flagsort.call(o.switches.first) }.each do |option|
text = option.description.gsub("\n", " ")
if option.type == :flag
@ -49,7 +52,7 @@ FPM::Package.types.sort_by { |k,v| k }.each do |name, type|
puts "This package type has no additional options"
end
options.sort_by { |flag, _| flag }.each do |flag, param, help, options, block|
options.sort_by { |flag, _| flagsort.call(flag.first) }.each do |flag, param, help, options, block|
if param == :flag
puts "* ``#{flag.first}``"
else

View File

@ -20,7 +20,7 @@ The source could be a:
* python (using easy_install or a local setup.py) package - ``python``
* python virtualenv - ``virtualenv``
* pear package - ``pear``
* pearl (cpan) module - ``cpan``
* perl (cpan) module - ``cpan``
* .deb package - ``deb``
* .rpm package - ``rpm``
* pacman (.pkg.tar.zst) package - ``pacman``

View File

@ -7,7 +7,7 @@ Installing FPM
--------------
.. note::
You must have ruby installed on your machine before installing fpm. `Here` are instructions to install Ruby on your machine.
You must have ruby installed on your machine before installing fpm. `Here`_ are instructions to install Ruby on your machine.
.. _Here: https://www.ruby-lang.org/en/documentation/installation/
@ -22,7 +22,7 @@ To make sure fpm is installed correctly, try running the following command::
You should get some output like this, although the exact output will depend on which version of FPM you have installed.::
% fpm --version
1.14.0
1.17.0
Now you can go on to `using FPM! <getting-started.html>`_

12
docs/packages/apk.rst Normal file
View File

@ -0,0 +1,12 @@
apk - Alpine package format
===========================
Supported Uses in FPM
---------------------
fpm supports using ``apk`` only as an output type. This means you can create ``apk`` packages from input types like ``deb``, ``dir``, or ``npm``
apk-specific command line flags
-------------------------------
.. include:: cli/apk.rst

View File

@ -1,21 +1,21 @@
* ``--[no-]cpan-cpanm-force``
- Pass the --force parameter to cpanm
* ``--[no-]cpan-mirror-only``
- Only use the specified mirror for metadata.
* ``--[no-]cpan-sandbox-non-core``
- Sandbox all non-core modules, even if they're already installed
* ``--[no-]cpan-test``
- Run the tests before packaging?
* ``--[no-]cpan-verbose``
- Produce verbose output from cpanm?
* ``--cpan-cpanm-bin CPANM_EXECUTABLE``
- The path to the cpanm executable you wish to run.
* ``--[no-]cpan-cpanm-force``
- Pass the --force parameter to cpanm
* ``--cpan-mirror CPAN_MIRROR``
- The CPAN mirror to use instead of the default.
* ``--[no-]cpan-mirror-only``
- Only use the specified mirror for metadata.
* ``--cpan-package-name-prefix NAME_PREFIX``
- Name to prefix the package name with.
* ``--cpan-perl-bin PERL_EXECUTABLE``
- The path to the perl executable you wish to run.
* ``--cpan-perl-lib-path PERL_LIB_PATH``
- Path of target Perl Libraries
* ``--[no-]cpan-sandbox-non-core``
- Sandbox all non-core modules, even if they're already installed
* ``--[no-]cpan-test``
- Run the tests before packaging?
* ``--[no-]cpan-verbose``
- Produce verbose output from cpanm?

View File

@ -1,33 +1,19 @@
* ``--[no-]deb-auto-config-files``
- Init script and default configuration files will be labeled as configuration files for Debian packages.
* ``--[no-]deb-generate-changes``
- Generate PACKAGENAME.changes file.
* ``--[no-]deb-ignore-iteration-in-dependencies``
- 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
* ``--[no-]deb-maintainerscripts-force-errorchecks``
- Activate errexit shell option according to lintian. https://lintian.debian.org/tags/maintainer-script-ignores-errors.html
* ``--[no-]deb-no-default-config-files``
- Do not add all files in /etc as configuration files by default for Debian packages.
* ``--[no-]deb-systemd-auto-start``
- Start service after install or upgrade
* ``--[no-]deb-systemd-enable``
- Enable service on install or upgrade
* ``--[no-]deb-systemd-restart-after-upgrade``
- Restart service after upgrade
* ``--[no-]deb-use-file-permissions``
- Use existing file permissions when defining ownership and modes
* ``--deb-activate EVENT``
- Package activates EVENT trigger
* ``--deb-activate-noawait EVENT``
- Package activates EVENT trigger
* ``--deb-after-purge FILE``
- A script to be run after package removal to purge remaining (config) files (a.k.a. postrm purge within apt-get purge)
* ``--[no-]deb-auto-config-files``
- Init script and default configuration files will be labeled as configuration files for Debian packages.
* ``--deb-build-depends DEPENDENCY``
- Add DEPENDENCY as a Build-Depends
* ``--deb-changelog FILEPATH``
- Add FILEPATH as debian changelog
* ``--deb-compression COMPRESSION``
- The compression type to use, must be one of gz, bzip2, xz, none.
- The compression type to use, must be one of gz, bzip2, xz, zst, none.
* ``--deb-compression-level [0-9]``
- Select a compression level. 0 is none or minimal. 9 is max compression.
* ``--deb-config SCRIPTPATH``
- Add SCRIPTPATH as debconf config file.
* ``--deb-custom-control FILEPATH``
@ -38,8 +24,12 @@
- Set the deb distribution.
* ``--deb-field 'FIELD: VALUE'``
- Add custom field to the control file
* ``--[no-]deb-generate-changes``
- Generate PACKAGENAME.changes file.
* ``--deb-group GROUP``
- The group owner of files in this package
* ``--[no-]deb-ignore-iteration-in-dependencies``
- 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
* ``--deb-init FILEPATH``
- Add FILEPATH as an init script
* ``--deb-installed-size KILOBYTES``
@ -48,8 +38,12 @@
- Package is interested in EVENT trigger
* ``--deb-interest-noawait EVENT``
- Package is interested in EVENT trigger without awaiting
* ``--[no-]deb-maintainerscripts-force-errorchecks``
- Activate errexit shell option according to lintian. https://lintian.debian.org/tags/maintainer-script-ignores-errors.html
* ``--deb-meta-file FILEPATH``
- Add FILEPATH to DEBIAN directory
* ``--[no-]deb-no-default-config-files``
- Do not add all files in /etc as configuration files by default for Debian packages.
* ``--deb-pre-depends DEPENDENCY``
- Add DEPENDENCY as a Pre-Depends
* ``--deb-priority PRIORITY``
@ -62,12 +56,22 @@
- Add PACKAGE to Suggests
* ``--deb-systemd FILEPATH``
- Add FILEPATH as a systemd script
* ``--[no-]deb-systemd-auto-start``
- Start service after install or upgrade
* ``--[no-]deb-systemd-enable``
- Enable service on install or upgrade
* ``--deb-systemd-path FILEPATH``
- Relative path to the systemd service directory
* ``--[no-]deb-systemd-restart-after-upgrade``
- Restart service after upgrade
* ``--deb-templates FILEPATH``
- Add FILEPATH as debconf templates file.
* ``--deb-upstart FILEPATH``
- Add FILEPATH as an upstart script
* ``--deb-upstream-changelog FILEPATH``
- Add FILEPATH as upstream changelog
* ``--[no-]deb-use-file-permissions``
- Use existing file permissions when defining ownership and modes
* ``--deb-user USER``
- The owner of files in this package

View File

@ -1,3 +1,5 @@
* ``--freebsd-origin ABI``
- Sets the FreeBSD 'origin' pkg field
* ``--freebsd-osversion VERSION``
- Sets the FreeBSD 'version' pkg field, ie 12 or 13, use '*' for all.

View File

@ -1,3 +1,7 @@
* ``--gem-bin-path DIRECTORY``
- The directory to install gem executables
* ``--gem-disable-dependency gem_name``
- The gem name to remove from dependency list
* ``--[no-]gem-embed-dependencies``
- Should the gem dependencies be installed?
* ``--[no-]gem-env-shebang``
@ -6,14 +10,6 @@
- Should the package dependencies be prefixed?
* ``--[no-]gem-fix-name``
- Should the target package name be prefixed?
* ``--[no-]gem-prerelease``
- Allow prerelease versions of a gem
* ``--[no-]gem-version-bins``
- Append the version to the bins
* ``--gem-bin-path DIRECTORY``
- The directory to install gem executables
* ``--gem-disable-dependency gem_name``
- The gem name to remove from dependency list
* ``--gem-gem PATH_TO_GEM``
- The path to the 'gem' tool (defaults to 'gem' and searches your $PATH)
* ``--gem-git-branch GIT_BRANCH``
@ -24,8 +20,12 @@
- Name to prefix the package name with.
* ``--gem-package-prefix NAMEPREFIX``
- (DEPRECATED, use --package-name-prefix) Name to prefix the package name with.
* ``--[no-]gem-prerelease``
- Allow prerelease versions of a gem
* ``--gem-shebang SHEBANG``
- Replace the shebang in the executables in the bin path with a custom string
* ``--gem-stagingdir STAGINGDIR``
- The directory where fpm installs the gem temporarily before conversion. Normally a random subdirectory of workdir.
* ``--[no-]gem-version-bins``
- Append the version to the bins

View File

@ -1,11 +1,11 @@
* ``--[no-]osxpkg-payload-free``
- Define no payload, assumes use of script options.
* ``--osxpkg-dont-obsolete DONT_OBSOLETE_PATH``
- A file path for which to 'dont-obsolete' in the built PackageInfo. Can be specified multiple times.
* ``--osxpkg-identifier-prefix IDENTIFIER_PREFIX``
- Reverse domain prefix prepended to package identifier, ie. 'org.great.my'. If this is omitted, the identifer will be the package name.
* ``--osxpkg-ownership OWNERSHIP``
- --ownership option passed to pkgbuild. Defaults to 'recommended'. See pkgbuild(1).
* ``--[no-]osxpkg-payload-free``
- Define no payload, assumes use of script options.
* ``--osxpkg-postinstall-action POSTINSTALL_ACTION``
- Post-install action provided in package metadata. Optionally one of 'logout', 'restart', 'shutdown'.

View File

@ -1,13 +1,13 @@
* ``--[no-]p5p-lint``
- Check manifest with pkglint
* ``--[no-]p5p-validate``
- Validate with pkg install
* ``--p5p-group GROUP``
- Set the group to GROUP in the prototype file.
* ``--[no-]p5p-lint``
- Check manifest with pkglint
* ``--p5p-publisher PUBLISHER``
- Set the publisher name for the repository
* ``--p5p-user USER``
- Set the user to USER in the prototype files.
* ``--[no-]p5p-validate``
- Validate with pkg install
* ``--p5p-zonetype ZONETYPE``
- Set the allowed zone types (global, nonglobal, both)

View File

@ -1,11 +1,11 @@
* ``--[no-]pacman-use-file-permissions``
- Use existing file permissions when defining ownership and modes
* ``--pacman-compression COMPRESSION``
- The compression type to use, must be one of gz, bzip2, xz, zstd, none.
* ``--pacman-group GROUP``
- The group owner of files in this package
* ``--pacman-optional-depends PACKAGE``
- Add an optional dependency to the pacman package.
* ``--[no-]pacman-use-file-permissions``
- Use existing file permissions when defining ownership and modes
* ``--pacman-user USER``
- The owner of files in this package

View File

@ -1,9 +1,9 @@
* ``--[no-]pear-channel-update``
- call 'pear channel-update' prior to installation
* ``--pear-bin-dir BIN_DIR``
- Directory to put binaries in
* ``--pear-channel CHANNEL_URL``
- The pear channel url to use instead of the default.
* ``--[no-]pear-channel-update``
- call 'pear channel-update' prior to installation
* ``--pear-data-dir DATA_DIR``
- Specify php dir relative to prefix if differs from pear default (pear/data)
* ``--pear-package-name-prefix PREFIX``

View File

@ -2,4 +2,6 @@
- The working directory used by the service
* ``--pleaserun-name SERVICE_NAME``
- The name of the service you are creating
* ``--pleaserun-user USER``
- The user to use for executing this program.

View File

@ -1,27 +1,29 @@
* ``--python-bin PYTHON_EXECUTABLE``
- The path to the python executable you wish to run.
* ``--[no-]python-dependencies``
- Include requirements defined in setup.py as dependencies.
- Include requirements defined by the python package as dependencies.
* ``--python-disable-dependency python_package_name``
- The python package name to remove from dependency list
* ``--[no-]python-downcase-dependencies``
- Should the package dependencies be in lowercase?
* ``--[no-]python-downcase-name``
- Should the target package name be in lowercase?
* ``--python-easyinstall EASYINSTALL_EXECUTABLE``
- The path to the easy_install executable tool
* ``--[no-]python-fix-dependencies``
- Should the package dependencies be prefixed?
* ``--[no-]python-fix-name``
- Should the target package name be prefixed?
* ``--python-install-bin BIN_PATH``
- (DEPRECATED, does nothing) The path to where python scripts should be installed to.
* ``--python-install-data DATA_PATH``
- (DEPRECATED, does nothing) The path to where data should be installed to. This is equivalent to 'python setup.py --install-data DATA_PATH
* ``--python-install-lib LIB_PATH``
- (DEPRECATED, does nothing) 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()'
* ``--[no-]python-internal-pip``
- Use the pip module within python to install modules - aka 'python -m pip'. This is the recommended usage since Python 3.4 (2014) instead of invoking the 'pip' script
* ``--[no-]python-obey-requirements-txt``
- Use a requirements.txt file in the top-level directory of the python package for dependency detection.
* ``--python-bin PYTHON_EXECUTABLE``
- The path to the python executable you wish to run.
* ``--python-disable-dependency python_package_name``
- The python package name to remove from dependency list
* ``--python-easyinstall EASYINSTALL_EXECUTABLE``
- The path to the easy_install executable tool
* ``--python-install-bin BIN_PATH``
- The path to where python scripts should be installed to.
* ``--python-install-data DATA_PATH``
- The path to where data should be installed to. This is equivalent to 'python setup.py --install-data DATA_PATH
* ``--python-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()'
* ``--python-package-name-prefix PREFIX``
- Name to prefix the package name with.
* ``--python-package-prefix NAMEPREFIX``
@ -31,9 +33,9 @@
* ``--python-pypi PYPI_URL``
- PyPi Server uri for retrieving packages.
* ``--python-scripts-executable PYTHON_EXECUTABLE``
- Set custom python interpreter in installing scripts. By default distutils will replace python interpreter in installing scripts (specified by shebang) with current python interpreter (sys.executable). This option is equivalent to appending 'build_scripts --executable PYTHON_EXECUTABLE' arguments to 'setup.py install' command.
- (DEPRECATED) Set custom python interpreter in installing scripts. By default distutils will replace python interpreter in installing scripts (specified by shebang) with current python interpreter (sys.executable). This option is equivalent to appending 'build_scripts --executable PYTHON_EXECUTABLE' arguments to 'setup.py install' command.
* ``--python-setup-py-arguments setup_py_argument``
- Arbitrary argument(s) to be passed to setup.py
- (DEPRECATED) Arbitrary argument(s) to be passed to setup.py
* ``--python-trusted-host PYPI_TRUSTED``
- Mark this host or host:port pair as trusted for pip

View File

@ -1,25 +1,15 @@
* ``--rpm-attr ATTRFILE``
- Set the attribute for a file (%attr), e.g. --rpm-attr 750,user1,group1:/some/file
* ``--[no-]rpm-auto-add-directories``
- Auto add directories not part of filesystem
* ``--rpm-auto-add-exclude-directories DIRECTORIES``
- Additional directories ignored by '--rpm-auto-add-directories' flag
* ``--[no-]rpm-autoprov``
- Enable RPM's AutoProv option
* ``--[no-]rpm-autoreq``
- Enable RPM's AutoReq option
* ``--[no-]rpm-autoreqprov``
- Enable RPM's AutoReqProv option
* ``--[no-]rpm-ignore-iteration-in-dependencies``
- 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
* ``--[no-]rpm-macro-expansion``
- install-time macro expansion in %pre %post %preun %postun scripts (see: https://rpm.org/user_doc/scriptlet_expansion.html)
* ``--[no-]rpm-sign``
- Pass --sign to rpmbuild
* ``--[no-]rpm-use-file-permissions``
- Use existing file permissions when defining ownership and modes.
* ``--[no-]rpm-verbatim-gem-dependencies``
- 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).
* ``--rpm-attr ATTRFILE``
- Set the attribute for a file (%attr), e.g. --rpm-attr 750,user1,group1:/some/file
* ``--rpm-auto-add-exclude-directories DIRECTORIES``
- Additional directories ignored by '--rpm-auto-add-directories' flag
* ``--rpm-changelog FILEPATH``
- Add changelog from FILEPATH contents
* ``--rpm-compression none|xz|xzmt|gzip|bzip2``
@ -40,8 +30,14 @@
- Set %filter_from_requires to the supplied REGEX.
* ``--rpm-group GROUP``
- Set the group to GROUP in the %files section. Overrides the group when used with use-file-permissions setting.
* ``--[no-]rpm-ignore-iteration-in-dependencies``
- 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
* ``--rpm-init FILEPATH``
- Add FILEPATH as an init script
* ``--[no-]rpm-macro-expansion``
- install-time macro expansion in %pre %post %preun %postun scripts (see: https://rpm-software-management.github.io/rpm/manual/scriptlet_expansion.html)
* ``--[no-]rpm-old-perl-dependency-name``
- Use older 'perl' depdency name. Newer Red Hat (and derivatives) use a dependency named 'perl-interpreter'.
* ``--rpm-os OS``
- The operating system to target this rpm for. You want to set this to 'linux' if you are using fpm on OS X, for example
* ``--rpm-posttrans FILE``
@ -50,20 +46,26 @@
- pretrans script
* ``--rpm-rpmbuild-define DEFINITION``
- Pass a --define argument to rpmbuild.
* ``--[no-]rpm-sign``
- Pass --sign to rpmbuild
* ``--rpm-summary SUMMARY``
- Set the RPM summary. Overrides the first line on the description if set
* ``--rpm-tag TAG``
- Adds a custom tag in the spec file as is. Example: --rpm-tag 'Requires(post): /usr/sbin/alternatives'
* ``--rpm-trigger-after-install '[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
- 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: https://rpm-software-management.github.io/rpm/manual/triggers.html
* ``--rpm-trigger-after-target-uninstall '[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
- 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: https://rpm-software-management.github.io/rpm/manual/triggers.html
* ``--rpm-trigger-before-install '[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
- 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: https://rpm-software-management.github.io/rpm/manual/triggers.html
* ``--rpm-trigger-before-uninstall '[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
- 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: https://rpm-software-management.github.io/rpm/manual/triggers.html
* ``--[no-]rpm-use-file-permissions``
- Use existing file permissions when defining ownership and modes.
* ``--rpm-user USER``
- Set the user to USER in the %files section. Overrides the user when used with use-file-permissions setting.
* ``--[no-]rpm-verbatim-gem-dependencies``
- 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).
* ``--rpm-verifyscript FILE``
- a script to be run on verification

View File

@ -1,11 +1,7 @@
* ``--[no-]virtualenv-fix-name``
- Should the target package name be prefixed?
* ``--[no-]virtualenv-setup-install``
- After building virtualenv run setup.py install useful when building a virtualenv for packages and including their requirements from
* ``--[no-]virtualenv-system-site-packages``
- Give the virtual environment access to the global site-packages
* ``--virtualenv-find-links PIP_FIND_LINKS``
- If a url or path to an html file, then parse for links to archives. If a local path or file:// url that's a directory, then look for archives in the directory listing.
* ``--[no-]virtualenv-fix-name``
- Should the target package name be prefixed?
* ``--virtualenv-install-location DIRECTORY``
- DEPRECATED: Use --prefix instead. Location to which to install the virtualenv by default.
* ``--virtualenv-other-files-dir DIRECTORY``
@ -16,4 +12,8 @@
- PyPi Server uri for retrieving packages.
* ``--virtualenv-pypi-extra-url PYPI_EXTRA_URL``
- PyPi extra-index-url for pointing to your priviate PyPi
* ``--[no-]virtualenv-setup-install``
- After building virtualenv run setup.py install useful when building a virtualenv for packages and including their requirements from requirements.txt
* ``--[no-]virtualenv-system-site-packages``
- Give the virtual environment access to the global site-packages

View File

@ -60,6 +60,6 @@ Fun Examples
Do you have any examples you want to share that use the ``cpan`` package type? Share your knowledge here: https://github.com/jordansissel/fpm/issues/new
cpan-specific command line flags
-------------------------------
--------------------------------
.. include:: cli/cpan.rst

View File

@ -1,5 +1,5 @@
dir - Local Files
===============================
=================
Supported Uses in FPM
---------------------
@ -14,7 +14,7 @@ Any number of arguments are supported and behave as follows:
1) A path to a local file or directory will be put into the output package as-is with the same path, contents, and metadata (file owner, modification date, etc)
2) A syntax of "localpath=destinationpath" to copy local paths into the output package with the destination path.
The local file paths are modified by the ``--chdir`` flag. The destination file paths are modified by the `--prefix`` flag.
The local file paths are modified by the ``--chdir`` flag. The destination file paths are modified by the ``--prefix`` flag.
Sample Usage
------------

12
docs/packages/freebsd.rst Normal file
View File

@ -0,0 +1,12 @@
freebsd - FreeBSD pkg
===============================
Supported Uses in FPM
---------------------
fpm supports using ``freebsd`` only as an output type. This means you can create ``freebsd`` packages from input types like ``deb``, ``dir``, or ``npm``
freebsd-specific command line flags
-----------------------------------
.. include:: cli/freebsd.rst

13
docs/packages/gem.rst Normal file
View File

@ -0,0 +1,13 @@
gem - Ruby's rubygem packages
=============================
Supported Uses in FPM
---------------------
fpm supports using ``gem`` only as an input type. This means you can convert
``gem`` input packages to output packages like ``deb``, ``rpm``, and more.
gem-specific command line flags
-------------------------------
.. include:: cli/gem.rst

15
docs/packages/osxpkg.rst Normal file
View File

@ -0,0 +1,15 @@
osxpkg - Apple macOS and OSX packages
=====================================
Supported Uses in FPM
---------------------
fpm supports input and output for Apple's package files. These files typically
end in ".pkg". This means you can read a ``.pkg`` file and convert it to a different
output type (such as a `dir` or `rpm`). It also means you can create a ``.pkg``
package file.
osxpkg-specific command line flags
----------------------------------
.. include:: cli/osxpkg.rst

15
docs/packages/p5p.rst Normal file
View File

@ -0,0 +1,15 @@
p5p - Oracle Solaris 11 p5p packages
====================================
This package format for the Solaris Image Packaging System (IPS) and should be
useful for OpenSolaris, Solaris 11, and Illumos.
Supported Uses in FPM
---------------------
fpm supports using ``p5p`` only as an output type. This means you can create ``p5p`` packages from input types like ``deb``, ``dir``, or ``npm``
p5p-specific command line flags
-------------------------------
.. include:: cli/p5p.rst

12
docs/packages/pacman.rst Normal file
View File

@ -0,0 +1,12 @@
pacman - Arch Linux package format
==================================
Supported Uses in FPM
---------------------
fpm supports input and output for Arch Linux's package format, pacman. This means you can read a pacman and convert it to a different output type (such as a `dir` or `rpm`). It also means you can create a pacman package.
pacman-specific command line flags
----------------------------------
.. include:: cli/pacman.rst

13
docs/packages/pear.rst Normal file
View File

@ -0,0 +1,13 @@
pear - PHP PEAR Packages
========================
Supported Uses in FPM
---------------------
fpm supports using ``pear`` only as an input type. This means you can convert
``pear`` input packages to output packages like ``deb``, ``rpm``, and more.
pear-specific command line flags
--------------------------------
.. include:: cli/pear.rst

12
docs/packages/pkgin.rst Normal file
View File

@ -0,0 +1,12 @@
pkgin - NetBSD's pkgsrc binary packages
=======================================
Supported Uses in FPM
---------------------
fpm supports using ``pkgin`` only as an output type. This means you can create ``pkgin`` packages from input types like ``deb``, ``dir``, or ``npm``
pkgin-specific command line flags
---------------------------------
.. include:: cli/pkgin.rst

View File

@ -0,0 +1,23 @@
pleaserun - Pleaserun services
==============================
Pleaserun helps generate service definitions for a variety of service manangers
such as systemd and sysv.
When used as an input, fpm will generate a package that include multiple service
definitions, one for each type (systemd, sysv, etc). At package installation, the package
will attempt to detect the best service manager used on the system and will
install only that definition.
You can learn more on the project website: https://github.com/jordansissel/pleaserun#readme
Supported Uses in FPM
---------------------
fpm supports using ``pleaserun`` only as an input type. This means you can convert
``pleaserun`` input packages to output packages like ``deb``, ``rpm``, and more.
pleaserun-specific command line flags
-------------------------------------
.. include:: cli/pleaserun.rst

15
docs/packages/puppet.rst Normal file
View File

@ -0,0 +1,15 @@
puppet - Puppet Manifests (deprecated)
======================================
Note: This package type hasn't been maintained since 2012 and generates puppet
manifests that target Puppet 2.4 or 3.x.
Supported Uses in FPM
---------------------
fpm supports using ``puppet`` only as an output type. This means you can create ``puppet`` packages from input types like ``deb``, ``dir``, or ``npm``
puppet-specific command line flags
----------------------------------
.. include:: cli/puppet.rst

13
docs/packages/python.rst Normal file
View File

@ -0,0 +1,13 @@
python - Python packages, PyPI, etc
===================================
Supported Uses in FPM
---------------------
fpm supports using ``python`` only as an input type. This means you can convert
``python`` input packages to output packages like ``deb``, ``rpm``, and more.
python-specific command line flags
----------------------------------
.. include:: cli/python.rst

24
docs/packages/sh.rst Normal file
View File

@ -0,0 +1,24 @@
sh - Self-managing shell archive
================================
The 'sh' output in fpm will generate a shell script that is, itself, an archive.
The resulting shell script will install the files you provided. You can run the
resulting shell script to see more helpful information.
# Create an example.sh package
% fpm -s empty -t sh -n example
# Get help.
% ./example.sh -h
Supported Uses in FPM
---------------------
fpm supports using ``sh`` only as an output type. This means you can create ``sh`` packages from input types like ``deb``, ``dir``, or ``npm``
sh-specific command line flags
-------------------------------
.. include:: cli/sh.rst

15
docs/packages/snap.rst Normal file
View File

@ -0,0 +1,15 @@
snap - Application package files for Linux
==========================================
You can learn more about ``snap`` itself by visiting the website: https://snapcraft.io/about
Supported Uses in FPM
---------------------
fpm supports input and output for snap packages. This means you can read a snap and convert it to a different output type (such as a `dir` or `rpm`). It also means you can create a snap package.
snap-specific command line flags
--------------------------------
.. include:: cli/snap.rst

19
docs/packages/solaris.rst Normal file
View File

@ -0,0 +1,19 @@
solaris - Solaris SRV4 package format
=====================================
This package format is typically used in older Solaris versions (Solaris 7, 8,
9, and 10). You may also know them by files with a SUNW prefix and may have file names that end in ".pkg".
If you're using Solaris 11, OpenSolaris, or Illumos, you might want to use `the newer package format, p5p`_.
.. _the newer package format, p5p: /packages/p5p.html
Supported Uses in FPM
---------------------
fpm supports using ``solaris`` only as an output type. This means you can create ``solaris`` packages from input types like ``deb``, ``dir``, or ``npm``
solaris-specific command line flags
-----------------------------------
.. include:: cli/solaris.rst

12
docs/packages/tar.rst Normal file
View File

@ -0,0 +1,12 @@
tar - Tape archives aka "tar" files
===================================
Supported Uses in FPM
---------------------
fpm supports input and output for NAME (tar). This means you can read a tar and convert it to a different output type (such as a `dir` or `rpm`). It also means you can create a tar package.
tar-specific command line flags
-------------------------------
.. include:: cli/tar.rst

View File

@ -0,0 +1,13 @@
virtualenv - Python virtualenv
==============================
Supported Uses in FPM
---------------------
fpm supports using ``virtualenv`` only as an input type. This means you can convert
``virtualenv`` input packages to output packages like ``deb``, ``rpm``, and more.
virtualenv-specific command line flags
--------------------------------------
.. include:: cli/virtualenv.rst

12
docs/packages/zip.rst Normal file
View File

@ -0,0 +1,12 @@
zip - Zip files
===============
Supported Uses in FPM
---------------------
fpm supports input and output for zip files. This means you can read a zip and convert it to a different output type (such as a `dir` or `rpm`). It also means you can create a zip package.
zip-specific command line flags
-------------------------------
.. include:: cli/zip.rst

View File

@ -1,2 +1 @@
# https://blog.readthedocs.com/build-errors-docutils-0-18/
docutils<0.18
sphinx_rtd_theme

View File

@ -1,53 +0,0 @@
#!/usr/bin/env ruby
$: << File.join(File.dirname(__FILE__), "..", "..", "lib")
# This example uses the API to create a package from local files
# it also creates necessary init-scripts and systemd files so our executable can be used as a service
require "fpm"
require "tmpdir"
require "fpm/package/pleaserun"
# enable logging
FPM::Util.send :module_function, :logger
FPM::Util.logger.level = :info
FPM::Util.logger.subscribe STDERR
package = FPM::Package::Dir.new
# Set some attributes
package.name = "my-service"
package.version = "1.0"
# Add a script to run after install (should be in the current directory):
package.scripts[:after_install] = 'my_after_install_script.sh'
# Example for adding special attributes
package.attributes[:deb_group] = "super-useful"
package.attributes[:rpm_group] = "super-useful"
# Add our files (should be in the current directory):
package.input("my-executable=/usr/bin/")
package.input("my-library.so=/usr/lib/")
# Now, add our init-scripts, systemd services, and so on:
pleaserun = package.convert(FPM::Package::PleaseRun)
pleaserun.input ["/usr/bin/my-executable", "--foo-from", "bar"]
# Create two output packages!
output_packages = []
output_packages << pleaserun.convert(FPM::Package::RPM)
output_packages << pleaserun.convert(FPM::Package::Deb)
# and write them both.
begin
output_packages.each do |output_package|
output = output_package.to_s
output_package.output(output)
puts "successfully created #{output}"
end
ensure
# defer cleanup until the end
output_packages.each {|p| p.cleanup}
end

View File

@ -1,16 +0,0 @@
$: << File.join(File.dirname(__FILE__), "..", "..", "lib")
require "fpm"
package = FPM::Package::Gem.new
# the Gem package takes a string name of the package to download/install.
# Example, run this script with 'rails' as an argument and it will convert
# the latest 'rails' gem into rpm.
package.input(ARGV[0])
rpm = package.convert(FPM::Package::RPM)
begin
output = "NAME-VERSION.ARCH.rpm"
rpm.output(rpm.to_s(output))
ensure
rpm.cleanup
end

View File

@ -1,18 +0,0 @@
$: << File.join(File.dirname(__FILE__), "..", "..", "lib")
require "fpm"
package = FPM::Package::Gem.new
ARGV.each do |gem|
name, version = gem.split(/[=]/, 2)
package.version = version # Allow specifying a specific version
package.input(gem)
end
rpm = package.convert(FPM::Package::RPM)
rpm.name = "rubygem-manythings"
rpm.version = "1.0"
begin
output = "NAME-VERSION.ARCH.rpm"
rpm.output(rpm.to_s(output))
ensure
rpm.cleanup
end

View File

@ -1,61 +0,0 @@
#
# 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
LIB_DIR=$(BUILD_DIR)/usr/lib/fpm
BIN_DIR=$(BUILD_DIR)/usr/bin
GEM_PATH:=$(shell readlink -f .)/build/gem
FPM_BIN=$(BIN_DIR)/fpm
BUNDLE_BIN=$(GEM_PATH)/bin/bundle
BUNDLE_CMD=$(RUBY_CMD) $(BUNDLE_BIN)
FPM_CMD=$(FPM_BIN)
GEM_CMD=$(RUBY_BIN) -S gem
.PHONY: clean
clean:
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
echo "#! $(RUBY_BIN)" > $(FPM_BIN)
echo 'load File.dirname($$0) + "/../lib/fpm/bundle/bundler/setup.rb"' >> $(FPM_BIN)
echo 'require "fpm"' >> $(FPM_BIN)
echo 'require "fpm/command"' >> $(FPM_BIN)
echo 'exit(FPM::Command.run || 0)' >> $(FPM_BIN)
chmod +x $(FPM_BIN)
.PHONY: install
install: $(CHECKOUT_DIR) $(BUNDLE_BIN) $(FPM_BIN)
mkdir --parents $(LIB_DIR)
cd $(CHECKOUT_DIR) && GEM_PATH=$(GEM_PATH) $(BUNDLE_CMD) install --without=development --standalone
cd $(CHECKOUT_DIR) && gem build fpm.gemspec
tar -xf $(CHECKOUT_DIR)/fpm*gem -C $(BUILD_DIR)
tar --touch -xf $(BUILD_DIR)/data.tar.gz -C $(LIB_DIR)
cp -r $(CHECKOUT_DIR)/bundle $(LIB_DIR)/bundle
.PHONY: package
package: install
$(FPM_BIN) -s dir -t deb -n fpm -d $(RUBY_PACKAGE) -v $(VERSION) -C $(BUILD_DIR) usr

View File

@ -1,18 +0,0 @@
# About
- build and install dependency on a ruby1.9 of some kind
- does not need root to package
- has its own GEM_DIR to keep its dependencies isolated
- installation does not install any gems in to your ruby environment
- installs in to standard locations /usr/{bin,lib}/fpm
- doesn't depend on having fpm installed for packaging to work
# Dependencies
- build-essential (perhaps more, but basically the standard packages you need
for deb packaging)
- ruby and ruby-dev
# Usage
- $ cd examples/fpm-with-system-ruby && make package

View File

@ -1,35 +0,0 @@
NAME=ruby
VERSION=1.9.2-p180
MAJOR_VERSION=1.9
ARCHITECTURE=x86
TARDIR=$(NAME)-$(VERSION)
TARBALL=$(TARDIR).tar.gz
DOWNLOAD=http://ftp.ruby-lang.org/pub/ruby/$(MAJOR_VERSION)/$(TARBALL)
PREFIX=/opt/fpm
PACKAGE_NAME=fpm
PACKAGE_VERSION=0.2.30
.PHONY: default
default: deb
package: deb
.PHONY: clean
clean:
rm -f $(NAME)-* $(NAME)_* |NAME| true
rm -fr $(TARDIR) || true
rm -fr $(PREFIX) || true
rm -f *.deb
$(TARBALL):
wget "$(DOWNLOAD)"
$(TARDIR): $(TARBALL)
tar -zxf $(TARBALL)
cd $(TARDIR); ./configure --enable-shared=false --prefix=$(PREFIX); make; make install
$(PREFIX)/bin/gem install fpm
.PHONY: deb
deb: $(TARDIR)
$(PREFIX)/bin/fpm -s dir -t deb -v $(PACKAGE_VERSION) -n $(PACKAGE_NAME) -a $(ARCHITECTURE) -C $(PREFIX) .

View File

@ -1,19 +0,0 @@
Notes:
You should have write permission on /opt directory
Dependencies:
$ sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libxml2-dev libxslt-dev autoconf libc6-dev
Usage:
$ make package
Should make the package. Try installing:
$ sudo dpkg -i fpm-0.2.30.x86.deb
Now try it:
$ /opt/fpm/bin/fpm --help

View File

@ -1,35 +0,0 @@
NAME=jruby
VERSION=1.6.1
DOWNLOAD=http://jruby.org.s3.amazonaws.com/downloads/$(VERSION)/$(TARBALL)
TARBALL=$(NAME)-bin-$(VERSION).tar.gz
TARDIR=$(NAME)-$(VERSION)
PREFIX=/opt/jruby
.PHONY: default
default: deb
package: deb
.PHONY: clean
clean:
rm -f $(NAME)-* $(NAME)_* || true
rm -fr $(TARDIR) || true
rm -f *.deb
rm -f *.rpm
$(TARBALL):
wget "$(DOWNLOAD)"
$(TARDIR): $(TARBALL)
tar -zxf $(TARBALL)
.PHONY: deb
deb: $(TARDIR)
fpm -s dir -t deb -v $(VERSION) -n $(NAME) -d "sun-java6-bin (>> 0)" \
-a all -d "sun-java6-jre (>> 0)" --prefix $(PREFIX) -C $(TARDIR) .
.PHONY: rpm
rpm: $(TARDIR)
fpm -s dir -t rpm -v $(VERSION) -n $(NAME) -d "jdk >= 1.6.0" \
-a all --prefix $(PREFIX) -C $(TARDIR) .

View File

@ -1,17 +0,0 @@
Usage:
make package
Should make the package. Try installing:
sudo dpkg -i jruby-1.6.0.RC2-1.all.deb
Now try it:
% /opt/jruby/bin/jirb
>> require "java"
=> true
>> java.lang.System.out.println("Hello")
Hello
=> nil

View File

@ -1,15 +0,0 @@
NAME=twisted
VERSION=10.2.0
twisted:
easy_install --editable --build-directory . "$(NAME)==$(VERSION)"
usr: twisted
cd twisted; python setup.py bdist
tar -zxf twisted/dist/Twisted-$(VERSION).linux-$(shell uname -m).tar.gz
package: usr
fpm -s dir -t deb -n $(NAME) -v $(VERSION) \
-p python-$(NAME)-VERSION_ARCH.deb -d "python" \
usr

View File

@ -22,13 +22,9 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 1.9.3'
# For parsing JSON (required for some Python support, etc)
# http://flori.github.com/json/doc/index.html
spec.add_dependency("json", ">= 1.7.7", "< 3.0") # license: Ruby License
# For logging
# https://github.com/jordansissel/ruby-cabin
spec.add_dependency("cabin", ">= 0.6.0") # license: Apache 2
spec.add_dependency("cabin", ">= 0.9.1") # license: Apache 2
# For backports to older rubies
# https://github.com/marcandre/backports
@ -39,24 +35,23 @@ Gem::Specification.new do |spec|
# For command-line flag support
# https://github.com/mdub/clamp/blob/master/README.markdown
spec.add_dependency("clamp", "~> 1.0.0") # license: MIT
spec.add_dependency("clamp", ">= 1.0.0") # license: MIT
# For sourcing from pleaserun
spec.add_dependency("pleaserun", "~> 0.0.29") # license: Apache 2
# For sourcing from git repos
spec.add_dependency("git", ">= 1.3.0", "< 2.0") # license: MIT
spec.add_dependency("stud")
# In Ruby 3.0, rexml was moved to a bundled gem instead of a default one,
# so I think this needs to be added explicitly?
spec.add_dependency("rexml")
spec.add_development_dependency("rspec", "~> 3.0.0") # license: MIT (according to wikipedia)
spec.add_development_dependency("rspec", "~> 3.13.0") # license: MIT (according to wikipedia)
spec.add_development_dependency("insist", "~> 1.0.0") # license: Apache 2
spec.add_development_dependency("pry")
spec.add_development_dependency("rake") # For FPM::RakeTask, #1877, #756
spec.files = files
spec.require_paths << "lib"
spec.bindir = "bin"

View File

@ -3,7 +3,6 @@ require "fpm/namespace"
require "fpm/version"
require "fpm/util"
require "clamp"
require "ostruct"
require "fpm"
require "tmpdir" # for Dir.tmpdir
@ -247,13 +246,24 @@ class FPM::Command < Clamp::Command
"See https://reproducible-builds.org/specs/source-date-epoch ",
:environment_variable => "SOURCE_DATE_EPOCH"
option "--fpm-options-file", "FPM_OPTIONS_FILE",
"A file that contains additional fpm options. Any fpm flag format is valid in this file. " \
"This can be useful on build servers where you want to use a common configuration or " \
"inject other parameters from a file instead of from a command-line flag.." do |path|
load_options(path)
end
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
# Keep a copy of the original flags (ones declared above, not by package types)
# This helps when generating the documentation
GENERAL_OPTIONS = @declared_options.clone
FPM::Package.types.each do |name, klass|
# This adds each package's flags to the main command
klass.apply_options(self)
end
@ -291,6 +301,15 @@ class FPM::Command < Clamp::Command
args << "."
end
if !File.exist?(workdir)
logger.fatal("Given --workdir=#{workdir} is not a path that exists.")
raise FPM::Package::InvalidArgument, "The given workdir '#{workdir}' does not exist."
end
if !File.directory?(workdir)
logger.fatal("Given --workdir=#{workdir} must be a directory")
raise FPM::Package::InvalidArgument, "The given workdir '#{workdir}' must be a directory."
end
logger.info("Setting workdir", :workdir => workdir)
ENV["TMP"] = workdir
@ -355,7 +374,7 @@ class FPM::Command < Clamp::Command
# If --inputs was specified, read it as a file.
if !inputs.nil?
if !File.exists?(inputs)
if !File.exist?(inputs)
logger.fatal("File given for --inputs does not exist (#{inputs})")
return 1
end
@ -370,7 +389,7 @@ class FPM::Command < Clamp::Command
# If --exclude-file was specified, read it as a file and append to
# the exclude pattern list.
if !exclude_file.nil?
if !File.exists?(exclude_file)
if !File.exist?(exclude_file)
logger.fatal("File given for --exclude-file does not exist (#{exclude_file})")
return 1
end
@ -394,7 +413,12 @@ class FPM::Command < Clamp::Command
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}")
# Not all options have a default value, so we assume `nil` if there's no default. (#1543)
# In clamp >= 1.3.0, options without `:default => ..` will not have any # `default_xyz`
# methods generated, so we need to check for the presence of this method first.
default = respond_to?("default_#{attribute}") ? send("default_#{attribute}") : nil
if object.send(attribute).nil? || send(attribute) != default
logger.info("Setting from flags: #{attribute}=#{send(attribute)}")
object.send("#{attribute}=", send(attribute))
end
@ -435,7 +459,7 @@ class FPM::Command < Clamp::Command
# Skip scripts not set
next if path.nil?
if !File.exists?(path)
if !File.exist?(path)
logger.error("No such file (for #{scriptname.to_s}): #{path.inspect}")
script_errors << path
end
@ -571,12 +595,86 @@ class FPM::Command < Clamp::Command
ARGV.unshift(*flags)
ARGV.push(*args)
super(run_args)
rescue FPM::Package::InvalidArgument => e
logger.error("Invalid package argument: #{e}")
return 1
end # def run
def load_options(path)
@loaded_files ||= []
if @loaded_files.include?(path)
#logger.error("Options file was already loaded once. Refusing to load a second time.", :path => path)
raise FPM::Package::InvalidArgument, "Options file already loaded once. Refusing to load a second time. Maybe a file tries to load itself? Path: #{path}"
end
if !File.exist?(path)
logger.fatal("Cannot load options from file because the file doesn't exist.", :path => path)
end
if !File.readable?(path)
logger.fatal("Cannot load options from file because the file isn't readable.", :path => path)
end
@loaded_files << path
logger.info("Loading flags from file", :path => path)
# Safety check, abort if the file is huge. Arbitrarily chosen limit is 100kb
stat = File.stat(path)
max = 100 * 1024
if stat.size > max
logger.fatal("Refusing to load options from file because the file seems pretty large.", :path => path, :size => stat.size)
raise FPM::Package::InvalidArgument, "Options file given to --fpm-options-file is seems too large. For safety, fpm is refusing to load this. Path: #{path} - Size: #{stat.size}, maximum allowed size #{max}."
end
File.read(path).split($/).each do |line|
logger.info("Processing flags from file", :path => path, :line => line)
# With apologies for this hack to mdub (Mike Williams, author of Clamp)...
# The following code will read a file and parse the file
# as flags as if they were in same argument position as the given --fpm-options-file option.
args = Shellwords.split(line)
while args.any?
arg = args.shift
# Lookup the Clamp option by its --flag-name or short name like -f
if arg =~ /^-/
# Single-letter options like -a or -z
if single_letter = arg.match(/^(-[A-Za-z0-9])(.*)$/)
option = self.class.find_option(single_letter.match(1))
arg, remainder = single_letter.match(1), single_letter.match(2)
if option.flag?
# Flags aka switches take no arguments, so we push the rest of the 'arg' entry back onto the args list
# For combined letter flags, like `-abc`, we want to consume the
# `-a` and then push `-bc` back to be processed.
# Only do this if there's more flags, like, not for `-a` but yes for `-abc`
args.unshift("-" + remainder) unless remainder.empty?
else
# Single letter options that take arguments, like `-ohello` same as `-o hello`
# For single letter flags with values, like `-ofilename` aka `-o filename`, push the remainder ("filename")
# back onto the args list so that it is consumed when we extract the flag value.
args.unshift(remainder) unless remainder.empty?
end
elsif arg.match(/^--/)
# Lookup the flag by its long --flag-name
option = self.class.find_option(arg)
end
end
# Extract the flag value, if any, from the remaining args list.
value = option.extract_value(arg, args)
# Process the flag into `self`
option.of(self).take(value)
end
end
end
# A simple flag validator
#
# The goal of this class is to ensure the flags and arguments given

View File

@ -3,7 +3,6 @@ require "fpm/util" # local
require "pathname" # stdlib
require "find"
require "tmpdir" # stdlib
require "ostruct"
require "backports/latest"
require "socket" # stdlib, for Socket.gethostname
require "shellwords" # stdlib, for Shellwords.escape
@ -331,11 +330,12 @@ class FPM::Package
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 = erbnew(template_code)
erb.filename = template_path
return erb
end # def template
#######################################
# The following methods are provided to
# easily override particular substitut-
@ -518,7 +518,7 @@ class FPM::Package
# flag), then apply it as an ERB template.
def script(script_name)
if attributes[:template_scripts?]
erb = ERB.new(scripts[script_name], nil, "-")
erb = erbnew(scripts[script_name])
# TODO(sissel): find the original file name for the file.
erb.filename = "script(#{script_name})"
return erb.result(binding)

View File

@ -112,6 +112,7 @@ class FPM::Package::CPAN < FPM::Package
self.vendor = case metadata["author"]
when String; metadata["author"]
when Array; metadata["author"].join(", ")
when NilClass; "No Vendor Or Author Provided"
else
raise FPM::InvalidPackageConfiguration, "Unexpected CPAN 'author' field type: #{metadata["author"].class}. This is a bug."
end if metadata.include?("author")
@ -304,7 +305,7 @@ class FPM::Package::CPAN < FPM::Package
directory = build_path("module")
::Dir.mkdir(directory)
args = [ "-C", directory, "-zxf", tarball,
"--strip-components", "1" ]
%q{--transform=s,[./]*[^/]*/,,} ]
safesystem("tar", *args)
return directory
end

View File

@ -27,18 +27,31 @@ class FPM::Package::Deb < FPM::Package
} unless defined?(SCRIPT_MAP)
# The list of supported compression types. Default is gz (gzip)
COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "zst", "none" ]
# https://www.debian.org/doc/debian-policy/ch-relationships.html#syntax-of-relationship-fields
# Example value with version relationship: libc6 (>= 2.2.1)
# Example value: libc6
# Package name docs here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-source
# Package names (both source and binary, see Package) must consist only of lower case letters (a-z),
# digits (0-9), plus (+) and minus (-) signs, and periods (.).
# They must be at least two characters long and must start with an alphanumeric character.
# Version string docs here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-version
# The format is: [epoch:]upstream_version[-debian_revision].
# epoch - This is a single (generally small) unsigned integer
# upstream_version - must contain only alphanumerics 6 and the characters . + - ~
# debian_revision - only alphanumerics and the characters + . ~
RELATIONSHIP_FIELD_PATTERN = /^(?<name>[A-z0-9_-]+)(?: *\((?<relation>[<>=]+) *(?<version>(?:[0-9]+:)?[0-9A-Za-z+~.-]+(?:-[0-9A-Za-z+~.]+)?)\))?$/
VERSION_FIELD_PATTERN = /
(?:(?:[0-9]+):)? # The epoch, an unsigned int
(?:[A-Za-z0-9+~.-]+) # upstream version, probably should not contain dashes?
(?:-[A-Za-z0-9+~.]+)? # debian_revision
/x # Version field pattern
RELATIONSHIP_FIELD_PATTERN = /^
(?<name>[A-z0-9][A-z0-9_.-]+)
(?:\s*\((?<relation>[<>=]+)\s(?<version>#{VERSION_FIELD_PATTERN})\))?
$/x # Relationship field pattern
option "--ignore-iteration-in-dependencies", :flag,
"For '=' (equal) dependencies, allow iterations on the specified " \
@ -60,12 +73,25 @@ class FPM::Package::Deb < FPM::Package
option "--compression", "COMPRESSION", "The compression type to use, must " \
"be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "gz" do |value|
if !COMPRESSION_TYPES.include?(value)
raise ArgumentError, "deb compression value of '#{value}' is invalid. " \
raise FPM::Package::InvalidArgument, "deb compression value of '#{value}' is invalid. " \
"Must be one of #{COMPRESSION_TYPES.join(", ")}"
end
value
end
option "--compression-level", "[0-9]", "Select a compression level. 0 is none or minimal. 9 is max compression.",
# Specify which compression level to use on the compressor backend, when building a package
:default => nil do |value|
valint = value.to_i
# if self.attributes[:deb_compression].nil?
# raise "Can't specify a compression level with compression disabled"
# end
unless value =~ /^\d$/ && valint >= 0 && valint <= 9
raise "Invalid compression level '#{value}'. Valid values are integers between 0 and 9 inclusive."
end
valint
end
option "--dist", "DIST-TAG", "Set the deb distribution.", :default => "unstable"
# Take care about the case when we want custom control file but still use fpm ...
@ -93,7 +119,7 @@ class FPM::Package::Deb < FPM::Package
end
option "--priority", "PRIORITY",
"The debian package 'priority' value.", :default => "extra"
"The debian package 'priority' value.", :default => "optional"
option "--use-file-permissions", :flag,
"Use existing file permissions when defining ownership and modes"
@ -197,6 +223,11 @@ class FPM::Package::Deb < FPM::Package
next File.expand_path(file)
end
option "--systemd-path", "FILEPATH", "Relative path to the systemd service directory",
:default => "lib/systemd/system" do |file|
next file.gsub(/^\/*/, '')
end
option "--systemd-enable", :flag , "Enable service on install or upgrade", :default => false
option "--systemd-auto-start", :flag , "Start service after install or upgrade", :default => false
@ -216,7 +247,7 @@ class FPM::Package::Deb < FPM::Package
def initialize(*args)
super(*args)
attributes[:deb_priority] = "extra"
attributes[:deb_priority] = "optional"
end # def initialize
private
@ -249,6 +280,9 @@ class FPM::Package::Deb < FPM::Package
when "noarch"
# Debian calls noarch "all"
@architecture = "all"
when "ppc64le"
# Debian calls ppc64le "ppc64el"
@architecture = "ppc64el"
end
return @architecture
end # def architecture
@ -287,6 +321,21 @@ class FPM::Package::Deb < FPM::Package
return (attributes[:prefix] or "/")
end # def prefix
def version
if @version.kind_of?(String)
if @version.start_with?("v") && @version.gsub(/^v/, "") =~ /^#{VERSION_FIELD_PATTERN}$/
logger.warn("Debian 'Version' field needs to start with a digit. I was provided '#{@version}' which seems like it just has a 'v' prefix to an otherwise-valid Debian version, I'll remove the 'v' for you.")
@version = @version.gsub(/^v/, "")
end
if @version !~ /^#{VERSION_FIELD_PATTERN}$/
raise FPM::InvalidPackageConfiguration, "The version looks invalid for Debian packages. Debian version field must contain only alphanumerics and . (period), + (plus), - (hyphen) or ~ (tilde). I have '#{@version}' which which isn't valid."
end
end
return @version
end
def input(input_path)
extract_info(input_path)
extract_files(input_path)
@ -304,6 +353,9 @@ class FPM::Package::Deb < FPM::Package
when "xz"
controltar = "control.tar.xz"
compression = "-J"
when "zst"
controltar = "control.tar.zst"
compression = "--use-compress-program 'zstd -d'"
when 'tar'
controltar = "control.tar"
compression = ""
@ -316,7 +368,7 @@ class FPM::Package::Deb < FPM::Package
build_path("control").tap do |path|
FileUtils.mkdir(path) if !File.directory?(path)
# unpack the control.tar.{,gz,bz2,xz} from the deb package into staging_path
# unpack the control.tar.{,gz,bz2,xz,zst} from the deb package into staging_path
# Unpack the control tarball
safesystem(ar_cmd[0] + " p #{package} #{controltar} | tar #{compression} -xf - -C #{path}")
@ -336,7 +388,7 @@ class FPM::Package::Deb < FPM::Package
version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
m = version_re.match(parse.call("Version"))
if !m
raise "Unsupported version string '#{parse.call("Version")}'"
raise FPM::InvalidPackageConfiguration, "Unsupported version string '#{parse.call("Version")}'"
end
self.epoch, self.version, self.iteration = m.captures
@ -426,6 +478,9 @@ class FPM::Package::Deb < FPM::Package
when "xz"
datatar = "data.tar.xz"
compression = "-J"
when "zst"
datatar = "data.tar.zst"
compression = "--use-compress-program 'zstd -d'"
when 'tar'
datatar = "data.tar"
compression = ""
@ -476,27 +531,36 @@ class FPM::Package::Deb < FPM::Package
end
if attributes[:source_date_epoch] == "0"
logger.error("Alas, ruby's Zlib::GzipWriter does not support setting an mtime of zero. Aborting.")
raise "#{name}: source_date_epoch of 0 not supported."
raise FPM::InvalidPackageConfiguration, "#{name}: source_date_epoch of 0 not supported."
end
if not attributes[:source_date_epoch].nil? and not ar_cmd_deterministic?
logger.error("Alas, could not find an ar that can handle -D option. Try installing recent gnu binutils. Aborting.")
raise "#{name}: ar is insufficient to support source_date_epoch."
raise FPM::InvalidPackageConfiguration, "#{name}: ar is insufficient to support source_date_epoch."
end
if not attributes[:source_date_epoch].nil? and not tar_cmd_supports_sort_names_and_set_mtime?
logger.error("Alas, could not find a tar that can set mtime and sort. Try installing recent gnu tar. Aborting.")
raise "#{name}: tar is insufficient to support source_date_epoch."
raise FPM::InvalidPackageConfiguration, "#{name}: tar is insufficient to support source_date_epoch."
end
attributes[:deb_systemd] = []
attributes.fetch(:deb_systemd_list, []).each do |systemd|
name = File.basename(systemd, ".service")
dest_systemd = staging_path("lib/systemd/system/#{name}.service")
name = File.basename(systemd)
extname = File.extname(name)
name_with_extension = if extname.empty?
"#{name}.service"
elsif [".service", ".timer"].include?(extname)
name
else
raise FPM::InvalidPackageConfiguration, "Invalid systemd unit file extension: #{extname}. Expected .service or .timer, or no extension."
end
dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name_with_extension}"))
mkdir_p(File.dirname(dest_systemd))
FileUtils.cp(systemd, dest_systemd)
File.chmod(0644, dest_systemd)
# add systemd service name to attribute
attributes[:deb_systemd] << name
attributes[:deb_systemd] << name_with_extension
end
if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?
@ -563,7 +627,7 @@ class FPM::Package::Deb < FPM::Package
end # No need to close, GzipWriter#close will close it.
end
if File.exists?(dest_changelog) and not File.exists?(dest_upstream_changelog)
if File.exist?(dest_changelog) and not File.exist?(dest_upstream_changelog)
# see https://www.debian.org/doc/debian-policy/ch-docs.html#s-changelogs
File.rename(dest_changelog, dest_upstream_changelog)
end
@ -599,8 +663,12 @@ class FPM::Package::Deb < FPM::Package
end
attributes.fetch(:deb_systemd_list, []).each do |systemd|
name = File.basename(systemd, ".service")
dest_systemd = staging_path("lib/systemd/system/#{name}.service")
name = File.basename(systemd)
extname = File.extname(systemd)
name_with_extension = extname.empty? ? "#{name}.service" : name
dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name_with_extension}"))
mkdir_p(File.dirname(dest_systemd))
FileUtils.cp(systemd, dest_systemd)
File.chmod(0644, dest_systemd)
@ -613,31 +681,40 @@ class FPM::Package::Deb < FPM::Package
when "gz", nil
datatar = build_path("data.tar.gz")
controltar = build_path("control.tar.gz")
compression = "-z"
compression_flags = ["-z"]
# gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
compressor_options = {"GZIP" => "-#{self.attributes[:deb_compression_level] || 9}" +
"#{'n' if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?}"}
when "bzip2"
datatar = build_path("data.tar.bz2")
controltar = build_path("control.tar.gz")
compression = "-j"
compression_flags = ["-j"]
compressor_options = {"BZIP" => "-#{self.attributes[:deb_compression_level] || 9}"}
when "xz"
datatar = build_path("data.tar.xz")
controltar = build_path("control.tar.xz")
compression = "-J"
compression_flags = ["-J"]
compressor_options = {"XZ_OPT" => "-#{self.attributes[:deb_compression_level] || 3}"}
when "zst"
datatar = build_path("data.tar.zst")
controltar = build_path("control.tar.zst")
compression_flags = ["--use-compress-program", "zstd"]
compressor_options = {"ZSTD_CLEVEL" => "-#{self.attributes[:deb_compression_level] || 3}"}
when "none"
datatar = build_path("data.tar")
controltar = build_path("control.tar")
compression = ""
compression_flags = []
compressor_options = {}
else
raise FPM::InvalidPackageConfiguration,
"Unknown compression type '#{self.attributes[:deb_compression]}'"
end
args = [ tar_cmd, "-C", staging_path, compression ] + data_tar_flags + [ "-cf", datatar, "." ]
args = [ tar_cmd, "-C", staging_path ] + compression_flags + data_tar_flags + [ "-cf", datatar, "." ]
if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
# Use gnu tar options to force deterministic file order and timestamp
args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])]
# gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
args.unshift({"GZIP" => "-9n"})
end
args.unshift(compressor_options)
safesystem(*args)
# pack up the .deb, which is just an 'ar' archive with 3 files
@ -677,9 +754,17 @@ class FPM::Package::Deb < FPM::Package
self.dependencies = self.dependencies.collect do |dep|
fix_dependency(dep)
end.flatten
# If an invalid depends field was found i.e. /bin.sh then fix_depends will blank it
# Make sure we remove this blank here
self.dependencies = self.dependencies.reject { |p| p.empty? }
self.provides = self.provides.collect do |provides|
fix_provides(provides)
end.flatten
# If an invalid provides field was found i.e. mypackage(arch) then fix_provides will blank it
# Make sure we remove this blank here
self.provides = self.provides.reject { |p| p.empty? }
if origin == FPM::Package::CPAN
# The fpm cpan code presents dependencies and provides fields as perl(ModuleName)
@ -694,7 +779,7 @@ class FPM::Package::Deb < FPM::Package
else
# Also replace '::' in the perl module name with '-'
modulename = m["name"].gsub("::", "-")
# Fix any upper-casing or other naming concerns Debian has about packages
name = "#{attributes[:cpan_package_name_prefix]}-#{modulename}"
@ -720,7 +805,7 @@ class FPM::Package::Deb < FPM::Package
if origin == FPM::Package::Deb
changelog_path = staging_path("usr/share/doc/#{name}/changelog.Debian.gz")
if File.exists?(changelog_path)
if File.exist?(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|
@ -734,7 +819,7 @@ class FPM::Package::Deb < FPM::Package
if origin == FPM::Package::Deb
changelog_path = staging_path("usr/share/doc/#{name}/changelog.gz")
if File.exists?(changelog_path)
if File.exist?(changelog_path)
logger.debug("Found an upstream changelog file, using it.", :path => changelog_path)
attributes[:deb_upstream_changelog] = build_path("deb_upstream_changelog")
File.open(attributes[:deb_upstream_changelog], "w") do |deb_upstream_changelog|
@ -780,6 +865,18 @@ class FPM::Package::Deb < FPM::Package
end
end
if dep.start_with?("/")
logger.warn("Blanking 'dependency' field '#{dep}' because it's invalid")
dep = ""
return dep
end
if dep.include?("rpmlib")
logger.warn("Blanking 'dependency' field '#{dep}' because it's invalid")
dep = ""
return dep
end
name_re = /^[^ \(]+/
name = dep[name_re]
if name =~ /[A-Z]/
@ -873,6 +970,11 @@ class FPM::Package::Deb < FPM::Package
provides = provides.gsub("_", "-")
end
if provides.include?("(") and !provides.include?("(=")
logger.warn("Blanking 'provides' field '#{provides}' because it's invalid")
provides = ""
end
if m = provides.match(/^([A-Za-z0-9_-]+)\s*=\s*(\d+.*$)/)
logger.warn("Replacing 'provides' entry #{provides} with syntax 'name (= version)'")
provides = "#{m[1]} (= #{m[2]})"
@ -906,13 +1008,22 @@ class FPM::Package::Deb < FPM::Package
case self.attributes[:deb_compression]
when "gz", "bzip2", nil
controltar = "control.tar.gz"
compression = "-z"
compression_flags = ["-z"]
# gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
compressor_options = {"GZIP" => "-#{self.attributes[:deb_compression_level] || 9}" +
"#{'n' if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?}"}
when "xz"
controltar = "control.tar.xz"
compression = "-J"
compression_flags = ["-J"]
compressor_options = {"XZ_OPT" => "-#{self.attributes[:deb_compression_level] || 3}"}
when "zst"
controltar = "control.tar.zst"
compression_flags = ["--use-compress-program", "zstd"]
compressor_options = {"ZSTD_CLEVEL" => "-#{self.attributes[:deb_compression_level] || 3}"}
when "none"
controltar = "control.tar"
compression = ""
compression_flags = []
compressor_options = {}
else
raise FPM::InvalidPackageConfiguration,
"Unknown compression type '#{self.attributes[:deb_compression]}'"
@ -922,14 +1033,13 @@ class FPM::Package::Deb < FPM::Package
build_path(controltar).tap do |controltar|
logger.info("Creating", :path => controltar, :from => control_path)
args = [ tar_cmd, "-C", control_path, compression, "-cf", controltar,
args = [ tar_cmd, "-C", control_path ] + compression_flags + [ "-cf", controltar,
"--owner=0", "--group=0", "--numeric-owner", "." ]
if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
# Force deterministic file order and timestamp
args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])]
# gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
args.unshift({"GZIP" => "-9n"})
end
args.unshift(compressor_options)
safesystem(*args)
end
@ -1011,7 +1121,7 @@ class FPM::Package::Deb < FPM::Package
etcfiles = []
# Add everything in /etc
begin
if !attributes[:deb_no_default_config_files?] && File.exists?(staging_path("/etc"))
if !attributes[:deb_no_default_config_files?] && File.exist?(staging_path("/etc"))
logger.warn("Debian packaging tools generally labels all files in /etc as config files, " \
"as mandated by policy, so fpm defaults to this behavior for deb packages. " \
"You can disable this default behavior with --deb-no-default-config-files flag")
@ -1041,6 +1151,7 @@ class FPM::Package::Deb < FPM::Package
logger.debug("Adding config file #{path} to Staging area #{staging_path}")
FileUtils.mkdir_p(File.dirname(dcl))
FileUtils.cp_r path, dcl
add_path(path, allconfigs)
else
logger.debug("Config file aready exists in staging area.")
end
@ -1185,5 +1296,5 @@ class FPM::Package::Deb < FPM::Package
return data_tar_flags
end # def data_tar_flags
public(:input, :output, :architecture, :name, :prefix, :converted_from, :to_s, :data_tar_flags)
public(:input, :output, :architecture, :name, :prefix, :version, :converted_from, :to_s, :data_tar_flags)
end # class FPM::Target::Deb

View File

@ -43,7 +43,7 @@ class FPM::Package::Dir < FPM::Package
# This mapping should work the same way 'rsync -a' does
# Meaning 'rsync -a source dest'
# and 'source=dest' in fpm work the same as the above rsync
if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
if path =~ /.=./ && !File.exist?(chdir == '.' ? path : File.join(chdir, path))
origin, destination = path.split("=", 2)
if File.directory?(origin) && origin[-1,1] == "/"
@ -197,10 +197,6 @@ class FPM::Package::Dir < FPM::Package
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

View File

@ -16,6 +16,10 @@ class FPM::Package::FreeBSD < FPM::Package
"Sets the FreeBSD 'origin' pkg field",
:default => "fpm/<name>"
option "--osversion", "VERSION",
"Sets the FreeBSD 'version' pkg field, ie 12 or 13, use '*' for all.",
:default => "13"
def output(output_path)
output_check(output_path)
@ -90,28 +94,36 @@ class FPM::Package::FreeBSD < FPM::Package
end # def output
# Handle architecture naming conversion:
# <osname>:<osversion>:<arch>:<wordsize>[.other]
# <osname>:<osversion>:<arch>
def architecture
osname = %x{uname -s}.chomp
osversion = %x{uname -r}.chomp.split('.').first
osname = 'FreeBSD'
# Essentially because no testing on other platforms
arch = 'x86'
wordsize = case @architecture
arch = case @architecture
when nil, 'native'
%x{getconf LONG_BIT}.chomp # 'native' is current arch
when 'arm64'
'64'
'arm64'
when 'aarch64'
'arm64'
when 'amd64'
'64'
'amd64'
when 'x86_64'
'amd64'
when 'i386'
'32'
'i386'
when 'i686'
'i386'
when 'any'
'*'
when 'all'
'*'
when 'noarch'
'*'
else
%x{getconf LONG_BIT}.chomp # default to native, the current arch
end
return [osname, osversion, arch, wordsize].join(':')
return [osname, attributes[:freebsd_osversion], arch].join(':')
end
def add_path(tar, tar_path, path)

View File

@ -104,19 +104,18 @@ class FPM::Package::Gem < FPM::Package
FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
if attributes[:gem_git_repo]
require "git"
logger.debug("Git cloning in directory #{download_dir}")
g = Git.clone(attributes[:gem_git_repo],gem_name,:path => download_dir)
safesystem("git", "-C", download_dir, "clone", attributes[:gem_git_repo], ".")
if attributes[:gem_git_branch]
g.branch(attributes[:gem_git_branch]).checkout
g.pull('origin',attributes[:gem_git_branch])
safesystem("git", "-C", download_dir, "checkout", attributes[:gem_git_branch])
end
gem_build = [ "#{attributes[:gem_gem]}", "build", "#{g.dir.to_s}/#{gem_name}.gemspec"]
::Dir.chdir(g.dir.to_s) do |dir|
gem_build = [ "#{attributes[:gem_gem]}", "build", "#{download_dir}/#{gem_name}.gemspec"]
::Dir.chdir(download_dir) do |dir|
logger.debug("Building in directory #{dir}")
safesystem(*gem_build)
end
gem_files = ::Dir.glob(File.join(g.dir.to_s, "*.gem"))
gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
else
gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
@ -135,9 +134,19 @@ class FPM::Package::Gem < FPM::Package
return gem_files.first
end # def download
GEMSPEC_YAML_CLASSES = [ ::Gem::Specification, ::Gem::Version, Time, ::Gem::Dependency, ::Gem::Requirement, Symbol ]
def load_package_info(gem_path)
spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
# TODO(sissel): Maybe we should check if `safe_load` method exists instead of this version check?
if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new("3.1.0")
# Ruby 3.1.0 switched to a Psych/YAML version that defaults to "safe" loading
# and unfortunately `gem specification --yaml` emits YAML that requires
# class loaders to process correctly
spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml},
:permitted_classes => GEMSPEC_YAML_CLASSES)
else
# Older versions of ruby call this method YAML.safe_load
spec = YAML.safe_load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml}, GEMSPEC_YAML_CLASSES)
end
if !attributes[:gem_package_prefix].nil?
attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]

View File

@ -4,7 +4,6 @@ require "fileutils"
require "fpm/package/dir"
require 'tempfile' # stdlib
require 'pathname' # stdlib
require 'rexml/document' # stdlib
# Use an OS X pkg built with pkgbuild.
#
@ -103,6 +102,7 @@ class FPM::Package::OSXpkg < FPM::Package
# Extract name and version from PackageInfo XML
def extract_info(package)
require 'rexml/document'
build_path("expand").tap do |path|
doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
pkginfo_elem = doc.elements["pkg-info"]
@ -148,6 +148,11 @@ class FPM::Package::OSXpkg < FPM::Package
write_scripts
args += ["--scripts", scripts_path]
end
if attributes[:prefix]
args += ["--install-location", attributes[:prefix]]
end
args << output_path
safesystem("pkgbuild", *args)

View File

@ -37,11 +37,15 @@ class FPM::Package::Pacman < FPM::Package
def architecture
case @architecture
when nil
return %x{uname -m}.chomp # default to current arch
when "amd64" # debian and pacman disagree on architecture names
return "x86_64"
return %x{uname -m}.chomp # default to current arch
when "amd64" # Debian uses amd64
return "x86_64" # Arch Linux uses x86_64
when "arm64" # Debian uses arm64
return "aarch64" # Arch Linux ARM uses aarch64
when "armhf" # Debian uses armhf
return "arm7hf" # Arch Linux ARM uses arm7hf
when "native"
return %x{uname -m}.chomp # 'native' is current arch
return %x{uname -m}.chomp # 'native' is the current arch
when "all", "any", "noarch"
return "any"
else
@ -234,7 +238,7 @@ class FPM::Package::Pacman < FPM::Package
return ""
when "gz"
return ".gz"
when "zx"
when "xz"
return ".xz"
when "bzip2"
return ".bz2"

View File

@ -18,6 +18,7 @@ class FPM::Package::PleaseRun < FPM::Package
option "--name", "SERVICE_NAME", "The name of the service you are creating"
option "--chdir", "CHDIR", "The working directory used by the service"
option "--user", "USER", "The user to use for executing this program."
private
def input(command)

View File

@ -60,7 +60,7 @@ class FPM::Package::Puppet < FPM::Package
end # case name
end # self.scripts.each
if File.exists?(params[:output])
if File.exist?(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)")

View File

@ -112,3 +112,4 @@ class get_metadata(Command):
else:
# For Python 2.5 and Debian's python-json
output.write(json.write(data))
output.close()

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
#import pkg_resources
import packaging.requirements
import json
import sys
# Expect requirements lines via stdin.
#requirements = pkg_resources.parse_requirements(sys.stdin)
# Process environment markers, if any, and produce a list of requirements for the current environment.
def evaluate_requirements(fd):
all_requirements = [packaging.requirements.Requirement(line) for line in sys.stdin]
for req in all_requirements:
# XXX: Note: marker.evaluate() can be given a dict() containing environment values to overwrite
if req.marker is None or req.marker.evaluate():
if len(req.specifier) > 0:
for spec in req.specifier:
yield "%s%s" % (req.name, spec)
else:
yield str(req.name)
print(json.dumps(list(evaluate_requirements(sys.stdin))))

View File

@ -51,76 +51,371 @@ class FPM::Package::Python < FPM::Package
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 " \
option "--install-bin", "BIN_PATH", "(DEPRECATED, does nothing) The path to where python scripts " \
"should be installed to." do
logger.warn("Using deprecated flag --install-bin")
end
option "--install-lib", "LIB_PATH", "(DEPRECATED, does nothing) 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 " \
"print get_python_lib()'" do
logger.warn("Using deprecated flag --install-bin")
end
option "--install-data", "DATA_PATH", "(DEPRECATED, does nothing) 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" \
"DATA_PATH" do
logger.warn("Using deprecated flag --install-bin")
end
option "--dependencies", :flag, "Include requirements defined by the python package" \
" as dependencies.", :default => true
option "--obey-requirements-txt", :flag, "Use a requirements.txt file " \
"in the top-level directory of the python package for dependency " \
"detection.", :default => false
option "--scripts-executable", "PYTHON_EXECUTABLE", "Set custom python " \
option "--scripts-executable", "PYTHON_EXECUTABLE", "(DEPRECATED) Set custom python " \
"interpreter in installing scripts. By default distutils will replace " \
"python interpreter in installing scripts (specified by shebang) with " \
"current python interpreter (sys.executable). This option is equivalent " \
"to appending 'build_scripts --executable PYTHON_EXECUTABLE' arguments " \
"to 'setup.py install' command."
"to 'setup.py install' command." do
logger.warn("Using deprecated flag --install-bin")
end
option "--disable-dependency", "python_package_name",
"The python package name to remove from dependency list",
:multivalued => true, :attribute_name => :python_disable_dependency,
:default => []
option "--setup-py-arguments", "setup_py_argument",
"Arbitrary argument(s) to be passed to setup.py",
"(DEPRECATED) Arbitrary argument(s) to be passed to setup.py",
:multivalued => true, :attribute_name => :python_setup_py_arguments,
:default => []
:default => [] do
logger.warn("Using deprecated flag --install-bin")
end
option "--internal-pip", :flag,
"Use the pip module within python to install modules - aka 'python -m pip'. This is the recommended usage since Python 3.4 (2014) instead of invoking the 'pip' script",
:attribute_name => :python_internal_pip,
:default => true
class PythonMetadata
require "strscan"
private
class MissingField < StandardError; end
class UnexpectedContent < StandardError; end
# According to https://packaging.python.org/en/latest/specifications/core-metadata/
# > Core Metadata v2.4 - August 2024
MULTIPLE_USE = %w(Dynamic Platform Supported-Platform License-File Classifier Requires-Dist Requires-External Project-URL Provides-Extra Provides-Dist Obsoletes-Dist)
# METADATA files are described in Python Packaging "Core Metadata"[1] and appear to have roughly RFC822 syntax.
# [1] https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata
def self.parse(input)
s = StringScanner.new(input)
headers = {}
# Default "Multiple use" fields to empty array instead of nil.
MULTIPLE_USE.each do |field|
headers[field] = []
end
while !s.eos? and !s.scan("\n") do
# Field is non-space up, but excluding the colon
field = s.scan(/[^\s:]+/)
# Skip colon and following whitespace
s.scan(/:\s*/)
# Value is text until newline, and any following lines if they have leading spaces.
value = s.scan(/[^\n]+(?:\Z|\n(?:[ \t][^\n]+\n)*)/)
if value.nil?
raise "Failed parsing Python package metadata value at field #{field}, char offset #{s.pos}"
end
value = value.chomp
if MULTIPLE_USE.include?(field)
raise "Header field should be an array. This is a bug in fpm." if !headers[field].is_a?(Array)
headers[field] << value
else
headers[field] = value
end
end # while reading headers
# If there's more content beyond the last header, then it's a content body.
# In Python Metadata >= 2.1, the descriptino can be written in the body.
if !s.eos?
if headers["Metadata-Version"].to_f >= 2.1
# Per Python core-metadata spec:
# > Changed in version 2.1: This field may be specified in the message body instead.
#return PythonMetadata.new(headers, s.string[s.pos ...])
return headers, s.string[s.pos ... ]
elsif headers["Metadata-Version"].to_f >= 2.0
# dnspython v1.15.0 has a description body and Metadata-Version 2.0
# this seems out of spec, but let's accept it anyway.
return headers, s.string[s.pos ... ]
else
raise "After reading METADATA headers, extra data is in the file but was not expected. This may be a bug in fpm."
end
end
#return PythonMetadata.new(headers)
return headers, nil # nil means no body in this metadata
rescue => e
puts "String scan failed: #{e}"
puts "Position: #{s.pointer}"
puts "---"
puts input
puts "==="
puts input[s.pointer...]
puts "---"
raise e
end # self.parse
def self.from(input)
return PythonMetadata.new(*parse(input))
end
# Only focusing on terms fpm may care about
attr_reader :name, :version, :summary, :description, :keywords, :maintainer, :license, :requires, :homepage
FIELD_MAP = {
:@name => "Name",
:@version => "Version",
:@summary => "Summary",
:@description => "Description",
:@keywords => "Keywords",
:@maintainer => "Author-email",
# Note: License can also come from the deprecated "License" field
# This is processed later in this method.
:@license => "License-Expression",
:@requires => "Requires-Dist",
}
REQUIRED_FIELDS = [ "Metadata-Version", "Name", "Version" ]
# headers - a Hash containing field-value pairs from headers as read from a python METADATA file.
# body - optional, a string containing the body text of a METADATA file
def initialize(headers, body=nil)
REQUIRED_FIELDS.each do |field|
if !headers.include?(field)
raise MissingField, "Missing required Python metadata field, '#{field}'. This might be a bug in the package or in fpm."
end
end
FIELD_MAP.each do |attr, field|
if headers.include?(field)
instance_variable_set(attr, headers.fetch(field))
end
end
# Do any extra processing on fields to turn them into their expected content.
process_description(headers, body)
process_license(headers)
process_homepage(headers)
process_maintainer(headers)
end # def initialize
private
def process_description(headers, body)
if @description
# Per python core-metadata spec:
# > To support empty lines and lines with indentation with respect to the
# > RFC 822 format, any CRLF character has to be suffixed by 7 spaces
# > followed by a pipe (“|”) char. As a result, the Description field is
# > encoded into a folded field that can be interpreted by RFC822 parser [2].
@description = @description.gsub!(/^ |/, "")
end
if !body.nil?
if headers["Metadata-Version"].to_f >= 2.1
# Per Python core-metadata spec:
# > Changed in version 2.1: [Description] field may be specified in the message body instead.
#
# The description is simply the rest of the METADATA file after the headers.
@description = body
elsif headers["Metadata-Version"].to_f >= 2.0
# dnspython v1.15.0 has a description body and Metadata-Version 2.0
# this seems out of spec, but let's accept it anyway.
@description = body
else
raise UnexpectedContent, "Found a content body in METADATA file, but Metadata-Version(#{headers["Metadata-Version"]}) is below 2.1 and doesn't support this. This may be a bug in fpm or a malformed python package."
end
# What to do if we find a description body but already have a Description field set in the headers?
if headers.include?("Description")
raise "Found a description in the body of the python package metadata, but the package already set the Description field. I don't know what to do. This is probably a bug in fpm."
end
end
# XXX: The description field can be markdown, plain text, or reST.
# Content type is noted in the "Description-Content-Type" field
# Should we transform this to plain text?
end # process_description
def process_license(headers)
# Ignore the "License" field if License-Expression is also present.
return if headers["Metadata-Version"].to_f >= 2.4 && headers.include?("License-Expression")
# Deprecated field, License, as described in python core-metadata:
# > As of Metadata 2.4, License and License-Expression are mutually exclusive.
# > If both are specified, tools which parse metadata will disregard License
# > and PyPI will reject uploads. See PEP 639.
if headers["License"]
# Note: This license can be free form text, so it's unclear if it's a great choice.
# however, the original python metadata License field is quite old/deprecated
# so maybe nobody uses it anymore?
@license = headers["License"]
elsif license_classifier = headers["Classifier"].find { |value| value =~ /^License ::/ }
# The license could also show up in the "Classifier" header with "License ::" as a prefix.
@license = license_classifier.sub(/^License ::/, "")
end # check for deprecated License field
end # process_license
def process_homepage(headers)
return if headers["Project-URL"].empty?
# Create a hash of Project-URL where the label is the key, url the value.
urls = Hash[*headers["Project-URL"].map do |text|
label, url = text.split(/, */, 2)
# Normalize the label by removing punctuation and spaces
# Reference: https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization
# > In plain language: a label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result to lowercase.
label = label.gsub(/[[:punct:][:space:]]/, "").downcase
[label, url]
end.flatten(1)]
# Prioritize certain URL labels when choosing the homepage url.
[ "homepage", "source", "documentation", "releasenotes" ].each do |label|
if urls.include?(label)
@homepage = urls[label]
end
end
# Otherwise, default to the first URL
@homepage = urls.values.first
end
def process_maintainer(headers)
# Python metadata supports both "Author-email" and "Maintainer-email"
# Of the "Maintainer" fields, python core-metadata says:
# > Note that this field is intended for use when a project is being maintained by someone other than the original author
#
# So we should prefer Maintainer-email if it exists, but fall back to Author-email otherwise.
@maintainer = headers["Maintainer-email"] unless headers["Maintainer-email"].nil?
end
end # class PythonMetadata
# Input a package.
#
# The 'package' can be any of:
#
# * A name of a package on pypi (ie; easy_install some-package)
# * The path to a directory containing setup.py
# * The path to a setup.py
# * The path to a directory containing setup.py or pyproject.toml
# * The path to a setup.py or pyproject.toml
# * The path to a python sdist file ending in .tar.gz
# * The path to a python wheel file ending in .whl
def input(package)
explore_environment
path_to_package = download_if_necessary(package, version)
# Expect a setup.py or pyproject.toml if it's a directory.
if File.directory?(path_to_package)
setup_py = File.join(path_to_package, "setup.py")
else
setup_py = path_to_package
if !(File.exist?(File.join(path_to_package, "setup.py")) or File.exist?(File.join(path_to_package, "pyproject.toml")))
raise FPM::InvalidPackageConfiguration, "The path ('#{path_to_package}') doesn't appear to be a python package directory. I expected either a pyproject.toml or setup.py but found neither."
end
end
if !File.exist?(setup_py)
logger.error("Could not find 'setup.py'", :path => setup_py)
raise "Unable to find python package; tried #{setup_py}"
if File.file?(path_to_package)
if ["setup.py", "pyproject.toml"].include?(File.basename(path_to_package))
path_to_package = File.dirname(path_to_package)
end
end
load_package_info(setup_py)
install_to_staging(setup_py)
if [".tar.gz", ".tgz"].any? { |suffix| path_to_package.end_with?(suffix) }
# Have pip convert the .tar.gz (source dist?) into a wheel
logger.debug("Found tarball and assuming it's a python source package.")
safesystem(*attributes[:python_pip], "wheel", "--no-deps", "-w", build_path, path_to_package)
path_to_package = ::Dir.glob(build_path("*.whl")).first
if path_to_package.nil?
raise FPM::InvalidPackageConfiguration, "Failed building python package format - fpm tried to build a python wheel, but didn't find the .whl file. This might be a bug in fpm."
end
elsif File.directory?(path_to_package)
logger.debug("Found directory and assuming it's a python source package.")
safesystem(*attributes[:python_pip], "wheel", "--no-deps", "-w", build_path, path_to_package)
if attributes[:python_obey_requirements_txt?]
reqtxt = File.join(path_to_package, "requirements.txt")
@requirements_txt = File.read(reqtxt).split("\n") if File.file?(reqtxt)
end
path_to_package = ::Dir.glob(build_path("*.whl")).first
if path_to_package.nil?
raise FPM::InvalidPackageConfiguration, "Failed building python package format - fpm tried to build a python wheel, but didn't find the .whl file. This might be a bug in fpm."
end
end
load_package_info(path_to_package)
install_to_staging(path_to_package)
end # def input
def explore_environment
if !attributes[:python_bin_given?]
# If --python-bin isn't set, try to find a good default python executable path, because it might not be "python"
pythons = [ "python", "python3", "python2" ]
default_python = pythons.find { |py| program_exists?(py) }
if default_python.nil?
raise FPM::Util::ExecutableNotFound, "Could not find any python interpreter. Tried the following: #{pythons.join(", ")}"
end
logger.info("Setting default python executable", :name => default_python)
attributes[:python_bin] = default_python
if !attributes[:python_package_name_prefix_given?]
attributes[:python_package_name_prefix] = default_python
logger.info("Setting package name prefix", :name => default_python)
end
end
if attributes[:python_internal_pip?]
# XXX: Should we detect if internal pip is available?
attributes[:python_pip] = [ attributes[:python_bin], "-m", "pip"]
end
end # explore_environment
# 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.exist?(path) and File.basename(path) == "setup.py")
return path
if File.exist?(path)
return path if File.directory?(path)
basename = File.basename(path)
return File.dirname(path) if basename == "pyproject.toml"
return File.dirname(path) if basename == "setup.py"
return path if path.end_with?(".tar.gz")
return path if path.end_with?(".tgz") # amqplib v1.0.2 does this
return path if path.end_with?(".whl")
return path if path.end_with?(".zip")
return path if File.exist?(File.join(path, "setup.py"))
return path if File.exist?(File.join(path, "pyproject.toml"))
raise [
"Local file doesn't appear to be a supported type for a python package. Expected one of:",
" - A directory containing setup.py or pyproject.toml",
" - A file ending in .tar.gz (a python source dist)",
" - A file ending in .whl (a python wheel)",
].join("\n")
end
logger.info("Trying to download", :package => package)
@ -134,24 +429,16 @@ class FPM::Package::Python < FPM::Package
target = build_path(package)
FileUtils.mkdir(target) unless File.directory?(target)
if attributes[:python_internal_pip?]
# XXX: Should we detect if internal pip is available?
attributes[:python_pip] = [ attributes[:python_bin], "-m", "pip"]
end
# attributes[:python_pip] -- expected to be a path
if attributes[:python_pip]
logger.debug("using pip", :pip => attributes[:python_pip])
# TODO: Support older versions of pip
pip = [attributes[:python_pip]] if pip.is_a?(String)
setup_cmd = [
*attributes[:python_pip],
"download",
"--no-clean",
"--no-deps",
"--no-binary", ":all:",
"-d", build_path,
"-d", target,
"-i", attributes[:python_pypi],
]
@ -162,117 +449,109 @@ class FPM::Package::Python < FPM::Package
]
end
setup_cmd += [
"--build",
target,
want_pkg,
]
setup_cmd << want_pkg
safesystem(*setup_cmd)
files = ::Dir.entries(target).filter { |entry| entry =~ /\.(whl|tgz|tar\.gz|zip)$/ }
if files.length != 1
raise "Unexpected directory layout after `pip download ...`. This might be an fpm bug? The directory contains these files: #{files.inspect}"
end
return File.join(target, files.first)
else
# 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)
# easy_install will put stuff in @tmpdir/packagename/, so find that:
# @tmpdir/somepackage/setup.py
#dirs = ::Dir.glob(File.join(target, "*"))
files = ::Dir.entries(target).filter { |entry| entry != "." && entry != ".." }
if dirs.length != 1
raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{build_path}"
end
return dirs.first
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}"
end
return dirs.first
end # def download
# Load the package information like name, version, dependencies.
def load_package_info(setup_py)
if !attributes[:python_package_prefix].nil?
attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
end
begin
json_test_code = [
"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
# in the same directory.
setup_dir = File.dirname(setup_py)
output = ::Dir.chdir(setup_dir) do
tmp = build_path("metadata.json")
setup_cmd = "env PYTHONPATH=#{pylib}:$PYTHONPATH #{attributes[:python_bin]} " \
"setup.py --command-packages=pyfpm get_metadata --output=#{tmp}"
if attributes[:python_obey_requirements_txt?]
setup_cmd += " --load-requirements-txt"
def load_package_info(path)
if path.end_with?(".whl")
# XXX: Maybe use rubyzip to parse the .whl (zip) file instead?
metadata = nil
execmd(["unzip", "-p", path, "*.dist-info/METADATA"], :stdin => false, :stderr => false) do |stdout|
metadata = PythonMetadata.from(stdout.read(64<<10))
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"
wheeldata = nil
execmd(["unzip", "-p", path, "*.dist-info/WHEEL"], :stdin => false, :stderr => false) do |stdout|
wheeldata, _ = PythonMetadata.parse(stdout.read(64<<10))
end
File.read(tmp)
else
raise "Unexpected python package path. This might be an fpm bug? The path is #{path}"
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"]
self.url = metadata["url"]
self.architecture = wheeldata["Root-Is-Purelib"] == "true" ? "all" : "native"
self.description = metadata.description unless metadata.description.nil?
self.license = metadata.license unless metadata.license.nil?
self.version = metadata.version
self.url = metadata.homepage unless metadata.homepage.nil?
self.name = metadata.name
# name prefixing is optional, if enabled, a name 'foo' will become
# 'python-foo' (depending on what the python_package_name_prefix is)
if attributes[:python_fix_name?]
self.name = fix_name(metadata["name"])
else
self.name = metadata["name"]
end
self.name = fix_name(self.name) if attributes[:python_fix_name?]
# convert python-Foo to python-foo if flag is set
self.name = self.name.downcase if attributes[:python_downcase_name?]
self.maintainer = metadata.maintainer
if !attributes[:no_auto_depends?] and attributes[:python_dependencies?]
metadata["dependencies"].each do |dep|
dep_re = /^([^<>!= ]+)\s*(?:([~<>!=]{1,2})\s*(.*))?$/
# Python Dependency specifiers are a somewhat complex format described here:
# https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers
#
# We can ask python's packaging module to parse and evaluate these.
# XXX: Allow users to override environnment values.
#
# Example:
# Requires-Dist: tzdata; sys_platform = win32
# Requires-Dist: asgiref>=3.8.1
dep_re = /^([^<>!= ]+)\s*(?:([~<>!=]{1,2})\s*(.*))?$/
reqs = []
# --python-obey-requirements-txt should use requirements.txt
# (if found in the python package) and replace the requirments listed from the metadata
if attributes[:python_obey_requirements_txt?] && !@requirements_txt.nil?
requires = @requirements_txt
else
requires = metadata.requires
end
# Evaluate python package requirements and only show ones matching the current environment
# (Environment markers, etc)
# Additionally, 'extra' features such as a requirement named `django[bcrypt]` isn't quite supported yet,
# since the marker.evaluate() needs to be passed some environment like { "extra": "bcrypt" }
execmd([attributes[:python_bin], File.expand_path(File.join("pyfpm", "parse_requires.py"), File.dirname(__FILE__))]) do |stdin, stdout, stderr|
requires.each { |r| stdin.puts(r) }
stdin.close
data = stdout.read
logger.pipe(stderr => :warn)
reqs += JSON.parse(data)
end
reqs.each do |dep|
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
next if attributes[:python_disable_dependency].include?(name)
@ -291,8 +570,12 @@ class FPM::Package::Python < FPM::Package
# convert dependencies from python-Foo to python-foo
name = name.downcase if attributes[:python_downcase_dependencies?]
self.dependencies << "#{name} #{cmp} #{version}"
end
if cmp.nil? && version.nil?
self.dependencies << "#{name}"
else
self.dependencies << "#{name} #{cmp} #{version}"
end
end # parse Requires-Dist dependencies
end # if attributes[:python_dependencies?]
end # def load_package_info
@ -312,55 +595,17 @@ class FPM::Package::Python < FPM::Package
end # def fix_name
# Install this package to the staging directory
def install_to_staging(setup_py)
project_dir = File.dirname(setup_py)
def install_to_staging(path)
prefix = "/"
prefix = attributes[:prefix] unless attributes[:prefix].nil?
# Some setup.py's assume $PWD == current directory of setup.py, so let's
# chdir first.
::Dir.chdir(project_dir) do
flags = [ "--root", staging_path ]
if !attributes[:python_install_lib].nil?
flags += [ "--install-lib", File.join(prefix, attributes[:python_install_lib]) ]
elsif !attributes[:prefix].nil?
# setup.py install --prefix PREFIX still installs libs to
# PREFIX/lib64/python2.7/site-packages/
# but we really want something saner.
#
# since prefix is given, but not python_install_lib, assume PREFIX/lib
flags += [ "--install-lib", File.join(prefix, "lib") ]
end
# XXX: Note: pip doesn't seem to have any equivalent to `--install-lib` or similar flags.
# XXX: Deprecate :python_install_data, :python_install_lib, :python_install_bin
# XXX: Deprecate: :python_setup_py_arguments
flags = [ "--root", staging_path ]
flags += [ "--prefix", prefix ] if !attributes[:prefix].nil?
if !attributes[:python_install_data].nil?
flags += [ "--install-data", File.join(prefix, attributes[:python_install_data]) ]
elsif !attributes[:prefix].nil?
# prefix given, but not python_install_data, assume PREFIX/data
flags += [ "--install-data", File.join(prefix, "data") ]
end
if !attributes[:python_install_bin].nil?
flags += [ "--install-scripts", File.join(prefix, attributes[:python_install_bin]) ]
elsif !attributes[:prefix].nil?
# prefix given, but not python_install_bin, assume PREFIX/bin
flags += [ "--install-scripts", File.join(prefix, "bin") ]
end
if !attributes[:python_scripts_executable].nil?
# Overwrite installed python scripts shebang binary with provided executable
flags += [ "build_scripts", "--executable", attributes[:python_scripts_executable] ]
end
if !attributes[:python_setup_py_arguments].nil? and !attributes[:python_setup_py_arguments].empty?
# Add optional setup.py arguments
attributes[:python_setup_py_arguments].each do |a|
flags += [ a ]
end
end
safesystem(attributes[:python_bin], "setup.py", "install", *flags)
end
safesystem(*attributes[:python_pip], "install", "--no-deps", *flags, path)
end # def install_to_staging
public(:input)

View File

@ -156,7 +156,7 @@ class FPM::Package::RPM < FPM::Package
option "--macro-expansion", :flag,
"install-time macro expansion in %pre %post %preun %postun scripts " \
"(see: https://rpm.org/user_doc/scriptlet_expansion.html)", :default => false
"(see: https://rpm-software-management.github.io/rpm/manual/scriptlet_expansion.html)", :default => false
option "--verifyscript", "FILE",
"a script to be run on verification" do |val|
@ -175,7 +175,7 @@ class FPM::Package::RPM < FPM::Package
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|
"See: https://rpm-software-management.github.io/rpm/manual/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
@ -185,6 +185,9 @@ class FPM::Package::RPM < FPM::Package
end
end
option "--old-perl-dependency-name", :flag,
"Use older 'perl' depdency name. Newer Red Hat (and derivatives) use a dependency named 'perl-interpreter'."
private
# Fix path name
@ -192,17 +195,30 @@ class FPM::Package::RPM < FPM::Package
# Replace * with [*] to make rpm not use globs
# Replace ? with [?] to make rpm not use globs
# Replace % with [%] to make rpm not expand macros
# Replace whitespace with ? to make rpm not split the filename
# If and only if any of the above are done, then also replace ' with \', " with \", and \ with \\\\
# to accommodate escape and quote processing that rpm will perform in that case (but not otherwise)
def rpm_fix_name(name)
name = name.gsub(/(\ |\[|\]|\*|\?|\%|\$|')/, {
' ' => '?',
'%' => '[%]',
'$' => '[$]',
'?' => '[?]',
'*' => '[*]',
'[' => '[\[]',
']' => '[\]]',
"'" => "\\'",
})
if name.match?(/[ \t*?%${}\[\]]/)
name = name.gsub(/(\ |\t|\[|\]|\*|\?|\%|\$|'|"|\{|\}|\\)/, {
' ' => '?',
"\t" => '?',
'%' => '[%]',
'$' => '[$]',
'?' => '[?]',
'*' => '[*]',
'[' => '[\[]',
']' => '[\]]',
#'{' => '[\{]',
#'}' => '[\}]',
'{' => '?',
'}' => '?',
'"' => '\\"',
"'" => "\\'",
'\\' => '\\\\\\\\',
})
end
name
end
def rpm_file_entry(file)
@ -266,9 +282,41 @@ class FPM::Package::RPM < FPM::Package
return @iteration ? @iteration : 1
end # def iteration
# Generate a generic changelog or return an existing definition
def changelog
if attributes[:rpm_changelog]
return attributes[:rpm_changelog]
end
reldate = if attributes[:source_date_epoch].nil?
Time.now()
else
Time.at(attributes[:source_date_epoch].to_i)
end
changed = reldate.strftime("%a %b %_e %Y")
changev = "#{version}-#{iteration}"
changev += "%{?dist}" if attributes[:rpm_dist]
"* #{changed} #{maintainer} - #{changev}\n- Package created with FPM\n"
end
# See FPM::Package#converted_from
def converted_from(origin)
if origin == FPM::Package::Gem
if origin == FPM::Package::CPAN
if !attributes[:rpm_old_perl_dependency_name?]
fixed_deps = []
self.dependencies.collect do |dep|
# RPM package "perl" is a metapackage which install all the Perl bits and core modules, then gcc...
# this must be replaced by perl-interpreter
if name=/^perl([\s<>=].*)$/.match(dep)
fixed_deps.push("perl-interpreter#{name[1]}")
else
fixed_deps.push(dep)
end
end
self.dependencies = fixed_deps
end
elsif origin == FPM::Package::Gem
fixed_deps = []
self.dependencies.collect do |dep|
# Gem dependency operator "~>" is not compatible with rpm. Translate any found.
@ -464,6 +512,7 @@ class FPM::Package::RPM < FPM::Package
args += ["--define", "dist .#{attributes[:rpm_dist]}"] if attributes[:rpm_dist]
args += [
"--buildroot", "#{build_path}/BUILD",
"--define", "buildroot #{build_path}/BUILD",
"--define", "_topdir #{build_path}",
"--define", "_sourcedir #{build_path}",

View File

@ -36,7 +36,7 @@ class FPM::Package::Virtualenv < FPM::Package
:default => nil
option "--setup-install", :flag, "After building virtualenv run setup.py install "\
"useful when building a virtualenv for packages and including their requirements from "
"useful when building a virtualenv for packages and including their requirements from "\
"requirements.txt"
option "--system-site-packages", :flag, "Give the virtual environment access to the "\
@ -158,6 +158,31 @@ class FPM::Package::Virtualenv < FPM::Package
end
end
# [2025-09-30] virtualenv-tools seems broken?
# The --update-path will look for a VIRTUAL_ENV= line in bin/activate,
# however, the version I tested looks for it with quotations, like VIRTUAL_ENV='
# And at time of writing, my `virtualenv` tool doesn't use quotations on this variable
#
# Maybe best case we can patch it here instead. The path update tool
# looks for the original virtualenv path and I think updates any bin
# files which point to it.
patched = []
activate_bin = File.join(virtualenv_build_folder, "bin/activate")
fd = File.open(activate_bin)
fd.each_line do |line|
re = /^VIRTUAL_ENV=([^'"].*)$/
match = line.match(re)
if match
# Quote the VIRTUAL_ENV var assignment to help virtualenv-tools work?
patched << "VIRTUAL_ENV='#{match}'\n"
else
patched << line
end
end
fd.close
File.write(activate_bin, patched.join)
# Rewrite the base path inside the virtualenv to prepare it to be packaged.
::Dir.chdir(virtualenv_build_folder) do
safesystem("virtualenv-tools", "--update-path", virtualenv_folder)
end
@ -191,7 +216,6 @@ class FPM::Package::Virtualenv < FPM::Package
dir.input(".")
@staging_path = dir.staging_path
dir.cleanup_build
end # def input
# Delete python precompiled files found in a given folder.

View File

@ -1,13 +1,39 @@
require "fpm/namespace"
require "ostruct"
require "rake"
require "rake/tasklib"
class FPM::RakeTask < Rake::TaskLib
class Options
attr_accessor :args
def initialize(defaults=nil)
if defaults.nil?
@h = Hash.new
else
@h = defaults
end
end
def method_missing(m, *args)
if m.end_with?("=")
raise ArgumentError, "#{self.class.name}##{m} ... Expected 1 arg, got #{args.length}" if args.length != 1
@h[m[0...-1]] = args[0]
else
raise ArgumentError, "Expected 0 arg, got #{args.length}" if args.length != 0
return @h[m]
end
end
def to_h
return @h
end
end # Options
attr_reader :options
def initialize(package_name, opts = {}, &block)
@options = OpenStruct.new(:name => package_name.to_s)
#@options = OpenStruct.new(:name => package_name.to_s)
@options = Options.new(:name => package_name.to_s)
@source, @target = opts.values_at(:source, :target).map(&:to_s)
@directory = File.expand_path(opts[:directory].to_s)
@ -18,8 +44,8 @@ class FPM::RakeTask < Rake::TaskLib
task(options.name) do |_, task_args|
block.call(*[options, task_args].first(block.arity)) if block_given?
abort("Must specify args") unless options.respond_to?(:args)
@args = options.delete_field(:args)
abort("Must specify args") if options.args.nil?
@args = options.args
run_cli
end
end

View File

@ -1,5 +1,6 @@
require "fpm/namespace"
require "fileutils"
require "stud/temporary"
# Some utility functions
module FPM::Util
@ -135,7 +136,7 @@ module FPM::Util
raise ExecutableNotFound.new(program)
end
logger.debug("Running command", :args => args2)
logger.info("Running command", :args => args2)
stdout_r, stdout_w = IO.pipe
stderr_r, stderr_w = IO.pipe
@ -154,10 +155,13 @@ module FPM::Util
yield(*args3)
stdin_w_close if opts[:stdin] and not stdin_w.closed?
stdin_w.close if opts[:stdin] and not stdin_w.closed?
stdout_r.close unless stdout_r.closed?
stderr_r.close unless stderr_r.closed?
else
# If no block given (not interactive) we should close stdin_w because we
# won't be able to give input which may cause a hang.
stdin_w.close
# 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)
@ -226,15 +230,18 @@ module FPM::Util
@@ar_cmd_deterministic = false
# FIXME: don't assume current directory writeable
FileUtils.touch(["fpm-dummy.tmp"])
emptyfile = Stud::Temporary.pathname
testarchive = Stud::Temporary.pathname
FileUtils.touch([emptyfile])
["ar", "gar"].each do |ar|
["-qc", "-qcD"].each do |ar_create_opts|
FileUtils.rm_f(["fpm-dummy.ar.tmp"])
FileUtils.rm_f([testarchive])
# Return this combination if it creates archives without uids or timestamps.
# Exitstatus will be nonzero if the archive can't be created,
# or its table of contents doesn't match the regular expression.
# Be extra-careful about locale and timezone when matching output.
system("#{ar} #{ar_create_opts} fpm-dummy.ar.tmp fpm-dummy.tmp 2>/dev/null && env TZ=UTC LANG=C LC_TIME=C #{ar} -tv fpm-dummy.ar.tmp | grep '0/0.*1970' > /dev/null 2>&1")
system("#{ar} #{ar_create_opts} #{testarchive} #{emptyfile} 2>/dev/null && env TZ=UTC LANG=C LC_TIME=C #{ar} -tv #{testarchive} | grep '0/0.*1970' > /dev/null 2>&1")
if $?.exitstatus == 0
@@ar_cmd = [ar, ar_create_opts]
@@ar_cmd_deterministic = true
@ -244,10 +251,8 @@ module FPM::Util
end
# If no combination of ar and options omits timestamps, fall back to default.
@@ar_cmd = ["ar", "-qc"]
FileUtils.rm_f([testarchive, emptyfile])
return @@ar_cmd
ensure
# Clean up
FileUtils.rm_f(["fpm-dummy.ar.tmp", "fpm-dummy.tmp"])
end # def ar_cmd
# Return whether the command returned by ar_cmd can create deterministic archives
@ -261,7 +266,10 @@ module FPM::Util
return @@tar_cmd if defined? @@tar_cmd
# FIXME: don't assume current directory writeable
FileUtils.touch(["fpm-dummy.tmp"])
emptyfile = Stud::Temporary.pathname
testarchive = Stud::Temporary.pathname
FileUtils.touch([emptyfile])
# Prefer tar that supports more of the features we want, stop if we find tar of our dreams
best="tar"
@ -273,7 +281,7 @@ module FPM::Util
opts=[]
score=0
["--sort=name", "--mtime=@0"].each do |opt|
system("#{tar} #{opt} -cf fpm-dummy.tar.tmp fpm-dummy.tmp > /dev/null 2>&1")
system("#{tar} #{opt} -cf #{testarchive} #{emptyfile} > /dev/null 2>&1")
if $?.exitstatus == 0
opts << opt
score += 1
@ -289,10 +297,9 @@ module FPM::Util
end
end
@@tar_cmd = best
FileUtils.rm_f([testarchive, emptyfile])
return @@tar_cmd
ensure
# Clean up
FileUtils.rm_f(["fpm-dummy.tar.tmp", "fpm-dummy.tmp"])
end # def tar_cmd
# Return whether the command returned by tar_cmd can create deterministic archives
@ -325,7 +332,15 @@ module FPM::Util
def copy_entry(src, dst, preserve=false, remove_destination=false)
case File.ftype(src)
st = File.lstat(src)
filetype = if st.ftype == "file" && st.nlink > 1
"hardlink"
else
st.ftype
end
case filetype
when 'fifo'
if File.respond_to?(:mkfifo)
File.mkfifo(dst)
@ -342,19 +357,24 @@ module FPM::Util
when 'characterSpecial', 'blockSpecial'
raise UnsupportedSpecialFile.new("File is device which fpm doesn't know how to copy (#{File.ftype(src)}): #{src}")
when 'directory'
FileUtils.mkdir(dst) unless File.exists? dst
else
# if the file with the same dev and inode has been copied already -
FileUtils.mkdir(dst) unless File.exist? dst
when 'hardlink'
# Handle hardlinks
# if the file with the same dev and inode has been copied already.
# hard link it's copy to `dst`, otherwise make an actual copy
st = File.lstat(src)
known_entry = copied_entries[[st.dev, st.ino]]
if known_entry
FileUtils.ln(known_entry, dst)
logger.debug("Copying hardlink", :src => src, :dst => dst, :link => known_entry)
else
FileUtils.copy_entry(src, dst, preserve, false,
remove_destination)
copied_entries[[st.dev, st.ino]] = dst
end
else
# Normal file, just copy it.
FileUtils.copy_entry(src, dst, preserve, false,
remove_destination)
end # else...
end # def copy_entry
@ -406,6 +426,33 @@ module FPM::Util
def logger
@logger ||= Cabin::Channel.get
end # def logger
def erbnew(template_code)
# In Ruby 2.6(?), Ruby changed how ERB::new is invoked.
# First, it added keyword args like `ERB.new(..., trim_mode: "-")`
# Later, it deprecated then removed the safe_level feature.
# As of Ruby 3.1, warnings are printed at runtime when ERB.new is called with the old syntax.
# Ruby 2.5 and older does not support the ERB.new keyword args.
#
# My tests showed:
# * Ruby 2.3.0 through 3.0 work correctly with the old syntax.
# * Ruby 3.1.0 and newer (at time of writing, Ruby 3.2) require the new syntax
# Therefore, in order to support the most versions of ruby, we need to do a version check
# to invoke ERB.new correctly and without printed warnings.
# References: https://github.com/jordansissel/fpm/issues/1894
# Honestly, I'm not sure if Gem::Version is correct to use in this situation, but it works.
# on older versions of Ruby, RUBY_VERSION is a frozen string, and
# Gem::Version.new calls String#strip! which throws an exception.
# so we have to call String#dup to get an unfrozen copy.
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("3.1.0")
# Ruby 3.0.x and older
return ERB.new(template_code, nil, "-")
else
# Ruby 3.1.0 and newer
return ERB.new(template_code, trim_mode: "-")
end
end
end # module FPM::Util
require 'fpm/util/tar_writer'

View File

@ -1,3 +1,3 @@
module FPM
VERSION = "1.14.1"
VERSION = "1.17.0"
end

View File

@ -1,72 +0,0 @@
#!/bin/sh
if [ ! -f "mk/bsd.pkg.mk" ] ; then
# TODO(sissel): Maybe download pkgsrc ourselves.
echo "Current directory doesn't appear to be a pkgsrc tree. ($PWD)"
echo "I was expecting to find file: ./mk/bsd.pkg.mk"
exit 1
fi
if [ ! -f "build/usr/local/bin/bmake" ] ; then
# TODO(sissel): Maybe bootstrap ourselves.
echo "This script requires pkgsrc to be bootstrapped in a specific way."
echo "I expected to find file: build/usr/local/bin/bmake and did not"
echo
echo "Bootstrap with:"
echo "SH=/bin/bash ./bootstrap/bootstrap --unprivileged --prefix $PWD/build/usr/local --pkgdbdir $PWD/pkgdb"
exit 1
fi
# TODO(sissel): put some flags.
LOCALBASE="/usr/local"
DESTDIR=$PWD/build
mkdir -p "$DESTDIR"
export PATH=$DESTDIR/$LOCALBASE/bin:$DESTDIR/$LOCALBASE/sbin:$PATH
for i in "$@" ; do
# process dependencies first before the final target.
set -- $(bmake -C "$@" show-depends-pkgpaths) "$@"
done
TARGETS="$*"
for target in $TARGETS; do
set --
eval "$(bmake -C $target show-vars-eval VARS="PKGNAME PKGVERSION")"
name="$(echo "$PKGNAME" | sed -e "s/-$PKGVERSION\$//")"
orig_version=${PKGVERSION}
version=${PKGVERSION}-pkgsrc
# Purge old package
rm packages/All/$PKGNAME.tgz
pkg_delete $name > /dev/null 2>&1
bmake -C $target clean || exit 1
bmake -C $target USE_DESTDIR=yes LOCALBASE=$LOCALBASE PREFIX=$LOCALBASE \
DESTDIR=$DESTDIR SKIP_DEPENDS=yes \
clean package || exit 1
# Start building fpm args
set -- -n "$name" -v "$version" --prefix $LOCALBASE
# Skip the pkgsrc package metadata files
set -- "$@" --exclude '+*'
# Handle deps
for dep in $(bmake -C $target show-depends-pkgpaths) ; do
eval "$(bmake -C $dep show-vars-eval VARS="PKGNAME PKGVERSION")"
PKGNAME="$(echo "$PKGNAME" | sed -e "s/-$PKGVERSION\$//")"
set -- "$@" -d "$PKGNAME (= $PKGVERSION-pkgsrc)"
done
set -- -s tar -t deb "$@"
set -- "$@" packages/All/$name-$orig_version.tgz
fpm "$@"
done

View File

@ -0,0 +1,4 @@
[project]
name = "example"
version = "1.2.3"
authors = [ { name = "Captain Fancy", email = "foo@example.com" } ]

99
spec/fixtures/python/METADATA vendored Normal file
View File

@ -0,0 +1,99 @@
Metadata-Version: 2.4
Name: Django
Version: 5.2.6
Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design.
Author-email: Django Software Foundation <foundation@djangoproject.com>
License: BSD-3-Clause
Project-URL: Homepage, https://www.djangoproject.com/
Project-URL: Documentation, https://docs.djangoproject.com/
Project-URL: Release notes, https://docs.djangoproject.com/en/stable/releases/
Project-URL: Funding, https://www.djangoproject.com/fundraising/
Project-URL: Source, https://github.com/django/django
Project-URL: Tracker, https://code.djangoproject.com/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
License-File: LICENSE
License-File: LICENSE.python
License-File: AUTHORS
Requires-Dist: asgiref>=3.8.1
Requires-Dist: sqlparse>=0.3.1
Requires-Dist: tzdata; sys_platform == "win32"
Provides-Extra: argon2
Requires-Dist: argon2-cffi>=19.1.0; extra == "argon2"
Provides-Extra: bcrypt
Requires-Dist: bcrypt; extra == "bcrypt"
Dynamic: license-file
======
Django
======
Django is a high-level Python web framework that encourages rapid development
and clean, pragmatic design. Thanks for checking it out.
All documentation is in the "``docs``" directory and online at
https://docs.djangoproject.com/en/stable/. If you're just getting started,
here's how we recommend you read the docs:
* First, read ``docs/intro/install.txt`` for instructions on installing Django.
* Next, work through the tutorials in order (``docs/intro/tutorial01.txt``,
``docs/intro/tutorial02.txt``, etc.).
* If you want to set up an actual deployment server, read
``docs/howto/deployment/index.txt`` for instructions.
* You'll probably want to read through the topical guides (in ``docs/topics``)
next; from there you can jump to the HOWTOs (in ``docs/howto``) for specific
problems, and check out the reference (``docs/ref``) for gory details.
* See ``docs/README`` for instructions on building an HTML version of the docs.
Docs are updated rigorously. If you find any problems in the docs, or think
they should be clarified in any way, please take 30 seconds to fill out a
ticket here: https://code.djangoproject.com/newticket
To get more help:
* Join the ``#django`` channel on ``irc.libera.chat``. Lots of helpful people
hang out there. `Webchat is available <https://web.libera.chat/#django>`_.
* Join the `Django Discord community <https://chat.djangoproject.com>`_.
* Join the community on the `Django Forum <https://forum.djangoproject.com/>`_.
To contribute to Django:
* Check out https://docs.djangoproject.com/en/dev/internals/contributing/ for
information about getting involved.
To run Django's test suite:
* Follow the instructions in the "Unit tests" section of
``docs/internals/contributing/writing-code/unit-tests.txt``, published online at
https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/#running-the-unit-tests
Supporting the Development of Django
====================================
Django's development depends on your contributions.
If you depend on Django, remember to support the Django Software Foundation: https://www.djangoproject.com/fundraising/

View File

@ -10,9 +10,6 @@ setup(name="Example",
package_dir={},
install_requires=[
"Dependency1", "dependency2",
# XXX: I don't know what these python_version-dependent deps mean
# needs investigation
# Reference: PEP-0508
'rtxt-dep3; python_version == "2.0"',
'rtxt-dep4; python_version > "2.0"',
],

View File

@ -3,6 +3,7 @@ require "stud/temporary"
require "fpm" # local
require "fpm/command" # local
require "fixtures/mockpackage"
require "shellwords"
describe FPM::Command do
describe "--prefix"
@ -25,6 +26,27 @@ describe FPM::Command do
describe "--directories"
describe "-a | --architecture"
describe "--workdir" do
subject { FPM::Command.new("fpm") }
context "with an nonexistent path" do
it "should fail aka return nonzero exit" do
args = ["--workdir", "/this/path/should/not/exist", "-s", "rpm", "-t", "empty", "-n", "example"]
insist { subject.run(args) } != 0
end
end
context "with a path that is not a directory" do
it "should fail aka return nonzero exit" do
Stud::Temporary.file do |file|
args = ["--workdir", file.path, "-s", "rpm", "-t", "empty", "-n", "example"]
insist { subject.run(args) } != 0
end
end
end
end
describe "-v | --version" do
subject { FPM::Command.new("fpm") }
@ -119,4 +141,132 @@ describe FPM::Command do
end
end
end
describe "--fpm-options-file" do
let(:path) { Stud::Temporary.pathname }
subject = FPM::Command.new("fpm")
after do
File.unlink(path) if File.exist?(path)
end
context "when considering option order" do
before do
File.write(path, [
"--name file-value",
].join($/))
end
it "should process options file content in-place" do
cli = [ "--name", "cli-value" ]
fileopt = [ "--fpm-options-file", path ]
subject.parse(cli + fileopt)
insist { subject.name } == "file-value"
subject = FPM::Command.new("fpm")
subject.parse(fileopt + cli)
insist { subject.name } == "cli-value"
end
end
context "with multiple flags on a single line" do
before do
File.write(path, [
"--version 123 --name fancy",
].join($/))
end
it "should work" do
subject.parse(["--fpm-options-file", path])
insist { subject.name } == "fancy"
insist { subject.version } == "123"
end
end
context "with multiple single-letter flags on a single line" do
subject { FPM::Command.new("fpm") }
context "separately" do
before do
File.write(path, [
"-f -f -f -f"
].join($/))
end
it "should work" do
subject.parse(["--fpm-options-file", path])
end
end
context "together" do
before do
File.write(path, [
"-ffff"
].join($/))
end
it "should work" do
subject.parse(["--fpm-options-file", path])
end
end
end
context "when a file refers to itself" do
subject { FPM::Command.new("fpm") }
before do
File.write(path, [
"--version 1.0",
"--fpm-options-file #{Shellwords.shellescape(path)}",
"--iteration 1",
].join($/))
end
it "should raise an exception" do
insist { subject.parse([ "--fpm-options-file", Shellwords.shellescape(path) ]) }.raises FPM::Package::InvalidArgument
end
end
context "when using an nonexistent file" do
it "should raise an exception" do
# At this point, 'path' file hasn't been created, so we should be safe to assume it doesn't exist.
insist { subject.parse([ "--fpm-options-file", path ]) }.raises Errno::ENOENT
end
end
context "when using an unreadable file (no permission to read)" do
it "should raise an exception" do
File.write(path, "")
File.chmod(000, path)
# At this point, 'path' file hasn't been created, so we should be safe to assume it doesn't exist.
insist { subject.parse([ "--fpm-options-file", path ]) }.raises Errno::EACCES
end
end
context "when reading a large file" do
let(:fd) { File.new(path, "w") }
before do
# Write more than 100kb
max = 105 * 1024
data = "\n" * 4096
bytes = 0
bytes += fd.syswrite(data) while bytes < max
fd.flush
end
after do
fd.close
end
it "should raise an exception" do
insist { subject.parse([ "--fpm-options-file", path ]) }.raises FPM::Package::InvalidArgument
end
end
end
end

View File

@ -1,4 +1,5 @@
require "spec_setup"
require "tmpdir" # for Dir.mktmpdir
require "fpm" # local
require "fpm/package/cpan" # local
@ -37,6 +38,22 @@ describe FPM::Package::CPAN do
# TODO(sissel): Check dependencies
end
it "should unpack tarball containing ./ leading paths" do
pending("Disabled on travis-ci because it always fails, and there is no way to debug it?") if is_travis
Dir.mktmpdir do |tmpdir|
# Create tarball containing a file './foo/bar.txt'
system("mkdir -p #{tmpdir}/z/foo")
system("touch #{tmpdir}/z/foo/bar.txt")
system("tar -C #{tmpdir} -cvzf #{tmpdir}/z.tar.gz .")
# Invoke the unpack method
directory = subject.instance_eval { unpack("#{tmpdir}/z.tar.gz") }
insist { File.file?("#{directory}/foo/bar.txt") } == true
end
end
it "should package File::Spec" do
pending("Disabled on travis-ci because it always fails, and there is no way to debug it?") if is_travis
subject.input("File::Spec")
@ -45,6 +62,16 @@ describe FPM::Package::CPAN do
insist { subject.name } == "perl-PathTools"
end
it "should package Class::Data::Inheritable" do
pending("Disabled on travis-ci because it always fails, and there is no way to debug it?") if is_travis
# Class::Data::Inheritable version 0.08 has a blank author field in its
# META.yml file.
subject.instance_variable_set(:@version, "0.08");
subject.input("Class::Data::Inheritable")
insist { subject.vendor } == "No Vendor Or Author Provided"
end
context "given a distribution without a META.* file" do
it "should package IPC::Session" do
pending("Disabled on travis-ci because it always fails, and there is no way to debug it?") if is_travis

View File

@ -53,7 +53,13 @@ describe FPM::Package::Deb do
it "should default to native" do
# Convert kernel name to debian name
expected = native == "x86_64" ? "amd64" : native
expected = if native == "x86_64"
"amd64"
elsif native == "aarch64"
"arm64"
else
native
end
expect(subject.architecture).to(be == expected)
end
end
@ -71,8 +77,8 @@ describe FPM::Package::Deb do
end
describe "priority" do
it "should default to 'extra'" do
expect(subject.attributes[:deb_priority]).to(be == "extra")
it "should default to 'optional'" do
expect(subject.attributes[:deb_priority]).to(be == "optional")
end
end
@ -126,6 +132,31 @@ describe FPM::Package::Deb do
end
end
context "when validating the version field" do
[ "_", "1_2", "abc def", "%", "1^a"].each do |v|
it "should reject as invalid, '#{v}'" do
subject.version = v
insist { subject.version }.raises FPM::InvalidPackageConfiguration
end
end
[ "1", "1.2", "1.2.3", "20200101", "1~beta", "1whatever"].each do |v|
it "should accept '#{v}'" do
subject.version = v
# should not raise exception
insist { subject.version } == v
end
it "should remove a leading 'v' from v#{v} and still accept it" do
subject.version = "v#{v}"
# should not raise exception
insist { subject.version } == v
end
end
end
describe "#output" do
let(:original) { FPM::Package::Deb.new }
let(:input) { FPM::Package::Deb.new }
@ -382,7 +413,7 @@ describe FPM::Package::Deb do
context "when run against lintian" do
before do
skip("Missing lintian program") unless have_lintian
skip("Missing lintian program") unless have_lintian
end
lintian_errors_to_ignore = [
@ -504,7 +535,8 @@ describe FPM::Package::Deb do
{
"bzip2" => "bz2",
"xz" => "xz",
"gz" => "gz"
"gz" => "gz",
"zst" => "zst"
}.each do |flag,suffix|
context "when --deb-compression is #{flag}" do
let(:target) { Stud::Temporary.pathname + ".deb" }
@ -543,7 +575,13 @@ describe FPM::Package::Deb do
# for broken symlinks. Since this package has no files, this check
# should always succeed. It would fail if fpm generated any invalid
# packages, such as ones with a bzip2-compressed control.tar file (#1840)
insist { system("lintian", "-C", "symlinks", target) } == true
#
# Note: At some point, Debian renamed the "symlinks" check to "files/symbolic-links/broken"
# In order to support both newer and older Debian derivatives, the test suite will try both checks,
# and if both fail, we should know something is wrong with the package.
insist {
system("lintian", "-C", "symlinks", target) || system("lintian", "-C", "files/symbolic-links/broken", target)
} == true
end
end
end

View File

@ -2,6 +2,7 @@ require "spec_setup"
require "fpm" # local
require "fpm/package/dir" # local
require "stud/temporary"
require "insist/assert"
if RUBY_VERSION =~ /^1\.8/
# The following method copied from ruby 1.9.3
@ -126,6 +127,49 @@ describe FPM::Package::Dir do
subject.output(output)
insist { File }.exist?(File.join(output, "foo", "a", "a=b"))
end
it "should create two normal files when one normal file is copied to two different locations" do
# For issue #2102
# With the following: fpm -s dir ... pathA=/location1 pathA=location2
# The above command was copying pathA to both locations but hardlinking them instead of creating normal files.
foo = File.join(tmpdir, "foo")
File.write(foo, "hello world")
paths = [ "/opt/example/foo", "/usr/share/example/foo" ]
paths.each do |path|
subject.input("#{foo}=#{path}")
end
subject.output(output)
outfiles = paths.collect { |path| File.join(output, path) }
expect(outfiles).to all(satisfy("have link count == 1") { |path| File.lstat(path).nlink == 1 })
end
end
context "hardlinks" do
it "should create hardlinks when inputs are hardlinks (within the context of the target package)" do
# For issue #2102
# With the following: fpm -s dir ... pathA=/location1 pathA=location2
# The above command was copying pathA to both locations but hardlinking them instead of creating normal files.
foo = File.join(tmpdir, "foo")
bar = File.join(tmpdir, "bar")
File.write(foo, "hello world")
File.link(foo, bar)
subject.attributes[:chdir] = tmpdir
subject.input(".")
subject.output(output)
outfiles = ["foo", "bar"].collect { |path| File.join(output, path) }
expect(outfiles).to all(satisfy("have link count == 2") { |path| File.stat(path).nlink == 2 })
end
end
context "SYMLINKS." do

View File

@ -8,7 +8,7 @@ if !platform_is_darwin
"which requires a Darwin platform.")
end
describe FPM::Package::OSXpkg do
describe FPM::Package::OSXpkg, :if => platform_is_darwin do
describe "#identifier" do
it "should be of the form reverse.domain.pkgname" do
subject.name = "name"

View File

@ -35,7 +35,7 @@ end
describe FPM::Package::Python do
before do
skip("Python program not found") unless python_usable?
subject.attributes[:python_bin] = find_python
#subject.attributes[:python_bin] = find_python
end
let (:example_dir) do
@ -50,16 +50,15 @@ describe FPM::Package::Python do
before :each do
subject.attributes[:python_downcase_name?] = false
end
context "when :python_fix_name? is true" do
before :each do
subject.attributes[:python_fix_name?] = true
end
context "and :python_package_name_prefix is nil/default" do
it "should prefix the package with 'python-'" do
it "should prefix the package name based on detected python-bin name" do
subject.input(example_dir)
insist { subject.name } == "python-Example"
insist { subject.name } == "#{subject.attributes[:python_bin]}-Example"
end
end
@ -67,6 +66,7 @@ describe FPM::Package::Python do
it "should prefix the package name appropriately" do
prefix = "whoa"
subject.attributes[:python_package_name_prefix] = prefix
subject.attributes[:python_package_name_prefix_given?] = true
subject.input(example_dir)
insist { subject.name } == "#{prefix}-Example"
end
@ -96,9 +96,10 @@ describe FPM::Package::Python do
end
context "and :python_package_name_prefix is nil/default" do
it "should prefix the package with 'python-'" do
it "should prefix the package based on the version of python" do
subject.input(example_dir)
insist { subject.name } == "python-example"
insist { subject.attributes[:python_package_name_prefix_given?] }.nil?
insist { subject.name } == "#{subject.attributes[:python_bin]}-example"
end
end
@ -106,6 +107,7 @@ describe FPM::Package::Python do
it "should prefix the package name appropriately" do
prefix = "whoa"
subject.attributes[:python_package_name_prefix] = prefix
subject.attributes[:python_package_name_prefix_given?] = true
subject.input(example_dir)
insist { subject.name } == "#{prefix}-example"
end
@ -130,9 +132,24 @@ describe FPM::Package::Python do
end
it "it should include the dependencies from setup.py" do
# Insist on using the defaults for this test, prefix not given and
# prefix should automatically be based on the python major version
insist { subject.attributes[:python_package_name_prefix_given?] }.nil?
subject.input(example_dir)
prefix = subject.attributes[:python_package_name_prefix]
# The package name prefix attribute should be set to _something_ by default
reject { prefix }.nil?
# XXX: Why is there extra whitespace in these strings?
insist { subject.dependencies.sort } == ["python-dependency1 ","python-dependency2 ", "python-rtxt-dep4 "]
#
# Note: The dependency list should only include entries which are supported by fpm.
# python dependencies can have 'environment markers' and most of those markers are
# not supported by fpm.
# In this test, there are (at time of writing) some python_version markers and fpm doesn't
# support those.
insist { subject.dependencies.sort } == ["#{prefix}-dependency1","#{prefix}-dependency2", "#{prefix}-rtxt-dep4"]
end
context "and :python_disable_dependency is set" do
@ -142,7 +159,8 @@ describe FPM::Package::Python do
it "it should exclude the dependency" do
subject.input(example_dir)
insist { subject.dependencies.sort } == ["python-dependency2 ", "python-rtxt-dep4 "]
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-dependency2", "#{prefix}-rtxt-dep4"]
end
end
end
@ -160,13 +178,15 @@ describe FPM::Package::Python do
it "it should prefix requirements.txt" do
subject.input(example_dir)
insist { subject.dependencies.sort } == ["python-rtxt-dep1 > 0.1", "python-rtxt-dep2 = 0.1", "python-rtxt-dep4 "]
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-rtxt-dep1 > 0.1", "#{prefix}-rtxt-dep2 = 0.1", "#{prefix}-rtxt-dep4"]
end
it "it should exclude the dependency" do
subject.attributes[:python_disable_dependency] = "rtxt-dep1"
subject.input(example_dir)
insist { subject.dependencies.sort } == ["python-rtxt-dep2 = 0.1", "python-rtxt-dep4 "]
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-rtxt-dep2 = 0.1", "#{prefix}-rtxt-dep4"]
end
end
@ -177,41 +197,120 @@ describe FPM::Package::Python do
it "it should load requirements.txt" do
subject.input(example_dir)
insist { subject.dependencies.sort } == ["rtxt-dep1 > 0.1", "rtxt-dep2 = 0.1", "rtxt-dep4 "]
insist { subject.dependencies.sort } == ["rtxt-dep1 > 0.1", "rtxt-dep2 = 0.1", "rtxt-dep4"]
end
it "it should exclude the dependency" do
subject.attributes[:python_disable_dependency] = "rtxt-dep1"
subject.input(example_dir)
insist { subject.dependencies.sort } == ["rtxt-dep2 = 0.1", "rtxt-dep4 "]
insist { subject.dependencies.sort } == ["rtxt-dep2 = 0.1", "rtxt-dep4"]
end
end
end
context "python_scripts_executable is set" do
it "should have scripts with a custom hashbang line" do
pending("Disabled on travis-ci becaulamese it always fails, and there is no way to debug it?") if is_travis
skip("Requires python3 executable") unless program_exists?("python3")
context "when input is a name" do
it "should download from pypi" do
subject.input("click==8.3.0")
prefix = subject.attributes[:python_package_name_prefix]
subject.attributes[:python_scripts_executable] = "fancypants"
# Newer versions of Django require Python 3.
subject.attributes[:python_bin] = "python3"
subject.input("django")
insist { subject.name } == "#{prefix}-click"
insist { subject.version } == "8.3.0"
insist { subject.maintainer } == "Pallets <contact@palletsprojects.com>"
insist { subject.architecture } == "all"
insist { subject.dependencies } == [ ]
# Determine, where 'easy_install' is going to install scripts
#script_dir = easy_install_default(subject.attributes[:python_bin], 'script_dir')
#path = subject.staging_path(File.join(script_dir, "django-admin.py"))
# Hardcode /usr/local/bin here. On newer Python 3's I cannot figure out how to
# determine the script_dir at installation time. easy_install's method is gone.
path = subject.staging_path("/usr/local/bin/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
context "when given a project containing a pyproject.toml" do
let (:project) do
File.expand_path("../../fixtures/python-pyproject.toml/", File.dirname(__FILE__))
end
it "should package it correctly" do
subject.input(project)
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.name } == "#{prefix}-example"
insist { subject.version } == "1.2.3"
insist { subject.maintainer } == "Captain Fancy <foo@example.com>"
end
it "should package it correctly even if the path given is directly to the pyproject.toml" do
subject.input(File.join(project, "pyproject.toml"))
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.name } == "#{prefix}-example"
insist { subject.version } == "1.2.3"
insist { subject.maintainer } == "Captain Fancy <foo@example.com>"
end
end
end # describe FPM::Package::Python
describe FPM::Package::Python::PythonMetadata do
context "processing simple examples" do
let(:text) {
[
"Metadata-Version: 2.4",
"Name: hello",
"Version: 1.0",
].join("\n") + "\n"
}
subject { described_class.from(text) }
it "should" do
insist { subject.name } == "hello"
insist { subject.version } == "1.0"
end
end
# Use a known METADATA file from a real Python package
context "when parsing Django 5.2.6's METADATA" do
let(:text) do
File.read(File.expand_path("../../fixtures/python/METADATA", File.dirname(__FILE__)))
end
expectations = {
"Metadata-Version" => "2.4",
"Name" => "Django",
"Version" => "5.2.6",
"Summary" => "A high-level Python web framework that encourages rapid development and clean, pragmatic design.",
"Author-email" => "Django Software Foundation <foundation@djangoproject.com>",
"License" => "BSD-3-Clause",
}
let(:parsed) { described_class.parse(text) }
let(:headers) { parsed[0] }
let(:body) { parsed[1] }
let(:metadata) { described_class.from(text) }
expectations.each do |field, value|
it "the #{field} field should be #{value.inspect}" do
insist { headers[field] } == value
end
end
it "should parse multivalue fields into an array value" do
insist { headers["Classifier"] }.is_a?(Enumerable)
insist { headers["Project-URL"] }.is_a?(Enumerable)
insist { headers["Requires-Dist"] }.is_a?(Enumerable)
insist { headers["Requires-Dist"] }.include?('asgiref>=3.8.1')
insist { headers["Requires-Dist"] }.include?('sqlparse>=0.3.1')
insist { headers["Requires-Dist"] }.include?('tzdata; sys_platform == "win32"')
insist { headers["Requires-Dist"] }.include?('argon2-cffi>=19.1.0; extra == "argon2"')
insist { headers["Requires-Dist"] }.include?('bcrypt; extra == "bcrypt"')
end
it "should provide correctly parsed values" do
insist { metadata.name } == "Django"
insist { metadata.version } == "5.2.6"
insist { metadata.summary } == "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
insist { metadata.license } == "BSD-3-Clause"
insist { metadata.homepage } == "https://www.djangoproject.com/"
end
end # parsing Django METADATA
end

View File

@ -484,6 +484,45 @@ describe FPM::Package::RPM do
File.unlink(@target)
end
end # dist
context "changelog" do
it "should generate a changelog in the release" do
subject.name = "example"
subject.attributes[:rpm_dist] = 'rhel'
subject.version = "1.2.3"
subject.maintainer = "Spec Test <spec.test@example.com>"
@target = Stud::Temporary.pathname
# Write RPM
subject.output(@target)
@rpm = ::RPM::File.new(@target)
insist { @rpm.tags[:changelogname] } == [ "Spec Test <spec.test@example.com> - 1.2.3-1.rhel" ]
insist { @rpm.tags[:changelogtext] } == [ "- Package created with FPM" ]
File.unlink(@target)
end
it "should have the changelog in the release" do
subject.name = "example"
subject.attributes[:rpm_changelog] = <<CHANGELOG
* Tue May 31 2016 Example Maintainers <fpm@example.com> - 1.0-1
- First example package
CHANGELOG
subject.version = "1.0"
@target = Stud::Temporary.pathname
# Write RPM
subject.output(@target)
@rpm = ::RPM::File.new(@target)
insist { @rpm.tags[:changelogtime] } == [ 1464696000 ]
insist { @rpm.tags[:changelogname] } == [ "Example Maintainers <fpm@example.com> - 1.0-1" ]
insist { @rpm.tags[:changelogtext] } == [ "- First example package" ]
File.unlink(@target)
end
end # changelog
end # #output
describe "prefix attribute" do
@ -530,6 +569,30 @@ describe FPM::Package::RPM do
insist { rpm.files } == [ "/example/%name%" ]
end
it "should escape '{' and '}' characters in filenames" do
Dir.mkdir(subject.staging_path("/example"))
File.write(subject.staging_path("/example/{{ test }}"), "Hello")
subject.output(@target)
rpm = ::RPM::File.new(@target)
insist { rpm.files } == [ "/example/{{ test }}" ]
end
it "should correctly include files with spaces and quotation marks" do
names = [
"/It's time to go.txt",
"/It's \"time\" to go.txt"
]
names.each do |n|
File.write(subject.staging_path("#{n}"), "Hello")
end
subject.output(@target)
rpm = ::RPM::File.new(@target)
insist { rpm.files.sort } == names.sort
end
it "should escape '%' characters in filenames while preserving permissions" do
Dir.mkdir(subject.staging_path("/example"))
File.write(subject.staging_path("/example/%name%"), "Hello")

View File

@ -26,7 +26,7 @@ describe FPM::Package::Tar do
end
it "doesn't include a .scripts folder" do
insist { Dir.exists?(File.join(output_dir, '.scripts')) } == false
insist { Dir.exist?(File.join(output_dir, '.scripts')) } == false
end
after do

View File

@ -12,7 +12,7 @@ if !virtualenv_usable?
"no virtualenv/tools bin on your path")
end
describe FPM::Package::Virtualenv do
describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
before do
skip("virtualenv and/or virtualenv-tools programs not found") unless virtualenv_usable?
end
@ -38,7 +38,7 @@ describe FPM::Package::Virtualenv do
activate_path = File.join(subject.build_path, '/usr/share/python/pip/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
end
it "can override the version specified on the input" do
@ -85,7 +85,7 @@ describe FPM::Package::Virtualenv do
subject.input("pip==8.1.2")
activate_path = File.join(subject.build_path, '/opt/foo/pip/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
end
end
@ -95,10 +95,10 @@ describe FPM::Package::Virtualenv do
subject.input("pip==8.1.2")
activate_path = File.join(subject.build_path, '/usr/share/python/pip/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
egg_path = File.join(subject.build_path, '/setup.py')
expect(File.exists?(egg_path)).to(be_truthy)
expect(File.exist?(egg_path)).to(be_truthy)
end
end
@ -118,7 +118,7 @@ describe FPM::Package::Virtualenv do
subject.input(File.join(fixtures_dir, 'requirements.txt'))
activate_path = File.join(subject.build_path, '/usr/share/python/virtualenv/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
expect(subject.name).to eq("virtualenv-virtualenv")
end
end
@ -129,7 +129,7 @@ describe FPM::Package::Virtualenv do
subject.input(File.join(fixtures_dir, 'requirements.txt'))
activate_path = File.join(subject.build_path, '/usr/share/python/virtualenv/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
expect(subject.name).to eq("virtualenv-foo")
end
@ -143,7 +143,7 @@ describe FPM::Package::Virtualenv do
activate_path = File.join(subject.staging_path, '/opt/foo/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
end
it "takes precedence over other folder options" do
@ -153,7 +153,7 @@ describe FPM::Package::Virtualenv do
activate_path = File.join(subject.staging_path, '/opt/foo/bin/activate')
expect(File.exists?(activate_path)).to(be_truthy)
expect(File.exist?(activate_path)).to(be_truthy)
end
end
end

View File

@ -2,6 +2,7 @@ require "spec_setup"
require "fpm" # local
require "fpm/util" # local
require "stud/temporary"
require "timeout"
describe FPM::Util do
@ -98,6 +99,20 @@ describe FPM::Util do
subject.safesystem("true")
end
end
it "should not prompt for input" do
expect {
Timeout::timeout(3) do
subject.safesystem("sh", "-c", "read foo || true")
end
}.not_to raise_error
end
it "should pipe command output to logger" do
logger = Cabin::Channel.get
expect(logger).to receive(:pipe)
subject.safesystem("true")
end
end
describe "#expand_pessimistic_constraints" do

View File

@ -13,6 +13,11 @@ $: << File.join(File.dirname(File.dirname(__FILE__)), "lib")
require "fpm/util"
include FPM::Util
Cabin::Channel.get.level = :error
spec_logger = Cabin::Channel.get("rspec")
spec_logger.subscribe(STDOUT)
spec_logger.level = :error
# Enable debug logs if requested.
if $DEBUG or ENV["DEBUG"]
Cabin::Channel.get.level = :debug
@ -28,10 +33,6 @@ else
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

View File

@ -1,4 +1,4 @@
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; urgency=medium
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) <%= distribution %>; urgency=medium
* Package created with FPM.

View File

@ -14,7 +14,7 @@ Description: <%= firstline %>
<%= remainder.collect { |l| l =~ /^ *$/ ? " ." : " #{l}" }.join("\n") %>
<% end -%>
Changes:
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; urgency=medium
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) <%= distribution %>; urgency=medium
* Package created with FPM.
Checksums-Sha1:
<% changes_files.each do |file| -%>

View File

@ -260,4 +260,4 @@ fi
<% end -%>
%changelog
<%= attributes[:rpm_changelog] %>
<%= changelog %>