Compare commits

..

4 Commits

Author SHA1 Message Date
Jordan Sissel 1e52128a82 Fix reStructuredText header length syntax 2022-03-30 23:14:22 -07:00
Jordan Sissel 8e811bc8a4 Add description in header 2022-03-30 23:14:07 -07:00
Jordan Sissel 14200c7604 Add description in header 2022-03-30 23:14:05 -07:00
Jordan Sissel 8a1dfb0f48 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:06:05 -07:00
85 changed files with 1620 additions and 2140 deletions

8
.github/main.workflow vendored Normal file
View File

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

View File

@ -1,30 +0,0 @@
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,14 +1,6 @@
# 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,71 +1,6 @@
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,5 +1,3 @@
# 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.
@ -10,98 +8,87 @@ ARG BASE_ENV=everything
ARG TARGET=test
# Container to throw an error if called with a bare `docker build .`
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
FROM ubuntu:18.04 as error
RUN echo "\n\n\nHey! Use buildkit. See the Makefile or docs\n\n\n"
RUN false
# Base container is used for various release and test things
FROM ubuntu:20.04 as minimal-base
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Etc/UTC
FROM ubuntu:18.04 as minimal-base
# Runtime deps. Build deps go in the build or test containers
# 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
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
# 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 <<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
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/*
# 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 AS test
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
# installing ffi here is a bit of an optimization for how COPY and layer reuse works
RUN gem install --no-document ffi:*
USER fpm
WORKDIR /origsrc
RUN gem install --no-ri --no-rdoc ffi
RUN install -d -o fpm /origsrc
COPY --chown=fpm . /origsrc
ENV HOME=/origsrc
ENV BUNDLE_PATH=/origsrc/.bundle
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"]
# 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
# build a container from a released gem. install build deps here, so
# we can omit them from the final release package
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 ${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 release
FROM build as release
COPY --from=build /fpm /fpm
ENV GEM_PATH=/fpm
ENV PATH="/fpm/bin:${PATH}"
USER fpm
ENV GEM_PATH /fpm
ENV PATH "/fpm/bin:${PATH}"
WORKDIR /src
ENTRYPOINT ["/fpm/bin/fpm"]
# This target is to help docker buildkit in resolving things.
# hadolint ignore=DL3006
FROM ${TARGET}
FROM ${TARGET} as final

View File

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

53
NOTES.md Normal file
View File

@ -0,0 +1,53 @@
# 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
===
|Gem|
|Chat| |Gem|
The goal of fpm is to make it easy and quick to build packages such as rpms,
debs, OSX packages, etc.
@ -97,5 +97,7 @@ 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,8 +2,10 @@
FROM debian:latest
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3-pip
RUN apt-get install -y python3-sphinx #pip3 install Sphinx
RUN pip3 install Sphinx
#==1.8
RUN apt-get install -y python3-sphinx python3-sphinx-rtd-theme python3-sphinx-autobuild
RUN pip3 install sphinx_rtd_theme
RUN pip3 install alabaster
RUN pip3 install 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 -I ../lib generate-cli-reference.rb > $@
ruby 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 generate-cli-reference.rb Makefile
ruby -I ../lib generate-cli-reference.rb $* > $@
packages/cli/%.rst: ../lib/fpm/package/%.rb packages/cli
ruby 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 package-type-cli
build: $(GENERATED_FILES) | docker-prep
docker run -it -v $$PWD/../:/project:z $(IMAGE) sh -xc 'make -C /project/docs html && chown -R 1000:1000 /project/docs'

View File

@ -37,7 +37,6 @@
.. _#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
@ -69,7 +68,6 @@
.. _#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
@ -91,12 +89,7 @@
.. _#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
@ -115,7 +108,6 @@
.. _#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
@ -129,14 +121,11 @@
.. _#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
@ -146,11 +135,8 @@
.. _#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
@ -165,91 +151,20 @@
.. _#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,9 +28,7 @@ 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 = [
'sphinx_rtd_theme'
]
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -48,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'fpm - packaging made simple'
copyright = u'2025, Jordan Sissel and contributors'
copyright = u'2016, Jordan Sissel'
author = u'Jordan Sissel'
# The version info for the project you're documenting, acts as replacement for
@ -56,16 +54,16 @@ author = u'Jordan Sissel'
# built documents.
#
# The short X.Y version.
version = u'1.17'
version = u'1.9'
# The full version, including alpha/beta/rc tags.
release = u'1.17.0'
release = u'1.9.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 = "en"
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:

View File

@ -56,15 +56,10 @@ 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 install libarchive-tools
apt-get install bsdtar
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,8 +2,6 @@
require_relative "../lib/fpm/command"
flagsort = lambda { |x| x.sub(/^--(?:\[no-\])?/, "") }
if ARGV.length == 0
puts "Command-line Reference"
puts "=========================="
@ -15,8 +13,7 @@ if ARGV.length == 0
puts "General Options"
puts "---------------"
#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|
FPM::Command.instance_variable_get(:@declared_options).each do |option|
text = option.description.gsub("\n", " ")
if option.type == :flag
@ -52,7 +49,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, _| flagsort.call(flag.first) }.each do |flag, param, help, options, block|
options.sort_by { |flag, _| flag }.each do |flag, param, help, options, block|
if param == :flag
puts "* ``#{flag.first}``"
else

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.17.0
1.14.0
Now you can go on to `using FPM! <getting-started.html>`_

View File

@ -1,21 +1,21 @@
* ``--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?
* ``--cpan-cpanm-bin CPANM_EXECUTABLE``
- The path to the cpanm executable you wish to run.
* ``--cpan-mirror CPAN_MIRROR``
- The CPAN mirror to use instead of the default.
* ``--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

View File

@ -1,19 +1,33 @@
* ``--[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, zst, none.
* ``--deb-compression-level [0-9]``
- Select a compression level. 0 is none or minimal. 9 is max compression.
- The compression type to use, must be one of gz, bzip2, xz, none.
* ``--deb-config SCRIPTPATH``
- Add SCRIPTPATH as debconf config file.
* ``--deb-custom-control FILEPATH``
@ -24,12 +38,8 @@
- 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``
@ -38,12 +48,8 @@
- 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``
@ -56,22 +62,12 @@
- 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,5 +1,3 @@
* ``--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,7 +1,3 @@
* ``--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``
@ -10,6 +6,14 @@
- 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``
@ -20,12 +24,8 @@
- 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 @@
* ``--p5p-group GROUP``
- Set the group to GROUP in the prototype file.
* ``--[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.
* ``--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,6 +2,4 @@
- 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,29 +1,27 @@
* ``--python-bin PYTHON_EXECUTABLE``
- The path to the python executable you wish to run.
* ``--[no-]python-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
- Include requirements defined in setup.py as dependencies.
* ``--[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``
@ -33,9 +31,9 @@
* ``--python-pypi PYPI_URL``
- PyPi Server uri for retrieving packages.
* ``--python-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.
- 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``
- (DEPRECATED) Arbitrary argument(s) to be passed to setup.py
- 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,15 +1,25 @@
* ``--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``
@ -30,14 +40,8 @@
- 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``
@ -46,26 +50,20 @@
- 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: https://rpm-software-management.github.io/rpm/manual/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: http://rpm.org/api/4.4.2.2/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: https://rpm-software-management.github.io/rpm/manual/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: http://rpm.org/api/4.4.2.2/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: https://rpm-software-management.github.io/rpm/manual/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: http://rpm.org/api/4.4.2.2/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: https://rpm-software-management.github.io/rpm/manual/triggers.html
* ``--[no-]rpm-use-file-permissions``
- Use existing file permissions when defining ownership and modes.
- 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
* ``--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,7 +1,11 @@
* ``--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?
* ``--[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.
* ``--virtualenv-install-location DIRECTORY``
- DEPRECATED: Use --prefix instead. Location to which to install the virtualenv by default.
* ``--virtualenv-other-files-dir DIRECTORY``
@ -12,8 +16,4 @@
- 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

@ -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
------------

View File

@ -7,6 +7,6 @@ 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

View File

@ -10,6 +10,6 @@ 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

View File

@ -7,6 +7,6 @@ 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

View File

@ -7,6 +7,6 @@ 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

@ -18,6 +18,6 @@ fpm supports using ``pleaserun`` only as an input type. This means you can conve
``pleaserun`` input packages to output packages like ``deb``, ``rpm``, and more.
pleaserun-specific command line flags
-------------------------------------
-------------------------------
.. include:: cli/pleaserun.rst

View File

@ -6,7 +6,7 @@ This package format is typically used in older Solaris versions (Solaris 7, 8,
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
.. _newer package format, p5p: /packages/p5p.html
Supported Uses in FPM
---------------------

View File

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

View File

@ -0,0 +1,53 @@
#!/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

@ -0,0 +1,16 @@
$: << 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

@ -0,0 +1,18 @@
$: << 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

@ -0,0 +1,61 @@
#
# 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

@ -0,0 +1,18 @@
# 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

35
examples/fpm/Makefile Normal file
View File

@ -0,0 +1,35 @@
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) .

19
examples/fpm/README.md Normal file
View File

@ -0,0 +1,19 @@
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

35
examples/jruby/Makefile Normal file
View File

@ -0,0 +1,35 @@
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) .

17
examples/jruby/README.md Normal file
View File

@ -0,0 +1,17 @@
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

@ -0,0 +1,15 @@
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,9 +22,13 @@ 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.9.1") # license: Apache 2
spec.add_dependency("cabin", ">= 0.6.0") # license: Apache 2
# For backports to older rubies
# https://github.com/marcandre/backports
@ -35,18 +39,21 @@ 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.13.0") # license: MIT (according to wikipedia)
spec.add_development_dependency("rspec", "~> 3.0.0") # license: MIT (according to wikipedia)
spec.add_development_dependency("insist", "~> 1.0.0") # license: Apache 2
spec.add_development_dependency("pry")

View File

@ -3,6 +3,7 @@ require "fpm/namespace"
require "fpm/version"
require "fpm/util"
require "clamp"
require "ostruct"
require "fpm"
require "tmpdir" # for Dir.tmpdir
@ -246,24 +247,13 @@ 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
@ -301,15 +291,6 @@ 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
@ -374,7 +355,7 @@ class FPM::Command < Clamp::Command
# If --inputs was specified, read it as a file.
if !inputs.nil?
if !File.exist?(inputs)
if !File.exists?(inputs)
logger.fatal("File given for --inputs does not exist (#{inputs})")
return 1
end
@ -389,7 +370,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.exist?(exclude_file)
if !File.exists?(exclude_file)
logger.fatal("File given for --exclude-file does not exist (#{exclude_file})")
return 1
end
@ -413,12 +394,7 @@ 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.
# 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
if object.send(attribute).nil? || send(attribute) != send("default_#{attribute}")
logger.info("Setting from flags: #{attribute}=#{send(attribute)}")
object.send("#{attribute}=", send(attribute))
end
@ -459,7 +435,7 @@ class FPM::Command < Clamp::Command
# Skip scripts not set
next if path.nil?
if !File.exist?(path)
if !File.exists?(path)
logger.error("No such file (for #{scriptname.to_s}): #{path.inspect}")
script_errors << path
end
@ -595,86 +571,12 @@ 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,6 +3,7 @@ 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
@ -330,12 +331,11 @@ class FPM::Package
template_path = File.join(template_dir, path)
template_code = File.read(template_path)
logger.info("Reading template", :path => template_path)
erb = erbnew(template_code)
erb = ERB.new(template_code, nil, "-")
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 = erbnew(scripts[script_name])
erb = ERB.new(scripts[script_name], nil, "-")
# TODO(sissel): find the original file name for the file.
erb.filename = "script(#{script_name})"
return erb.result(binding)

View File

@ -112,7 +112,6 @@ 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")
@ -305,7 +304,7 @@ class FPM::Package::CPAN < FPM::Package
directory = build_path("module")
::Dir.mkdir(directory)
args = [ "-C", directory, "-zxf", tarball,
%q{--transform=s,[./]*[^/]*/,,} ]
"--strip-components", "1" ]
safesystem("tar", *args)
return directory
end

View File

@ -27,31 +27,18 @@ 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", "zst", "none" ]
COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "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 + . ~
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
RELATIONSHIP_FIELD_PATTERN = /^(?<name>[A-z0-9_-]+)(?: *\((?<relation>[<>=]+) *(?<version>(?:[0-9]+:)?[0-9A-Za-z+~.-]+(?:-[0-9A-Za-z+~.]+)?)\))?$/
option "--ignore-iteration-in-dependencies", :flag,
"For '=' (equal) dependencies, allow iterations on the specified " \
@ -73,25 +60,12 @@ 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 FPM::Package::InvalidArgument, "deb compression value of '#{value}' is invalid. " \
raise ArgumentError, "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 ...
@ -119,7 +93,7 @@ class FPM::Package::Deb < FPM::Package
end
option "--priority", "PRIORITY",
"The debian package 'priority' value.", :default => "optional"
"The debian package 'priority' value.", :default => "extra"
option "--use-file-permissions", :flag,
"Use existing file permissions when defining ownership and modes"
@ -223,11 +197,6 @@ 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
@ -247,7 +216,7 @@ class FPM::Package::Deb < FPM::Package
def initialize(*args)
super(*args)
attributes[:deb_priority] = "optional"
attributes[:deb_priority] = "extra"
end # def initialize
private
@ -280,9 +249,6 @@ 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
@ -321,21 +287,6 @@ 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)
@ -353,9 +304,6 @@ 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 = ""
@ -368,7 +316,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,zst} from the deb package into staging_path
# unpack the control.tar.{,gz,bz2,xz} from the deb package into staging_path
# Unpack the control tarball
safesystem(ar_cmd[0] + " p #{package} #{controltar} | tar #{compression} -xf - -C #{path}")
@ -388,7 +336,7 @@ class FPM::Package::Deb < FPM::Package
version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
m = version_re.match(parse.call("Version"))
if !m
raise FPM::InvalidPackageConfiguration, "Unsupported version string '#{parse.call("Version")}'"
raise "Unsupported version string '#{parse.call("Version")}'"
end
self.epoch, self.version, self.iteration = m.captures
@ -478,9 +426,6 @@ 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 = ""
@ -531,36 +476,27 @@ 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 FPM::InvalidPackageConfiguration, "#{name}: source_date_epoch of 0 not supported."
raise "#{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 FPM::InvalidPackageConfiguration, "#{name}: ar is insufficient to support source_date_epoch."
raise "#{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 FPM::InvalidPackageConfiguration, "#{name}: tar is insufficient to support source_date_epoch."
raise "#{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)
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}"))
name = File.basename(systemd, ".service")
dest_systemd = staging_path("lib/systemd/system/#{name}.service")
mkdir_p(File.dirname(dest_systemd))
FileUtils.cp(systemd, dest_systemd)
File.chmod(0644, dest_systemd)
attributes[:deb_systemd] << name_with_extension
# add systemd service name to attribute
attributes[:deb_systemd] << name
end
if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?
@ -627,7 +563,7 @@ class FPM::Package::Deb < FPM::Package
end # No need to close, GzipWriter#close will close it.
end
if File.exist?(dest_changelog) and not File.exist?(dest_upstream_changelog)
if File.exists?(dest_changelog) and not File.exists?(dest_upstream_changelog)
# see https://www.debian.org/doc/debian-policy/ch-docs.html#s-changelogs
File.rename(dest_changelog, dest_upstream_changelog)
end
@ -663,12 +599,8 @@ class FPM::Package::Deb < FPM::Package
end
attributes.fetch(:deb_systemd_list, []).each do |systemd|
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}"))
name = File.basename(systemd, ".service")
dest_systemd = staging_path("lib/systemd/system/#{name}.service")
mkdir_p(File.dirname(dest_systemd))
FileUtils.cp(systemd, dest_systemd)
File.chmod(0644, dest_systemd)
@ -681,40 +613,31 @@ class FPM::Package::Deb < FPM::Package
when "gz", nil
datatar = build_path("data.tar.gz")
controltar = build_path("control.tar.gz")
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?}"}
compression = "-z"
when "bzip2"
datatar = build_path("data.tar.bz2")
controltar = build_path("control.tar.gz")
compression_flags = ["-j"]
compressor_options = {"BZIP" => "-#{self.attributes[:deb_compression_level] || 9}"}
compression = "-j"
when "xz"
datatar = build_path("data.tar.xz")
controltar = build_path("control.tar.xz")
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}"}
compression = "-J"
when "none"
datatar = build_path("data.tar")
controltar = build_path("control.tar")
compression_flags = []
compressor_options = {}
compression = ""
else
raise FPM::InvalidPackageConfiguration,
"Unknown compression type '#{self.attributes[:deb_compression]}'"
end
args = [ tar_cmd, "-C", staging_path ] + compression_flags + data_tar_flags + [ "-cf", datatar, "." ]
args = [ tar_cmd, "-C", staging_path, compression ] + 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
@ -754,17 +677,9 @@ 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)
@ -779,7 +694,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}"
@ -805,7 +720,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.exist?(changelog_path)
if File.exists?(changelog_path)
logger.debug("Found a deb changelog file, using it.", :path => changelog_path)
attributes[:deb_changelog] = build_path("deb_changelog")
File.open(attributes[:deb_changelog], "w") do |deb_changelog|
@ -819,7 +734,7 @@ class FPM::Package::Deb < FPM::Package
if origin == FPM::Package::Deb
changelog_path = staging_path("usr/share/doc/#{name}/changelog.gz")
if File.exist?(changelog_path)
if File.exists?(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|
@ -865,18 +780,6 @@ 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]/
@ -970,11 +873,6 @@ 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]})"
@ -1008,22 +906,13 @@ class FPM::Package::Deb < FPM::Package
case self.attributes[:deb_compression]
when "gz", "bzip2", nil
controltar = "control.tar.gz"
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?}"}
compression = "-z"
when "xz"
controltar = "control.tar.xz"
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}"}
compression = "-J"
when "none"
controltar = "control.tar"
compression_flags = []
compressor_options = {}
compression = ""
else
raise FPM::InvalidPackageConfiguration,
"Unknown compression type '#{self.attributes[:deb_compression]}'"
@ -1033,13 +922,14 @@ 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_flags + [ "-cf", controltar,
args = [ tar_cmd, "-C", control_path, compression, "-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
@ -1121,7 +1011,7 @@ class FPM::Package::Deb < FPM::Package
etcfiles = []
# Add everything in /etc
begin
if !attributes[:deb_no_default_config_files?] && File.exist?(staging_path("/etc"))
if !attributes[:deb_no_default_config_files?] && File.exists?(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")
@ -1151,7 +1041,6 @@ 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
@ -1296,5 +1185,5 @@ class FPM::Package::Deb < FPM::Package
return data_tar_flags
end # def data_tar_flags
public(:input, :output, :architecture, :name, :prefix, :version, :converted_from, :to_s, :data_tar_flags)
public(:input, :output, :architecture, :name, :prefix, :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.exist?(chdir == '.' ? path : File.join(chdir, path))
if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
origin, destination = path.split("=", 2)
if File.directory?(origin) && origin[-1,1] == "/"
@ -197,6 +197,10 @@ 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,10 +16,6 @@ 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)
@ -94,36 +90,28 @@ class FPM::Package::FreeBSD < FPM::Package
end # def output
# Handle architecture naming conversion:
# <osname>:<osversion>:<arch>
# <osname>:<osversion>:<arch>:<wordsize>[.other]
def architecture
osname = 'FreeBSD'
osname = %x{uname -s}.chomp
osversion = %x{uname -r}.chomp.split('.').first
arch = case @architecture
# Essentially because no testing on other platforms
arch = 'x86'
wordsize = case @architecture
when nil, 'native'
%x{getconf LONG_BIT}.chomp # 'native' is current arch
when 'arm64'
'arm64'
when 'aarch64'
'arm64'
'64'
when 'amd64'
'amd64'
when 'x86_64'
'amd64'
'64'
when 'i386'
'i386'
when 'i686'
'i386'
when 'any'
'*'
when 'all'
'*'
when 'noarch'
'*'
'32'
else
%x{getconf LONG_BIT}.chomp # default to native, the current arch
end
return [osname, attributes[:freebsd_osversion], arch].join(':')
return [osname, osversion, arch, wordsize].join(':')
end
def add_path(tar, tar_path, path)

View File

@ -104,18 +104,19 @@ 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}")
safesystem("git", "-C", download_dir, "clone", attributes[:gem_git_repo], ".")
g = Git.clone(attributes[:gem_git_repo],gem_name,:path => download_dir)
if attributes[:gem_git_branch]
safesystem("git", "-C", download_dir, "checkout", attributes[:gem_git_branch])
g.branch(attributes[:gem_git_branch]).checkout
g.pull('origin',attributes[:gem_git_branch])
end
gem_build = [ "#{attributes[:gem_gem]}", "build", "#{download_dir}/#{gem_name}.gemspec"]
::Dir.chdir(download_dir) do |dir|
gem_build = [ "#{attributes[:gem_gem]}", "build", "#{g.dir.to_s}/#{gem_name}.gemspec"]
::Dir.chdir(g.dir.to_s) do |dir|
logger.debug("Building in directory #{dir}")
safesystem(*gem_build)
end
gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
gem_files = ::Dir.glob(File.join(g.dir.to_s, "*.gem"))
else
gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
@ -134,19 +135,9 @@ 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)
# 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
spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
if !attributes[:gem_package_prefix].nil?
attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]

