Skip to content

Commit

Permalink
bundler: Add upstream documentation
Browse files Browse the repository at this point in the history
- update main README.md
- add new document for bundler

Signed-off-by: Michal Šoltis <[email protected]>
  • Loading branch information
slimreaper35 committed Oct 18, 2024
1 parent 2e49721 commit df6a464
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 1 deletion.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@ Supported:
* [pip](#pip)
* [npm](#npm)
* [yarn](#yarn)
* [bundler](#bundler)

Planned:

* dnf
* cargo
* bundler

*Based on the [supported package managers](https://github.com/containerbuildsystem/cachito#package-managers) in the
original Cachito.*
Expand Down Expand Up @@ -223,6 +223,20 @@ prior to pointing cachi2 to your project.

See [docs/yarn.md](docs/yarn.md) for more details.

### bundler

<https://bundler.io/>

Cachi2 supports bundler by parsing the [Gemfile.lock](https://bundler.io/guides/using_bundler_in_applications.html#gemfilelock)
file present in the source repository and downloading the declared dependencies.

To generate a lockfile or to make sure the file is up to date, you can use
for example the `bundle lock` command, which generates the `Gemfile.lock` file based
on the dependencies specified in the [Gemfile](https://bundler.io/v2.5/man/gemfile.5.html).
Both files must be present in the source repository so you should check them into your git repository.

See [docs/bundler.md](docs/bundler.md) for more details.

## Project status

Cachi2 was derived (but is not a direct fork) from [Cachito](https://github.com/containerbuildsystem/cachito) and is
Expand Down
174 changes: 174 additions & 0 deletions docs/bundler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Bundler

<https://bundler.io/>

## Prerequisites

To use Cachi2 with Bundler locally, ensure you have Ruby and Bundler installed
on your system.

```bash
sudo dnf install rubygem-bundler
```

Then ensure you have both, **Gemfile** and **Gemfile.lock** in your project
directory. We parse the **Gemfile.lock** to pre-fetch all dependencies
specified in that file.

## Basic usage

Run the following command in your terminal to pre-fetch your project's
dependencies. The command will download all dependencies specified in the
**Gemfile.lock** to the specified output directory.

```bash
cd path-to-your-ruby-project
cachi2 fetch-deps bundler
```

In addition, it will prepare the necessary environment variables and
configuration files for the build phase. See the following sections for more
information.

### Gems

Each gem has a name, version, and platform. If the platform is "ruby", it means
that it should work on any platform Ruby runs on. Using the ruby platform means
ignoring the current machine's platform and installing only ruby platform gems.
As a result, gems with native extensions will be compiled from the source.

However, occasionally some gems do not have a version for the ruby platform and
are only available as pre-compiled binaries. In this case, you may need to enable
the pre-fetching of gems for specific platforms to avoid potential failures with
the `allow_binary` option set to `true` when running the `fetch-deps` command.

```bash
cd path-to-your-ruby-project
cachi2 fetch-deps '{"type": "bundler", "allow_binary": "true"}'
```

By default, the `allow_binary` option is disabled.

## Configuration

[Bundler](https://bundler.io/v2.5/man/bundle-config.1.html#DESCRIPTION) uses
an unorthodox system when dealing with configuration options. The highest
precedence is given to the config file, and then to the environment variables.
This is a current limitation of Bundler, that we had to work around. We may
drop the workaround if this ends up being addressed in future Bundler releases.

The order of precedence for Bundler configuration options is as follows:

1. Local config (`<project_root>/.bundle/config or $BUNDLE_APP_CONFIG/config`)
2. Environment variables (ENV)
3. Global config (`~/.bundle/config`)
4. Bundler default config

We set the following configuration options to ensure that the build process
works correctly:

- BUNDLE_CACHE_PATH: "${output_dir}/deps/bundler"
- BUNDLE_DEPLOYMENT: "true"
- BUNDLE_NO_PRUNE: "true"
- BUNDLE_VERSION: "system"

Here is a brief description of each configuration option taken from the
[Bundler documentation](https://bundler.io/v2.5/man/bundle-config.1.html#LIST-OF-AVAILABLE-KEYS):

### BUNDLE_CACHE_PATH

The directory that Bundler will place cached gems in when running `bundle package`,
and that Bundler will look in when installing gems. Defaults to `vendor/cache`.

### BUNDLE_DEPLOYMENT

Disallow changes to the **Gemfile**. When the **Gemfile** is changed and the lockfile
has not been updated, running Bundler commands will be blocked.

### BUNDLE_NO_PRUNE

Whether Bundler should leave outdated gems unpruned when caching.

### BUNDLE_VERSION

The version of Bundler to use when running under the Bundler environment. Defaults
to lockfile. You can also specify system or x.y.z. lockfile will use the Bundler
version specified in the Gemfile.lock, system will use the system version of
Bundler and x.y.z will use the specified version of Bundler.

**Note**: _A prefetch could fail when Bundler versions differ between the build system and
lockfile and when the former is outdated. Therefore we do not recommend using
mismatching or outdated versions of Bundler in build systems as this might result
in unexpected failures._

To create the configuration file, run the following command.

```bash
cachi2 inject-files --for-output-dir /tmp/cachi2-output cachi2-output
```

You should see a log message that the file was created successfully.
Lastly, you need to set the `BUNDLE_APP_CONFIG` environment variable to point
to the copied configuration file.

```bash
cachi2 generate-env --output ./cachi2.env --for-output-dir /tmp/cachi2-output ./cachi2-output
```

```bash
# cat cachi2.env
export BUNDLE_APP_CONFIG=/tmp/cachi2-output/bundler/config_override
```

The generated environment file should be sourced before running any Bundler command.

### Limitations

Since the local configuration takes higher precedence than the environment
variables (except `BUNDLE_APP_CONFIG`), we copy the configuration file and
overwrite the environment variables above. Then, we change the
`BUNDLE_APP_CONFIG` environment variable to point to the new configuration file.

It should not affect the build process unless you have multiple packages in
your repository with different configuration settings. In that case, you may
have to adjust the build phase accordingly.

## Hermetic build

After using the `fetch-deps`, `inject-files`, and `generate-env` commands
to set up the directory, building the Dockerfile will produce a container with
the application fully compiled without any network access. The build will be
hermetic and reproducible.

```Dockerfile
FROM docker.io/library/ruby:latest

WORKDIR /app

COPY Gemfile .
COPY Gemfile.lock .

...

RUN . /tmp/cachi2.env && bundle install

...
```

Assuming `cachi2-output` and `cachi2.env` are in the same directory as the
Dockerfile, build the image with the following command:

```bash
podman build . \
--volume "$(realpath ./cachi2-output)":/tmp/cachi2-output:Z \
--volume "$(realpath ./cachi2.env)":/tmp/cachi2.env:Z \
--network none \
--tag my-ruby-app
```

## Unsupported features

- checksum validation (blocked by pending official support)
- downloading the Bundler version specified in the **Gemfile.lock**
- reporting development dependencies
- plugins

0 comments on commit df6a464

Please sign in to comment.