Compare commits
4 Commits
main
...
issue/1884
| Author | SHA1 | Date |
|---|---|---|
|
|
1e52128a82 | |
|
|
8e811bc8a4 | |
|
|
14200c7604 | |
|
|
8a1dfb0f48 |
|
|
@ -0,0 +1,8 @@
|
|||
workflow "New workflow" {
|
||||
on = "push"
|
||||
resolves = ["GitHub Action for Docker"]
|
||||
}
|
||||
|
||||
action "GitHub Action for Docker" {
|
||||
uses = "actions/docker/cli@76ff57a"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
129
Dockerfile
129
Dockerfile
|
|
@ -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
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
12
docs/conf.py
12
docs/conf.py
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>`_
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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``
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---------------------
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
sphinx_rtd_theme
|
||||
# https://blog.readthedocs.com/build-errors-docutils-0-18/
|
||||
docutils<0.18
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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) .
|
||||
|
|
@ -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
|
||||
|
|
@ -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) .
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
13
fpm.gemspec
13
fpm.gemspec
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
module FPM
|
||||
VERSION = "1.17.0"
|
||||
VERSION = "1.14.1"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[project]
|
||||
name = "example"
|
||||
version = "1.2.3"
|
||||
authors = [ { name = "Captain Fancy", email = "foo@example.com" } ]
|
||||
|
|
@ -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/
|
||||
|
|
@ -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"',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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| -%>
|
||||
|
|
|
|||
|
|
@ -260,4 +260,4 @@ fi
|
|||
<% end -%>
|
||||
|
||||
%changelog
|
||||
<%= changelog %>
|
||||
<%= attributes[:rpm_changelog] %>
|
||||
|
|
|
|||
Loading…
Reference in New Issue