View File

@ -4,6 +4,7 @@ require "fileutils"
require "fpm/package/dir"
require 'tempfile' # stdlib
require 'pathname' # stdlib
require 'rexml/document' # stdlib
# Use an OS X pkg built with pkgbuild.
#
@ -102,7 +103,6 @@ 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,11 +148,6 @@ 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,15 +37,11 @@ class FPM::Package::Pacman < FPM::Package
def architecture
case @architecture
when nil
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
return %x{uname -m}.chomp # default to current arch
when "amd64" # debian and pacman disagree on architecture names
return "x86_64"
when "native"
return %x{uname -m}.chomp # 'native' is the current arch
return %x{uname -m}.chomp # 'native' is current arch
when "all", "any", "noarch"
return "any"
else
@ -238,7 +234,7 @@ class FPM::Package::Pacman < FPM::Package
return ""
when "gz"
return ".gz"
when "xz"
when "zx"
return ".xz"
when "bzip2"
return ".bz2"

View File

@ -18,7 +18,6 @@ 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.exist?(params[:output])
if File.exists?(params[:output])
# TODO(sissel): Allow folks to choose output?
logger.error("Puppet module directory '#{params[:output]}' already " \
"exists. Delete it or choose another output (-p flag)")

View File

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

View File

@ -1,24 +0,0 @@
#!/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,371 +51,76 @@ class FPM::Package::Python < FPM::Package
option "--downcase-dependencies", :flag, "Should the package dependencies " \
"be in lowercase?", :default => true
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 " \
option "--install-bin", "BIN_PATH", "The path to where python scripts " \
"should be installed to."
option "--install-lib", "LIB_PATH", "The path to where python libs " \
"should be installed to (default depends on your python installation). " \
"Want to find out what your target platform is using? Run this: " \
"python -c 'from distutils.sysconfig import get_python_lib; " \
"print get_python_lib()'" do
logger.warn("Using deprecated flag --install-bin")
end
option "--install-data", "DATA_PATH", "(DEPRECATED, does nothing) The path to where data should be " \
"print get_python_lib()'"
option "--install-data", "DATA_PATH", "The path to where data should be " \
"installed to. This is equivalent to 'python setup.py --install-data " \
"DATA_PATH" do
logger.warn("Using deprecated flag --install-bin")
end
option "--dependencies", :flag, "Include requirements defined by the python package" \
"DATA_PATH"
option "--dependencies", :flag, "Include requirements defined in setup.py" \
" 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", "(DEPRECATED) Set custom python " \
option "--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." do
logger.warn("Using deprecated flag --install-bin")
end
"to 'setup.py install' command."
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",
"(DEPRECATED) Arbitrary argument(s) to be passed to setup.py",
"Arbitrary argument(s) to be passed to setup.py",
:multivalued => true, :attribute_name => :python_setup_py_arguments,
:default => [] do
logger.warn("Using deprecated flag --install-bin")
end
:default => []
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"
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
private
# 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 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
# * The path to a directory containing setup.py
# * The path to a setup.py
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)
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
setup_py = File.join(path_to_package, "setup.py")
else
setup_py = path_to_package
end
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
if !File.exist?(setup_py)
logger.error("Could not find 'setup.py'", :path => setup_py)
raise "Unable to find python package; tried #{setup_py}"
end
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)
load_package_info(setup_py)
install_to_staging(setup_py)
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.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")
if File.directory?(path) or (File.exist?(path) and File.basename(path) == "setup.py")
return path
end
logger.info("Trying to download", :package => package)
@ -429,16 +134,24 @@ 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",
"-d", target,
"--no-binary", ":all:",
"-d", build_path,
"-i", attributes[:python_pypi],
]
@ -449,109 +162,117 @@ class FPM::Package::Python < FPM::Package
]
end
setup_cmd << want_pkg
setup_cmd += [
"--build",
target,
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(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
wheeldata = nil
execmd(["unzip", "-p", path, "*.dist-info/WHEEL"], :stdin => false, :stderr => false) do |stdout|
wheeldata, _ = PythonMetadata.parse(stdout.read(64<<10))
end
else
raise "Unexpected python package path. This might be an fpm bug? The path is #{path}"
def load_package_info(setup_py)
if !attributes[:python_package_prefix].nil?
attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
end
self.architecture = wheeldata["Root-Is-Purelib"] == "true" ? "all" : "native"
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
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?
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
self.name = metadata.name
# 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"
end
# Capture the output, which will be JSON metadata describing this python
# package. See fpm/lib/fpm/package/pyfpm/get_metadata.py for more
# details.
logger.info("fetching package metadata", :setup_cmd => setup_cmd)
success = safesystem(setup_cmd)
#%x{#{setup_cmd}}
if !success
logger.error("setup.py get_metadata failed", :command => setup_cmd,
:exitcode => $?.exitstatus)
raise "An unexpected error occurred while processing the setup.py file"
end
File.read(tmp)
end
logger.debug("result from `setup.py get_metadata`", :data => output)
metadata = JSON.parse(output)
logger.info("object output of get_metadata", :json => metadata)
self.architecture = metadata["architecture"]
self.description = metadata["description"]
# Sometimes the license field is multiple lines; do best-effort and just
# use the first line.
self.license = metadata["license"].split(/[\r\n]+/).first
self.version = metadata["version"]
self.url = metadata["url"]
# name prefixing is optional, if enabled, a name 'foo' will become
# 'python-foo' (depending on what the python_package_name_prefix is)
self.name = fix_name(self.name) if attributes[:python_fix_name?]
if attributes[:python_fix_name?]
self.name = fix_name(metadata["name"])
else
self.name = metadata["name"]
end
# 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?]
# 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|
metadata["dependencies"].each do |dep|
dep_re = /^([^<>!= ]+)\s*(?:([~<>!=]{1,2})\s*(.*))?$/
match = dep_re.match(dep)
if match.nil?
logger.error("Unable to parse dependency", :dependency => dep)
raise FPM::InvalidPackageConfiguration, "Invalid dependency '#{dep}'"
end
name, cmp, version = match.captures
next if attributes[:python_disable_dependency].include?(name)
@ -570,12 +291,8 @@ class FPM::Package::Python < FPM::Package
# convert dependencies from python-Foo to python-foo
name = name.downcase if attributes[:python_downcase_dependencies?]
if cmp.nil? && version.nil?
self.dependencies << "#{name}"
else
self.dependencies << "#{name} #{cmp} #{version}"
end
end # parse Requires-Dist dependencies
self.dependencies << "#{name} #{cmp} #{version}"
end
end # if attributes[:python_dependencies?]
end # def load_package_info
@ -595,17 +312,55 @@ class FPM::Package::Python < FPM::Package
end # def fix_name
# Install this package to the staging directory
def install_to_staging(path)
def install_to_staging(setup_py)
project_dir = File.dirname(setup_py)
prefix = "/"
prefix = attributes[:prefix] unless attributes[:prefix].nil?
# 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?
# 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
safesystem(*attributes[:python_pip], "install", "--no-deps", *flags, path)
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
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-software-management.github.io/rpm/manual/scriptlet_expansion.html)", :default => false
"(see: https://rpm.org/user_doc/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: https://rpm-software-management.github.io/rpm/manual/triggers.html" do |trigger|
"See: http://rpm.org/api/4.4.2.2/triggers.html" do |trigger|
match = trigger.match(/^(\[.*\]|)(.*): (.*)$/)
@logger.fatal("Trigger '#{trigger_type}' definition can't be parsed ('#{trigger}')") unless match
opt, pkg, file = match.captures
@ -185,9 +185,6 @@ 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
@ -195,30 +192,17 @@ 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)
if name.match?(/[ \t*?%${}\[\]]/)
name = name.gsub(/(\ |\t|\[|\]|\*|\?|\%|\$|'|"|\{|\}|\\)/, {
' ' => '?',
"\t" => '?',
'%' => '[%]',
'$' => '[$]',
'?' => '[?]',
'*' => '[*]',
'[' => '[\[]',
']' => '[\]]',
#'{' => '[\{]',
#'}' => '[\}]',
'{' => '?',
'}' => '?',
'"' => '\\"',
"'" => "\\'",
'\\' => '\\\\\\\\',
})
end
name
name = name.gsub(/(\ |\[|\]|\*|\?|\%|\$|')/, {
' ' => '?',
'%' => '[%]',
'$' => '[$]',
'?' => '[?]',
'*' => '[*]',
'[' => '[\[]',
']' => '[\]]',
"'" => "\\'",
})
end
def rpm_file_entry(file)
@ -282,41 +266,9 @@ 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::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
if origin == FPM::Package::Gem
fixed_deps = []
self.dependencies.collect do |dep|
# Gem dependency operator "~>" is not compatible with rpm. Translate any found.
@ -512,7 +464,6 @@ 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,31 +158,6 @@ 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
@ -216,6 +191,7 @@ 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,39 +1,13 @@
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 = Options.new(:name => package_name.to_s)
@options = OpenStruct.new(:name => package_name.to_s)
@source, @target = opts.values_at(:source, :target).map(&:to_s)
@directory = File.expand_path(opts[:directory].to_s)
@ -44,8 +18,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") if options.args.nil?
@args = options.args
abort("Must specify args") unless options.respond_to?(:args)
@args = options.delete_field(:args)
run_cli
end
end

