Last active
May 17, 2024 09:25
-
-
Save paddor/b03b3c500a00ed05cbb4a6c247bec900 to your computer and use it in GitHub Desktop.
Snapcraft Ruby plugin for core20
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is one way to build a snap for a Ruby app that: | |
* uses Ruby 3.3 with YJIT | |
* uses ZMQ/CZMQ, Sqlite3, libxml2, libmbedtls, libmysqlclient | |
* bundles custom fonts and wktohtmlpdf for PDF generation | |
* has a daemon (system service) which uses the 'network-bind' plug | |
* respects the env vars FOOBAR_APP (path to app) and FOOBAR_CONF (path to config file) | |
* has a separate, bundled web UI (unpacks webui-dist.tgz to /app/webui/dist inside the snap) | |
* builds the snap using an LXD container | |
HOW TO USE | |
========== | |
Put the ruby.py script into snap/plugins/ directory of your app. | |
Put the snapcraft.yml into the snap/ directory of your app. | |
Put the snapify.sh script into the root directory of your script. | |
The snapify.sh script packages the bundle (gems) into a tarball and doesn't update it unless necessary, which speeds up the snapping process. | |
Build artefacts are kept out of the final snap file to reduce size. | |
It works for our use case. Use at your own risk. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | |
# | |
# Copyright (C) 2021 Patrik Wenger <paddor@gmail.com> | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License version 3 as | |
# published by the Free Software Foundation. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
"""The ruby plugin is useful for ruby based parts. | |
It uses ruby-install to install Ruby and can use bundler to install | |
dependencies from a `Gemfile` found in the sources. | |
Additionally, this plugin uses the following plugin-specific keywords: | |
- ruby-version: | |
(string) | |
The version of ruby you want this snap to run. (e.g. '3.0' or '2.7.2') | |
- ruby-flavor: | |
(string) | |
Other flavors of ruby supported by ruby-install (e.g. 'jruby', ...) | |
- ruby-gems: | |
(list) | |
A list of gems to install. | |
- ruby-use-bundler | |
(boolean) | |
Use bundler to install gems from a Gemfile (defaults 'false'). | |
- ruby-prefix: | |
(string) | |
Prefix directory for installation (defaults '/usr'). | |
- ruby-shared: | |
(boolean) | |
Build ruby as a shared library (defaults 'false'). | |
- ruby-use-jemalloc: | |
(boolean) | |
Build ruby with libjemalloc (defaults 'false'). | |
- ruby-configure-options: | |
(array of strings) | |
Additional configure options to use when configuring ruby. | |
""" | |
import os | |
import re | |
import logging | |
from typing import Any, Dict, List, Set | |
from snapcraft.plugins.v2 import PluginV2 | |
class PluginImpl(PluginV2): | |
@classmethod | |
def get_schema(cls) -> Dict[str, Any]: | |
return { | |
"$schema": "http://json-schema.org/draft-04/schema#", | |
"type": "object", | |
"additionalProperties": False, | |
"properties": { | |
"ruby-flavor": { | |
"type": "string", | |
"default": "ruby", | |
}, | |
"ruby-version": { | |
"type": "string", | |
"default": "3.0", | |
"pattern": r"^\d+\.\d+(\.\d+)?$", | |
}, | |
"ruby-use-bundler": { | |
"type": "boolean", | |
"default": False, | |
}, | |
"ruby-prefix": { | |
"type": "string", | |
"default": "/usr", | |
}, | |
"ruby-use-jemalloc": { | |
"type": "boolean", | |
"default": False, | |
}, | |
"ruby-shared": { | |
"type": "boolean", | |
"default": False, | |
}, | |
"ruby-configure-options": { | |
"type": "array", | |
"items": {"type": "string"}, | |
"default": [], | |
}, | |
"ruby-gems": { | |
"type": "array", | |
"items": {"type": "string"}, | |
"default": [], | |
}, | |
}, | |
} | |
def get_build_snaps(self) -> Set[str]: | |
return set() | |
def get_build_packages(self) -> Set[str]: | |
packages = {"curl"} | |
if self.options.ruby_use_jemalloc: | |
packages.add("libjemalloc-dev") | |
return packages | |
def get_build_environment(self) -> Dict[str, str]: | |
env = { | |
"PATH": f"${{SNAPCRAFT_PART_INSTALL}}{self.options.ruby_prefix}/bin:${{PATH}}", | |
} | |
if self.options.ruby_shared: | |
# for finding ruby.so when running `gem` or `bundle` | |
env["LD_LIBRARY_PATH"] = f"${{SNAPCRAFT_PART_INSTALL}}{self.options.ruby_prefix}/lib${{LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}}" | |
return env | |
def _configure_opts(self) -> List[str]: | |
configure_opts = [ | |
"--without-baseruby", | |
"--enable-load-relative", | |
"--disable-install-doc", | |
] + self.options.ruby_configure_options | |
if self.options.ruby_shared: | |
configure_opts.append("--enable-shared") | |
if self.options.ruby_use_jemalloc: | |
configure_opts.append("--with-jemalloc") | |
return configure_opts | |
def get_build_commands(self) -> List[str]: | |
# NOTE: To update ruby-install version, go to https://github.com/postmodern/ruby-install/tags | |
ruby_install_version = '0.9.3' | |
# NOTE: To update SHA256 checksum, run the following command (with updated version) and copy the output (one line) here: | |
# curl -L https://github.com/postmodern/ruby-install/archive/refs/tags/v0.9.3.tar.gz -o ruby-install.tar.gz && sha256sum --tag ruby-install.tar.gz | |
ruby_install_checksum = 'SHA256 (ruby-install.tar.gz) = 3e920c9c770ce040cdf71cc64b809e861b613c570c6113ee61ab1d2885a16ab3' | |
configure_opts = ' '.join(self._configure_opts()) | |
commands = ['uname -a', 'env'] | |
# NOTE: Download and verify ruby-install and use it to download, compile, and install Ruby | |
commands.append(f"curl -L --proto '=https' --tlsv1.2 https://github.com/postmodern/ruby-install/archive/refs/tags/v{ruby_install_version}.tar.gz -o ruby-install.tar.gz") | |
commands.append("echo 'Checksum of downloaded file:'") | |
commands.append("sha256sum --tag ruby-install.tar.gz") | |
commands.append("echo 'Checksum is correct if it matches:'") | |
commands.append(f"echo '{ruby_install_checksum}'") | |
commands.append(f"echo '{ruby_install_checksum}' | sha256sum --check --strict") | |
commands.append("tar xfz ruby-install.tar.gz") | |
commands.append(f"ruby-install-{ruby_install_version}/bin/ruby-install --src-dir ${{SNAPCRAFT_PART_SRC}} --install-dir ${{SNAPCRAFT_PART_INSTALL}}{self.options.ruby_prefix} --package-manager apt --jobs=${{SNAPCRAFT_PARALLEL_BUILD_COUNT}} {self.options.ruby_flavor}-{self.options.ruby_version} -- {configure_opts}") | |
# NOTE: Update bundler and avoid conflicts/prompts about replacing bundler | |
# executables by removing them first. | |
commands.append(f"rm -f ${{SNAPCRAFT_PART_INSTALL}}{self.options.ruby_prefix}/bin/{{bundle,bundler}}") | |
commands.append("gem update --system") | |
commands.append("gem install --env-shebang --no-document bundler") | |
if self.options.ruby_use_bundler: | |
commands.append("bundle") | |
if self.options.ruby_gems: | |
commands.append("gem install --env-shebang --no-document {}".format(' '.join(self.options.ruby_gems))) | |
return commands |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: foobar | |
base: core20 | |
version: '0.1.0' | |
summary: Foobar example snapcraft app | |
description: | | |
Foobar example snapcraft app with daemon | |
grade: stable | |
confinement: strict | |
parts: | |
ruby: | |
plugin: ruby | |
ruby-version: '3.3' | |
ruby-shared: true | |
build-packages: | |
- zlib1g-dev | |
- libssl-dev | |
- rustc | |
prime: | |
- -usr/bin/bundler | |
- -usr/bin/erb | |
- -usr/bin/racc | |
- -usr/bin/rake | |
- -usr/bin/rbs | |
- -usr/bin/rdoc | |
- -usr/bin/ri | |
- -usr/bin/typeprof | |
- -usr/include | |
- -usr/lib/ruby/gems/*/doc | |
- -usr/share | |
- -usr/share/man | |
- -usr/usr/share/doc | |
wkhtmltopdf: | |
source: https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb | |
source-type: deb | |
plugin: dump | |
stage-packages: | |
- fontconfig | |
- libjpeg8 | |
- libpng16-16 | |
- libx11-6 | |
- libxrender1 | |
- libssl1.1 | |
- xfonts-75dpi | |
- xfonts-base | |
- zlib1g | |
override-stage: | | |
snapcraftctl stage | |
ln -sf ../local/bin/wkhtmltopdf $SNAPCRAFT_STAGE/usr/bin/wkhtmltopdf | |
stage: | |
# remove original config file | |
- -etc/fonts/fonts.conf | |
prime: | |
- etc/fonts | |
# - usr/bin/fc-list | |
- usr/bin/wkhtmltopdf | |
- usr/local/bin/wkhtmltopdf | |
- usr/lib | |
- usr/share/fonts/truetype | |
- lib | |
fontconfig: | |
after: | |
- wkhtmltopdf | |
source: snap/local | |
plugin: dump | |
organize: | |
# use our own config file | |
fonts.conf: etc/fonts/ | |
app: | |
source: . | |
plugin: dump | |
organize: | |
bin: app/bin | |
lib: app/lib | |
conf: app/conf | |
conf.rb.template: app/ | |
Gemfile*: app/ | |
stage: | |
- app | |
bundle: | |
after: | |
- ruby | |
- app | |
plugin: dump | |
source: vendor_cache.tar.gz | |
build-packages: | |
- autoconf | |
- automake | |
- git | |
- cmake | |
- pkg-config | |
- libczmq-dev | |
- libsqlite3-dev | |
- libmysqlclient-dev | |
- libxml2-dev | |
- python3 | |
- python3-pip | |
- libmbedtls-dev | |
stage-packages: | |
- libsqlite3-dev | |
- libmysqlclient-dev | |
- libczmq-dev | |
- libxml2-dev | |
- libmbedtls-dev | |
override-build: | | |
env | |
pip install conan | |
command -v conan | |
command -v bundle | |
bundle version | |
cd $SNAPCRAFT_STAGE/app | |
bundle config set --local deployment true | |
bundle config set --local path $SNAPCRAFT_PART_INSTALL/app/vendor/bundle | |
bundle config set --local cache_path $SNAPCRAFT_PART_SRC | |
bundle config set --local silence_root_warning true | |
bundle config set --local jobs 1 | |
bundle env | |
if [ -e $SNAPCRAFT_PART_SRC/../cache/bundle ] | |
then | |
mkdir -p $SNAPCRAFT_PART_INSTALL/app/vendor/ | |
cp -R $SNAPCRAFT_PART_SRC/../cache/bundle $SNAPCRAFT_PART_INSTALL/app/vendor/ | |
fi | |
if bundle check | |
then | |
echo "Bundle is already installed." | |
else | |
echo "Installing bundle ..." | |
bundle install --local | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.gem' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.a' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.o' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name 'gem_make.out' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.h' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.c' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.md' -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name 'test' -type d -print0 | xargs -0 rm -rf | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name 'spec' -type d -print0 | xargs -0 rm -rf | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name 'configure' -type f -delete | |
find $SNAPCRAFT_PART_INSTALL/app/vendor/bundle -name '*.log' -type f -delete | |
mkdir -p $SNAPCRAFT_PART_SRC/../cache | |
cp -R $SNAPCRAFT_PART_INSTALL/app/vendor/bundle $SNAPCRAFT_PART_SRC/../cache/ | |
fi | |
prime: | |
- -usr/include | |
- -usr/share | |
- -usr/lib/x86_64-linux-gnu/*.a | |
webui: | |
source: webui-dist.tgz | |
plugin: dump | |
organize: | |
'*': app/webui/dist/ | |
apps: | |
foobar: | |
command: usr/bin/bundle exec $SNAP/app/bin/main | |
plugs: | |
- network-bind | |
environment: | |
BUNDLE_GEMFILE: $SNAP/app/Gemfile | |
FOOBAR_APP: $SNAP/app | |
FOOBAR_CONF: $SNAP_COMMON/conf.rb | |
BUNDLE_PATH: $SNAP/app/vendor/bundle | |
daemon: | |
command: usr/bin/bundle exec $SNAP/app/bin/main start | |
daemon: simple | |
restart-condition: always | |
restart-delay: 15s | |
plugs: | |
- network-bind | |
environment: | |
BUNDLE_GEMFILE: $SNAP/app/Gemfile | |
RUBYOPT: --disable-did_you_mean | |
FOOBAR_APP: $SNAP/app | |
FOOBAR_CONF: $SNAP_COMMON/conf.rb | |
BUNDLE_PATH: $SNAP/app/vendor/bundle | |
XDG_DATA_HOME: $SNAP | |
XDG_CONFIG_HOME: $SNAP | |
FONTCONFIG_FILE: $SNAP/etc/fonts/fonts.conf | |
# wkhtmltopdf: | |
# command: usr/local/bin/wkhtmltopdf | |
# environment: | |
# XDG_DATA_HOME: $SNAP | |
# XDG_CONFIG_HOME: $SNAP | |
# FONTCONFIG_FILE: $SNAP/etc/fonts/fonts.conf | |
# | |
# fc-list: | |
# command: usr/bin/fc-list | |
# environment: | |
# XDG_DATA_HOME: $SNAP | |
# XDG_CONFIG_HOME: $SNAP | |
# FONTCONFIG_FILE: $SNAP/etc/fonts/fonts.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/sh -e | |
usage() | |
{ | |
cat << HEREDOC | |
Usage: $0 | |
optional arguments: | |
-h, --help show this help message and exit | |
HEREDOC | |
} | |
while true; do | |
case "$1" in | |
-h | --help ) | |
usage; exit;; | |
-- ) | |
shift; break ;; | |
* ) | |
break ;; | |
esac | |
done | |
BUNDLE_CACHE_ALL=1 | |
BUNDLE_CACHE_ALL_PLATFORMS=1 | |
BUNDLE_NO_INSTALL=1 | |
export BUNDLE_CACHE_ALL | |
export BUNDLE_CACHE_ALL_PLATFORMS | |
export BUNDLE_NO_INSTALL | |
bundle check | |
bundle package | |
tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2022-12-19' -c vendor/cache | gzip -n > vendor_cache_new.tar.gz | |
if diff -q vendor_cache.tar.gz vendor_cache_new.tar.gz | |
then | |
echo "vendor/cache hasn't changed. Leaving vendor_cache.tar.gz as is." | |
rm -f vendor_cache_new.tar.gz | |
else | |
echo "vendor/cache has changed. Updating vendor_cache.tar.gz ..." | |
mv vendor_cache_new.tar.gz vendor_cache.tar.gz | |
fi | |
snapcraft --use-lxd --debug |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment