Compare commits

...

4 Commits

Author SHA1 Message Date
Jordan Sissel d590cc3e18 Use correct constant name 2021-11-09 16:42:31 -08:00
seph d4746c105a Skip tests which cannot be run due to missing dependencies.
Also fix at a failing deb lintian test which was failing due to a
missing 'lsb-base' dependency
2021-11-09 16:37:19 -08:00
seph 958e82ffed Add documentation for running fpm from docker
Also add documentation for running the fpm test suite in docker.

From #1681
2021-11-09 16:37:19 -08:00
seph 16ba27251e fpm tests can now be run through docker.
This changes the Dockerfile to create docker images suitable for running tests (fpm rspec suite in docker) and also as a normal release (using fpm through docker).

Fixes #1682, #1453
2021-11-09 16:37:19 -08:00
8 changed files with 210 additions and 39 deletions

3
.gitignore vendored
View File

@ -42,3 +42,6 @@ Gemfile.lock
docs/_build
docs/.work
.docker-test-everything
.docker-test-minimal

View File

@ -21,6 +21,7 @@ sbuss
Brett Gailey (github: dnbert)
Daniel Haskin (github: djhaskin987)
Richard Grainger (github: liger1978)
seph (github: directionless)
If you have contributed (bug reports, feature requests, help in IRC, blog
posts, code, etc) and aren't listed here, please let me know if you wish to be

View File

@ -1,17 +1,94 @@
#
# To build this Docker image: docker build -t fpm .
#
# To run this Docker container interactively: docker run -it fpm
#
FROM alpine:3.12
# Are we running against the minimal container, or the everything
# container? Minimal is mostly the compiled package tools. Everything
# pulls in scripting langauges.
ARG BASE_ENV=everything
RUN apk add --no-cache \
ruby \
ruby-dev \
ruby-etc \
gcc \
libffi-dev \
make \
libc-dev \
rpm \
&& gem install --no-document fpm
# Are we running tests, or a release? Tests build and run against the
# CWD, where release will use the downloaded gem.
ARG TARGET=test
# Container to throw an error if called with a bare `docker build .`
FROM ubuntu:18.04 as error
RUN echo "\n\n\nHey! Use buildkit. See the Makefile or docs\n\n\n"
RUN false
# Base container is used for various release and test things
FROM ubuntu:18.04 as minimal-base
# Runtime deps. Build deps go in the build or test containers
RUN apt-get update \
&& apt-get -y dist-upgrade \
&& apt-get install --no-install-recommends -y \
ruby rubygems rubygems-integration \
bsdtar \
cpio \
debsigs \
pacman \
rpm \
squashfs-tools \
xz-utils \
zip \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
RUN adduser fpm
# everything container includes all the scripting languages. These
# greatly embiggen the underlying docker container, so they're
# conditionalized.
FROM minimal-base AS everything-base
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
cpanminus \
npm \
perl \
python3-pip \
&& pip3 --no-cache-dir install setuptools \
&& pip3 --no-cache-dir install wheel \
&& pip3 --no-cache-dir install virtualenv virtualenv-tools3 \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
&& rm -rf /var/lib/apt/lists/*
# Run tests against the current working directory. This is a bit
# orthogonal to the container release process, but it has a lot of
# same dependancies, so we reuse it. This uses COPY to allow rspect to
# initall the gems, but runtime usage expects you to mount a volume
# into /src
FROM ${BASE_ENV}-base AS test
WORKDIR /src
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
gcc make ruby-dev libc-dev lintian git
# installing ffi here is a bit of an optimization for how COPY and layer reuse works
RUN gem install --no-ri --no-rdoc ffi
RUN install -d -o fpm /origsrc
COPY --chown=fpm . /origsrc
ENV HOME=/origsrc
ENV BUNDLE_PATH=/origsrc/.bundle
# Install a specific version of bundler
WORKDIR /origsrc
RUN gem install -v "$(grep -A1 '^BUNDLED WITH' Gemfile.lock | tail -1)" bundler
USER fpm
RUN bundle install
CMD bundle exec rspec
# build a container from a released gem. install build deps here, so
# we can omit them from the final release package
FROM ${BASE_ENV}-base AS build
RUN apt-get update
RUN apt-get install --no-install-recommends -y \
gcc make ruby-dev libc-dev
ENV GEM_PATH /fpm
ENV PATH "/fpm/bin:${PATH}"
RUN gem install --no-ri --no-rdoc --install-dir=/fpm fpm
FROM build as release
COPY --from=build /fpm /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.
FROM ${TARGET} as final

View File

@ -52,3 +52,18 @@ clean:
publish-docs:
$(MAKE) -C docs publish
# Testing in docker.
# The dot file is a sentinal file that will built a docker image, and tag it.
# The normal make target runs said image, mounting CWD against it.
SECONDARY: .docker-test-minimal .docker-test-everything
.docker-test-%: Gemfile.lock fpm.gemspec Dockerfile
DOCKER_BUILDKIT=1 docker build -t fpm-test-$* --build-arg BASE_ENV=$* --build-arg TARGET=test .
touch "$@"
docker-test-%: .docker-test-%
docker run -v `pwd`:/src fpm-test-$*
docker-release-%:
DOCKER_BUILDKIT=1 docker build -t fpm --build-arg BASE_ENV=$* --build-arg TARGET=release --squash .

70
docs/docker.rst Normal file
View File

@ -0,0 +1,70 @@
FPM and Docker
==============
Because fpm depends on so many underlying system tools, docker can
alleviate the need to install them locally.
An end user may use a docker container in lieu of installing
locally. And a developer can use docker to run the test suite.
Running FPM inside docker
-------------------------
First, build a container will all the dependencies::
make docker-release-everything
Now, run it as you would the fpm command. Note that you will have to
mount your source directly into the docker volume::
docker run -v $(pwd):/src fpm --help
As a full example::
mkdir /tmp/fpm-test
mkdir /tmp/fpm-test/files
touch /tmp/fpm-test/files/one
touch /tmp/fpm-test/files/two
docker run -v /tmp/fpm-test/files:/src -v /tmp/fpm-test:/out fpm -s dir -t tar -n example -p /out/out.tar .
tar tf /tmp/fpm-test/out.tar
Depending on your needs, you will have to adjust the volume mounts and
relative paths to fit your particular situation.
Running rpsec inside docker
---------------------------
The Makefile provides some targets for testing. They will build a
docker container with the dependencies, and then invoked `rspec`
inside it. The makefile uses a sentinel file to indicate that the
docker image has been build, and can be reused.
make docker-test-everything
How does this work
------------------
The Dockerfile makes heavy use of multistage
builds. This allows the various output containers to build on the same
earlier stages.
There are two ``base`` images. A ``minimal`` image, which contains
compiled dependencies and ruby. And an ``everything`` image which brings
in scripting systems like ``python`` and ``perl``. These are split to
allow a smaller ``minimal`` image in cases where building scripting
language packages are not needed.
The Dockerfile the argument ``BASE_ENV`` to specify what base image to
use. This can be set to either ``minimal`` or ``everything``. If
unspecified, it defaults to ``everything``
We want to use the same set of base images for both the ``rspec``
testing, as well as the run time containerization. We do this by using
the ``TARGET`` argument to select which container to build.
The makefile encodes this logic with two pattern rules.

View File

@ -467,11 +467,11 @@ describe FPM::Package::Deb do
end # after
it "it should output bit-for-bit identical packages" do
lamecmds = []
lamecmds << "ar" if not ar_cmd_deterministic?
lamecmds << "tar" if not tar_cmd_supports_sort_names_and_set_mtime?
if not lamecmds.empty?
skip("fpm searched for variants of #{lamecmds.join(", ")} that support(s) deterministic archives, but found none, so can't test reproducibility.")
cmds = []
cmds << "ar" if not ar_cmd_deterministic?
cmds << "tar" if not tar_cmd_supports_sort_names_and_set_mtime?
if not cmds.empty?
skip("fpm searched for variants of [#{cmds.join(", ")}] that support(s) deterministic archives, but found none, so can't test reproducibility.")
return
end

View File

@ -182,7 +182,8 @@ describe FPM::Package::Python do
context "python_scripts_executable is set" do
it "should have scripts with a custom hashbang line" do
pending("Disabled on travis-ci because it always fails, and there is no way to debug it?") if is_travis
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")
subject.attributes[:python_scripts_executable] = "fancypants"
# Newer versions of Django require Python 3.

View File

@ -2,10 +2,14 @@ require "spec_setup"
require "fpm" # local
require "English" # for $CHILD_STATUS
is_old_ruby = (RUBY_VERSION =~ /^((1\.)|(2\.0))/)
IS_OLD_RUBY = (RUBY_VERSION =~ /^((1\.)|(2\.0))/)
describe FPM::Package::Snap do
let(:target) { Stud::Temporary.pathname + ".snap" }
before do
skip("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
skip("Missing program 'mksquashfs'") unless program_exists?("mksquashfs")
end
after do
subject.cleanup
File.unlink(target) if File.exist?(target)
@ -21,7 +25,7 @@ describe FPM::Package::Snap do
end
it "should have a default output usable as a filename" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
# This is the default filename commonly produced by snapcraft
insist { subject.to_s } == "name_123-100_all.snap"
end
@ -32,7 +36,7 @@ describe FPM::Package::Snap do
end
it "should not include iteration if it is nil" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
# This is the default filename commonly produced by snapcraft
expect(subject.to_s).to(be == "name_123_all.snap")
end
@ -89,32 +93,32 @@ describe FPM::Package::Snap do
end
it "should have the correct name" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.name } == original.name
end
it "should have the correct version" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.version } == original.version
end
it "should have the correct description" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.description } == original.description
end
it "should have the correct architecture" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.architecture } == original.architecture
end
it "should have the correct apps" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.attributes[:snap_apps] } == original.attributes[:snap_apps]
end
it "should have the correct hooks" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.attributes[:snap_hooks] } == original.attributes[:snap_hooks]
end
end
@ -143,38 +147,38 @@ describe FPM::Package::Snap do
end
it "should have the custom name" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.name } == "custom-name"
end
it "should have the custom version" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.version } == "custom-version"
end
it "should have the custom description" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.description } == "custom-summary\ncustom-description"
end
it "should have the custom architecture" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.architecture } == "custom-architecture"
end
it "should have the custom apps" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.attributes[:snap_apps] } == []
end
it "should have the custom hooks" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
insist { input.attributes[:snap_hooks] } == []
end
end
it "should support specifying confinement" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
original.attributes[:snap_confinement] = "test-confinement"
original.output(target)
@ -184,7 +188,7 @@ describe FPM::Package::Snap do
end
it "should support specifying grade" do
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if is_old_ruby
pending("Ruby 1.x and 2.0.x are unsupported for Snap because it lacks Psych::safe_load") if IS_OLD_RUBY
original.attributes[:snap_grade] = "test-grade"
original.output(target)