View File

@ -1,6 +1,5 @@
require "fpm/namespace"
require "fileutils"
require "stud/temporary"
# Some utility functions
module FPM::Util
@ -136,7 +135,7 @@ module FPM::Util
raise ExecutableNotFound.new(program)
end
logger.info("Running command", :args => args2)
logger.debug("Running command", :args => args2)
stdout_r, stdout_w = IO.pipe
stderr_r, stderr_w = IO.pipe
@ -155,13 +154,10 @@ 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)
@ -230,18 +226,15 @@ module FPM::Util
@@ar_cmd_deterministic = false
# FIXME: don't assume current directory writeable
emptyfile = Stud::Temporary.pathname
testarchive = Stud::Temporary.pathname
FileUtils.touch([emptyfile])
FileUtils.touch(["fpm-dummy.tmp"])
["ar", "gar"].each do |ar|
["-qc", "-qcD"].each do |ar_create_opts|
FileUtils.rm_f([testarchive])
FileUtils.rm_f(["fpm-dummy.ar.tmp"])
# 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} #{testarchive} #{emptyfile} 2>/dev/null && env TZ=UTC LANG=C LC_TIME=C #{ar} -tv #{testarchive} | grep '0/0.*1970' > /dev/null 2>&1")
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")
if $?.exitstatus == 0
@@ar_cmd = [ar, ar_create_opts]
@@ar_cmd_deterministic = true
@ -251,8 +244,10 @@ 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
@ -266,10 +261,7 @@ module FPM::Util
return @@tar_cmd if defined? @@tar_cmd
# FIXME: don't assume current directory writeable
emptyfile = Stud::Temporary.pathname
testarchive = Stud::Temporary.pathname
FileUtils.touch([emptyfile])
FileUtils.touch(["fpm-dummy.tmp"])
# Prefer tar that supports more of the features we want, stop if we find tar of our dreams
best="tar"
@ -281,7 +273,7 @@ module FPM::Util
opts=[]
score=0
["--sort=name", "--mtime=@0"].each do |opt|
system("#{tar} #{opt} -cf #{testarchive} #{emptyfile} > /dev/null 2>&1")
system("#{tar} #{opt} -cf fpm-dummy.tar.tmp fpm-dummy.tmp > /dev/null 2>&1")
if $?.exitstatus == 0
opts << opt
score += 1
@ -297,9 +289,10 @@ 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
@ -332,15 +325,7 @@ module FPM::Util
def copy_entry(src, dst, preserve=false, remove_destination=false)
st = File.lstat(src)
filetype = if st.ftype == "file" && st.nlink > 1
"hardlink"
else
st.ftype
end
case filetype
case File.ftype(src)
when 'fifo'
if File.respond_to?(:mkfifo)
File.mkfifo(dst)
@ -357,24 +342,19 @@ 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.exist? dst
when 'hardlink'
# Handle hardlinks
# if the file with the same dev and inode has been copied already.
FileUtils.mkdir(dst) unless File.exists? dst
else
# 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
@ -426,33 +406,6 @@ 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.17.0"
VERSION = "1.14.1"
end

72
misc/pkgsrc.sh Executable file
View File

@ -0,0 +1,72 @@
#!/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

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

View File

@ -1,99 +0,0 @@
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,6 +10,9 @@ 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,7 +3,6 @@ require "stud/temporary"
require "fpm" # local
require "fpm/command" # local
require "fixtures/mockpackage"
require "shellwords"
describe FPM::Command do
describe "--prefix"
@ -26,27 +25,6 @@ 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") }
@ -141,132 +119,4 @@ 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,5 +1,4 @@
require "spec_setup"
require "tmpdir" # for Dir.mktmpdir
require "fpm" # local
require "fpm/package/cpan" # local
@ -38,22 +37,6 @@ 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")
@ -62,16 +45,6 @@ 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,13 +53,7 @@ describe FPM::Package::Deb do
it "should default to native" do
# Convert kernel name to debian name
expected = if native == "x86_64"
"amd64"
elsif native == "aarch64"
"arm64"
else
native
end
expected = native == "x86_64" ? "amd64" : native
expect(subject.architecture).to(be == expected)
end
end
@ -77,8 +71,8 @@ describe FPM::Package::Deb do
end
describe "priority" do
it "should default to 'optional'" do
expect(subject.attributes[:deb_priority]).to(be == "optional")
it "should default to 'extra'" do
expect(subject.attributes[:deb_priority]).to(be == "extra")
end
end
@ -132,31 +126,6 @@ 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 }
@ -413,7 +382,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 = [
@ -535,8 +504,7 @@ describe FPM::Package::Deb do
{
"bzip2" => "bz2",
"xz" => "xz",
"gz" => "gz",
"zst" => "zst"
"gz" => "gz"
}.each do |flag,suffix|
context "when --deb-compression is #{flag}" do
let(:target) { Stud::Temporary.pathname + ".deb" }
@ -575,13 +543,7 @@ 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)
#
# 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
insist { system("lintian", "-C", "symlinks", target) } == true
end
end
end

View File

@ -2,7 +2,6 @@ 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
@ -127,49 +126,6 @@ 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, :if => platform_is_darwin do
describe FPM::Package::OSXpkg 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,15 +50,16 @@ 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 name based on detected python-bin name" do
it "should prefix the package with 'python-'" do
subject.input(example_dir)
insist { subject.name } == "#{subject.attributes[:python_bin]}-Example"
insist { subject.name } == "python-Example"
end
end
@ -66,7 +67,6 @@ 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,10 +96,9 @@ describe FPM::Package::Python do
end
context "and :python_package_name_prefix is nil/default" do
it "should prefix the package based on the version of python" do
it "should prefix the package with 'python-'" do
subject.input(example_dir)
insist { subject.attributes[:python_package_name_prefix_given?] }.nil?
insist { subject.name } == "#{subject.attributes[:python_bin]}-example"
insist { subject.name } == "python-example"
end
end
@ -107,7 +106,6 @@ 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
@ -132,24 +130,9 @@ 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?
#
# 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"]
insist { subject.dependencies.sort } == ["python-dependency1 ","python-dependency2 ", "python-rtxt-dep4 "]
end
context "and :python_disable_dependency is set" do
@ -159,8 +142,7 @@ describe FPM::Package::Python do
it "it should exclude the dependency" do
subject.input(example_dir)
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-dependency2", "#{prefix}-rtxt-dep4"]
insist { subject.dependencies.sort } == ["python-dependency2 ", "python-rtxt-dep4 "]
end
end
end
@ -178,15 +160,13 @@ describe FPM::Package::Python do
it "it should prefix requirements.txt" do
subject.input(example_dir)
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-rtxt-dep1 > 0.1", "#{prefix}-rtxt-dep2 = 0.1", "#{prefix}-rtxt-dep4"]
insist { subject.dependencies.sort } == ["python-rtxt-dep1 > 0.1", "python-rtxt-dep2 = 0.1", "python-rtxt-dep4 "]
end
it "it should exclude the dependency" do
subject.attributes[:python_disable_dependency] = "rtxt-dep1"
subject.input(example_dir)
prefix = subject.attributes[:python_package_name_prefix]
insist { subject.dependencies.sort } == ["#{prefix}-rtxt-dep2 = 0.1", "#{prefix}-rtxt-dep4"]
insist { subject.dependencies.sort } == ["python-rtxt-dep2 = 0.1", "python-rtxt-dep4 "]
end
end
@ -197,120 +177,41 @@ 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 "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]
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")
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 } == [ ]
subject.attributes[:python_scripts_executable] = "fancypants"
# Newer versions of Django require Python 3.
subject.attributes[:python_bin] = "python3"
subject.input("django")
# 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,45 +484,6 @@ 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
@ -569,30 +530,6 @@ CHANGELOG
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.exist?(File.join(output_dir, '.scripts')) } == false
insist { Dir.exists?(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, :if => virtualenv_usable? do
describe FPM::Package::Virtualenv do
before do
skip("virtualenv and/or virtualenv-tools programs not found") unless virtualenv_usable?
end
@ -38,7 +38,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
activate_path = File.join(subject.build_path, '/usr/share/python/pip/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
end
it "can override the version specified on the input" do
@ -85,7 +85,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
subject.input("pip==8.1.2")
activate_path = File.join(subject.build_path, '/opt/foo/pip/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
end
end
@ -95,10 +95,10 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
subject.input("pip==8.1.2")
activate_path = File.join(subject.build_path, '/usr/share/python/pip/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
egg_path = File.join(subject.build_path, '/setup.py')
expect(File.exist?(egg_path)).to(be_truthy)
expect(File.exists?(egg_path)).to(be_truthy)
end
end
@ -118,7 +118,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
subject.input(File.join(fixtures_dir, 'requirements.txt'))
activate_path = File.join(subject.build_path, '/usr/share/python/virtualenv/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
expect(subject.name).to eq("virtualenv-virtualenv")
end
end
@ -129,7 +129,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
subject.input(File.join(fixtures_dir, 'requirements.txt'))
activate_path = File.join(subject.build_path, '/usr/share/python/virtualenv/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
expect(subject.name).to eq("virtualenv-foo")
end
@ -143,7 +143,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
activate_path = File.join(subject.staging_path, '/opt/foo/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
end
it "takes precedence over other folder options" do
@ -153,7 +153,7 @@ describe FPM::Package::Virtualenv, :if => virtualenv_usable? do
activate_path = File.join(subject.staging_path, '/opt/foo/bin/activate')
expect(File.exist?(activate_path)).to(be_truthy)
expect(File.exists?(activate_path)).to(be_truthy)
end
end
end

View File

@ -2,7 +2,6 @@ require "spec_setup"
require "fpm" # local
require "fpm/util" # local
require "stud/temporary"
require "timeout"
describe FPM::Util do
@ -99,20 +98,6 @@ 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,11 +13,6 @@ $: << 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
@ -33,6 +28,10 @@ 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 %>) <%= distribution %>; urgency=medium
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; 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 %>) <%= distribution %>; urgency=medium
<%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; urgency=medium
* Package created with FPM.
Checksums-Sha1:
<% changes_files.each do |file| -%>

View File

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