class RubyPlugin(BasePlugin): @classmethod def schema(cls): schema = super().schema() schema['properties']['use-bundler'] = { 'type': 'boolean', 'default': False } schema['properties']['ruby-version'] = { 'type': 'string', 'default': '2.4.2' } schema['properties']['gems'] = { 'type': 'array', 'minitems': 1, 'uniqueItems': True, 'items': { 'type': 'string' }, 'default': [] } return schema @classmethod def get_pull_properties(cls): # Inform Snapcraft of the properties associated with pulling. If these # change in the YAML Snapcraft will consider the build step dirty. return ['ruby-version', 'gems', 'use-bundler'] def __init__(self, name, options, project): super().__init__(name, options, project) # Beta Warning # Remove this comment and warning once ruby plugin is stable. logger.warn("The ruby plugin is currently in beta, " "its API may break. Use at your own risk") self._ruby_version = options.ruby_version self._ruby_part_dir = os.path.join(self.partdir, 'ruby') self._ruby_download_url = \ 'https://cache.ruby-lang.org/pub/ruby/ruby-{}.tar.gz'.format( self._ruby_version) self._ruby_tar = Tar(self._ruby_download_url, self._ruby_part_dir) self._gems = options.gems or [] self.build_packages.extend([ 'gcc', 'g++', 'make', 'zlib1g-dev', 'libssl-dev', 'libreadline-dev' ]) def snap_fileset(self): fileset = super().snap_fileset() fileset.append('-include/') fileset.append('-share/') return fileset def pull(self): super().pull() os.makedirs(self._ruby_part_dir, exist_ok=True) logger.info('Fetching ruby {}...'.format(self._ruby_version)) self._ruby_tar.download() logger.info('Building/installing ruby...') self._ruby_install(builddir=self._ruby_part_dir) self._gem_install() if self.options.use_bundler: self._bundle_install() def env(self, root): env = super().env(root) for key, value in self._env_dict(root).items(): env.append('{}="{}"'.format(key, value)) return env def _env_dict(self, root): env = dict() rubydir = os.path.join(root, 'lib', 'ruby') # Patch versions of ruby continue to use the minor version's RUBYLIB, # GEM_HOME, and GEM_PATH. Fortunately there should just be one, so we # can detect it by globbing instead of trying to determine what the # minor version is programatically versions = glob.glob(os.path.join(rubydir, 'gems', '*')) # Before Ruby has been pulled/installed, no versions will be found. # If that's the case, we won't define any Ruby-specific variables yet if len(versions) == 1: ruby_version = os.path.basename(versions[0]) rubylib = os.path.join(rubydir, ruby_version) # Ruby uses some pretty convoluted rules for determining its # arch-specific RUBYLIB. Rather than try and duplicate that logic # here, let's just look for a file that we know is in there: # rbconfig.rb. There should only be one. paths = glob.glob(os.path.join(rubylib, '*', 'rbconfig.rb')) if len(paths) != 1: raise SnapcraftEnvironmentError( 'Expected a single rbconfig.rb, but found {}'.format( len(paths))) env['RUBYLIB'] = '{}:{}'.format(rubylib, os.path.dirname(paths[0])) env['GEM_HOME'] = os.path.join(rubydir, 'gems', ruby_version) env['GEM_PATH'] = os.path.join(rubydir, 'gems', ruby_version) elif len(versions) > 1: raise SnapcraftEnvironmentError( 'Expected a single Ruby version, but found {}'.format( len(versions))) return env def _run(self, command, **kwargs): """Regenerate the build environment, then run requested command. Without this function, the build environment would not be regenerated and thus the newly installed Ruby would not be discovered. """ env = os.environ.copy() env.update(self._env_dict(self.installdir)) self.run(command, env=env, **kwargs) def _ruby_install(self, builddir): self._ruby_tar.provision(builddir, clean_target=False, keep_tarball=True) self._run(['./configure', '--disable-install-rdoc', '--prefix=/'], cwd=builddir) self._run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self._run(['make', 'install', 'DESTDIR={}'.format(self.installdir)], cwd=builddir) # Fix all shebangs to use the in-snap ruby file_utils.replace_in_file(self.installdir, re.compile(r''), re.compile(r'^#!.*ruby'), r'#!/usr/bin/env ruby') def _gem_install(self): if self.options.use_bundler: self._gems = self._gems + ['bundler'] if self._gems: logger.info('Installing gems...') gem_install_cmd = [ os.path.join(self.installdir, 'bin', 'ruby'), os.path.join(self.installdir, 'bin', 'gem'), 'install', '--env-shebang' ] self._run(gem_install_cmd + self._gems) def _bundle_install(self): bundle_install_cmd = [ os.path.join(self.installdir, 'bin', 'ruby'), os.path.join(self.installdir, 'bin', 'bundle'), 'install' ] self._run(bundle_install_cmd)
class NginxPlugin(BasePlugin): @classmethod def schema(cls): schema = super().schema() schema['properties']['nginx-version'] = { 'type': 'string', 'default': '1.14.0' } schema['properties']['pcre-version'] = { 'type': 'string', 'default': '8.42' } schema['properties']['zlib-version'] = { 'type': 'string', 'default': '1.2.11' } schema['properties']['openssl-version'] = { 'type': 'string', 'default': '1.1.0h' } schema['properties']['use-passenger'] = { 'type': 'boolean', 'default': False } return schema @classmethod def get_pull_properties(cls): # Inform Snapcraft of the properties associated with pulling. If these # change in the YAML Snapcraft will consider the build step dirty. return [ 'nginx-version', 'pcre-version', 'zlib-version', 'openssl-version' ] def __init__(self, name, options, project): super().__init__(name, options, project) # Beta Warning # Remove this comment and warning once nginx plugin is stable. logger.warn("The nginx plugin is currently in beta, " "its API may break. Use at your own risk") # NGINX bits self._nginx_download_url = \ 'http://nginx.org/download/nginx-{}.tar.gz'.format( self.options.nginx_version) self._nginx_part_dir = os.path.join(self.partdir, 'nginx') self._nginx_tar = Tar(self._nginx_download_url, self._nginx_part_dir) # PCRE self._pcre_download_url = \ 'https://svwh.dl.sourceforge.net/project/pcre/pcre/{0}/pcre-{0}.tar.gz'.format( self.options.pcre_version) self._pcre_part_dir = os.path.join(self.partdir, 'pcre') self._pcre_tar = Tar(self._pcre_download_url, self._pcre_part_dir) # OPENSSL self._openssl_download_url = \ 'http://www.openssl.org/source/openssl-{}.tar.gz'.format( self.options.openssl_version) self._openssl_part_dir = os.path.join(self.partdir, 'openssl') self._openssl_tar = Tar(self._openssl_download_url, self._openssl_part_dir) # ZLIB self._zlib_download_url = \ 'http://zlib.net/zlib-{}.tar.gz'.format( self.options.zlib_version) self._zlib_part_dir = os.path.join(self.partdir, 'zlib') self._zlib_tar = Tar(self._zlib_download_url, self._zlib_part_dir) # PASSENGER if self.options.use_passenger: self._passenger_download_url = \ 'https://www.phusionpassenger.com/latest_stable_tarball' self._passenger_part_dir = os.path.join(self.partdir, 'passenger') self._passenger_tar = Tar(self._passenger_download_url, self._passenger_part_dir) self.build_packages.extend( ['gcc', 'g++', 'make', 'ruby-dev', 'libcurl4-openssl-dev']) def snap_fileset(self): fileset = super().snap_fileset() fileset.append('-include/') fileset.append('-share/') return fileset def pull(self): super().pull() # PCRE os.makedirs(self._pcre_part_dir, exist_ok=True) self._pcre_tar.download() self._pcre_install(builddir=self._pcre_part_dir) # ZLIB os.makedirs(self._zlib_part_dir, exist_ok=True) self._zlib_tar.download() self._zlib_install(builddir=self._zlib_part_dir) # OPENSSL os.makedirs(self._openssl_part_dir, exist_ok=True) self._openssl_tar.download() self._openssl_install(builddir=self._openssl_part_dir) # PASSENGER if self.options.use_passenger: os.makedirs(self._passenger_part_dir, exist_ok=True) self._passenger_tar.download() self._passenger_install(builddir=self._passenger_part_dir) # NGINX os.makedirs(self._nginx_part_dir, exist_ok=True) self._nginx_tar.download() self._nginx_install(builddir=self._nginx_part_dir) def env(self, root): env = super().env(root) env.append('PATH={}:{}'.format(os.path.join(root, 'bin'), os.environ['PATH'])) return env def _pcre_install(self, builddir): self._pcre_tar.provision(builddir, clean_target=False, keep_tarball=True) self.run(['./configure', '--prefix={}'.format(self.partdir)], cwd=builddir) self.run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self.run(['make', 'install'], cwd=builddir) def _zlib_install(self, builddir): self._zlib_tar.provision(builddir, clean_target=False, keep_tarball=True) self.run(['./configure', '--prefix={}'.format(self.partdir)], cwd=builddir) self.run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self.run(['make', 'install'], cwd=builddir) def _openssl_install(self, builddir): self._openssl_tar.provision(builddir, clean_target=False, keep_tarball=True) self.run(['./config', '--prefix={}'.format(self.partdir)], cwd=builddir) self.run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self.run(['make', 'install'], cwd=builddir) def _passenger_install(self, builddir): self._passenger_tar.provision(builddir, clean_target=False, keep_tarball=True) def _nginx_install(self, builddir): self._nginx_tar.provision(builddir, clean_target=False, keep_tarball=True) cmd = [ './configure', '--sbin-path={}'.format( os.path.join(self.installdir, 'nginx', 'sbin', 'nginx')), '--conf-path={}'.format( os.path.join(self.installdir, 'nginx', 'conf', 'nginx.conf')), '--with-pcre={}'.format(self._pcre_part_dir), '--with-zlib={}'.format(self._zlib_part_dir), '--with-openssl={}'.format(self._openssl_part_dir), '--with-http_gzip_static_module', '--with-stream', '--prefix={}'.format(self.partdir) ] if self.options.use_passenger: cmd.append('--add-module={}'.format( os.path.join(self._passenger_part_dir, 'src', 'nginx_module'))) self.run(cmd, cwd=builddir) self.run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self.run(['make', 'install'], cwd=builddir)
class RubyPlugin(BasePlugin): @classmethod def schema(cls): schema = super().schema() schema['properties']['use-bundler'] = { 'type': 'boolean', 'default': False } schema['properties']['ruby-version'] = { 'type': 'string', 'default': '2.4.0' } schema['properties']['gems'] = { 'type': 'array', 'minitems': 1, 'uniqueItems': True, 'items': { 'type': 'string' }, 'default': [] } return schema @classmethod def get_pull_properties(cls): # Inform Snapcraft of the properties associated with pulling. If these # change in the YAML Snapcraft will consider the build step dirty. return ['ruby-version', 'gems', 'use-bundler'] def __init__(self, name, options, project): super().__init__(name, options, project) # Beta Warning # Remove this comment and warning once ruby plugin is stable. logger.warn("The ruby plugin is currently in beta, " "its API may break. Use at your own risk") self._ruby_version = options.ruby_version self._ruby_part_dir = os.path.join(self.partdir, 'ruby') self._ruby_download_url = \ 'https://cache.ruby-lang.org/pub/ruby/ruby-{}.tar.gz'.format( self._ruby_version) self._ruby_tar = Tar(self._ruby_download_url, self._ruby_part_dir) self._gems = options.gems or [] self.build_packages.extend(['gcc', 'g++', 'make', 'zlib1g-dev', 'libssl-dev', 'libreadline-dev']) def snap_fileset(self): fileset = super().snap_fileset() fileset.append('-include/') fileset.append('-share/') return fileset def pull(self): super().pull() os.makedirs(self._ruby_part_dir, exist_ok=True) logger.info('Fetching ruby {}...'.format(self._ruby_version)) self._ruby_tar.download() logger.info('Building/installing ruby...') self._ruby_install(builddir=self._ruby_part_dir) self._gem_install() if self.options.use_bundler: self._bundle_install() def env(self, root): env = super().env(root) for key, value in self._env_dict(root).items(): env.append('{}="{}"'.format(key, value)) return env def _env_dict(self, root): env = dict() rubydir = os.path.join(root, 'lib', 'ruby') # Patch versions of ruby continue to use the minor version's RUBYLIB, # GEM_HOME, and GEM_PATH. Fortunately there should just be one, so we # can detect it by globbing instead of trying to determine what the # minor version is programatically versions = glob.glob(os.path.join(rubydir, 'gems', '*')) # Before Ruby has been pulled/installed, no versions will be found. # If that's the case, we won't define any Ruby-specific variables yet if len(versions) == 1: ruby_version = os.path.basename(versions[0]) rubylib = os.path.join(rubydir, ruby_version) env['RUBYLIB'] = '{}:{}'.format(rubylib, os.path.join( rubylib, '{}-linux'.format(platform.machine()))) env['GEM_HOME'] = os.path.join(rubydir, 'gems', ruby_version) env['GEM_PATH'] = os.path.join(rubydir, 'gems', ruby_version) elif len(versions) > 1: raise SnapcraftEnvironmentError( 'Expected a single Ruby version, but found {}'.format( len(versions))) return env def _run(self, command, **kwargs): """Regenerate the build environment, then run requested command. Without this function, the build environment would not be regenerated and thus the newly installed Ruby would not be discovered. """ env = os.environ.copy() env.update(self._env_dict(self.installdir)) self.run(command, env=env, **kwargs) def _ruby_install(self, builddir): self._ruby_tar.provision( builddir, clean_target=False, keep_tarball=True) self._run(['./configure', '--disable-install-rdoc', '--prefix=/'], cwd=builddir) self._run(['make', '-j{}'.format(self.parallel_build_count)], cwd=builddir) self._run(['make', 'install', 'DESTDIR={}'.format(self.installdir)], cwd=builddir) # Fix all shebangs to use the in-snap ruby file_utils.replace_in_file( self.installdir, re.compile(r''), re.compile(r'^#!.*ruby'), r'#!/usr/bin/env ruby') def _gem_install(self): if self.options.use_bundler: self._gems = self._gems + ['bundler'] if self._gems: logger.info('Installing gems...') gem_install_cmd = [os.path.join(self.installdir, 'bin', 'ruby'), os.path.join(self.installdir, 'bin', 'gem'), 'install', '--env-shebang'] self._run(gem_install_cmd + self._gems) def _bundle_install(self): bundle_install_cmd = [os.path.join(self.installdir, 'bin', 'ruby'), os.path.join(self.installdir, 'bin', 'bundle'), 'install'] self._run(bundle_install_cmd)
class RubyPlugin(BasePlugin): @classmethod def schema(cls): schema = super().schema() schema["properties"]["use-bundler"] = { "type": "boolean", "default": False } schema["properties"]["ruby-version"] = { "type": "string", "default": "2.4.2", "pattern": r"^\d+\.\d+(\.\d+)?$", } schema["properties"]["gems"] = { "type": "array", "minitems": 1, "uniqueItems": True, "items": { "type": "string" }, "default": [], } schema["required"] = ["source"] return schema @classmethod def get_pull_properties(cls): # Inform Snapcraft of the properties associated with pulling. If these # change in the YAML Snapcraft will consider the build step dirty. return ["ruby-version", "gems", "use-bundler"] def __init__(self, name, options, project): super().__init__(name, options, project) if project.info.get_build_base() not in ("core", "core16", "core18"): raise errors.PluginBaseError(part_name=self.name, base=project.info.get_build_base()) # Beta Warning # Remove this comment and warning once ruby plugin is stable. logger.warning("The ruby plugin is currently in beta, " "its API may break. Use at your own risk") self._ruby_version = options.ruby_version self._ruby_part_dir = os.path.join(self.partdir, "ruby") feature_pattern = re.compile(r"^(\d+\.\d+)\..*$") feature_version = feature_pattern.sub(r"\1", self._ruby_version) self._ruby_download_url = "https://cache.ruby-lang.org/pub/ruby/{}/ruby-{}.tar.gz".format( feature_version, self._ruby_version) self._ruby_tar = Tar(self._ruby_download_url, self._ruby_part_dir) self._gems = options.gems or [] self.build_packages.extend([ "gcc", "g++", "make", "zlib1g-dev", "libssl-dev", "libreadline-dev" ]) def snap_fileset(self): fileset = super().snap_fileset() fileset.append("-include/") fileset.append("-share/") return fileset def pull(self): super().pull() os.makedirs(self._ruby_part_dir, exist_ok=True) logger.info("Fetching ruby {}...".format(self._ruby_version)) self._ruby_tar.download() logger.info("Building/installing ruby...") self._ruby_install(builddir=self._ruby_part_dir) self._gem_install() if self.options.use_bundler: self._bundle_install() def env(self, root): env = super().env(root) for key, value in self._env_dict(root).items(): env.append('{}="{}"'.format(key, value)) return env def _env_dict(self, root): env = dict() rubydir = os.path.join(root, "lib", "ruby") # Patch versions of ruby continue to use the minor version's RUBYLIB, # GEM_HOME, and GEM_PATH. Fortunately there should just be one, so we # can detect it by globbing instead of trying to determine what the # minor version is programmatically versions = glob.glob(os.path.join(rubydir, "gems", "*")) # Before Ruby has been pulled/installed, no versions will be found. # If that's the case, we won't define any Ruby-specific variables yet if len(versions) == 1: ruby_version = os.path.basename(versions[0]) rubylib = os.path.join(rubydir, ruby_version) # Ruby uses some pretty convoluted rules for determining its # arch-specific RUBYLIB. Rather than try and duplicate that logic # here, let's just look for a file that we know is in there: # rbconfig.rb. There should only be one. paths = glob.glob(os.path.join(rubylib, "*", "rbconfig.rb")) if len(paths) != 1: raise errors.SnapcraftEnvironmentError( "Expected a single rbconfig.rb, but found {}".format( len(paths))) env["RUBYLIB"] = "{}:{}".format(rubylib, os.path.dirname(paths[0])) env["GEM_HOME"] = os.path.join(rubydir, "gems", ruby_version) env["GEM_PATH"] = os.path.join(rubydir, "gems", ruby_version) elif len(versions) > 1: raise errors.SnapcraftEnvironmentError( "Expected a single Ruby version, but found {}".format( len(versions))) return env def _run(self, command, **kwargs): """Regenerate the build environment, then run requested command. Without this function, the build environment would not be regenerated and thus the newly installed Ruby would not be discovered. """ env = os.environ.copy() env.update(self._env_dict(self.installdir)) self.run(command, env=env, **kwargs) def _ruby_install(self, builddir): self._ruby_tar.provision(builddir, clean_target=False, keep_tarball=True) self._run(["./configure", "--disable-install-rdoc", "--prefix=/"], cwd=builddir) self._run(["make", "-j{}".format(self.parallel_build_count)], cwd=builddir) self._run(["make", "install", "DESTDIR={}".format(self.installdir)], cwd=builddir) # Fix all shebangs to use the in-snap ruby file_utils.replace_in_file( self.installdir, re.compile(r""), re.compile(r"^#!.*ruby"), r"#!/usr/bin/env ruby", ) def _gem_install(self): if self.options.use_bundler: self._gems = self._gems + ["bundler"] if self._gems: logger.info("Installing gems...") gem_install_cmd = [ os.path.join(self.installdir, "bin", "ruby"), os.path.join(self.installdir, "bin", "gem"), "install", "--env-shebang", ] self._run(gem_install_cmd + self._gems) def _bundle_install(self): bundle_install_cmd = [ os.path.join(self.installdir, "bin", "ruby"), os.path.join(self.installdir, "bin", "bundle"), "install", ] self._run(bundle_install_cmd)
class RubyPlugin(BasePlugin): @classmethod def schema(cls): schema = super().schema() schema["properties"]["use-bundler"] = {"type": "boolean", "default": False} schema["properties"]["ruby-version"] = {"type": "string", "default": "2.4.2"} schema["properties"]["gems"] = { "type": "array", "minitems": 1, "uniqueItems": True, "items": {"type": "string"}, "default": [], } return schema @classmethod def get_pull_properties(cls): # Inform Snapcraft of the properties associated with pulling. If these # change in the YAML Snapcraft will consider the build step dirty. return ["ruby-version", "gems", "use-bundler"] def __init__(self, name, options, project): super().__init__(name, options, project) # Beta Warning # Remove this comment and warning once ruby plugin is stable. logger.warn( "The ruby plugin is currently in beta, " "its API may break. Use at your own risk" ) self._ruby_version = options.ruby_version self._ruby_part_dir = os.path.join(self.partdir, "ruby") self._ruby_download_url = "https://cache.ruby-lang.org/pub/ruby/ruby-{}.tar.gz".format( self._ruby_version ) self._ruby_tar = Tar(self._ruby_download_url, self._ruby_part_dir) self._gems = options.gems or [] self.build_packages.extend( ["gcc", "g++", "make", "zlib1g-dev", "libssl-dev", "libreadline-dev"] ) def snap_fileset(self): fileset = super().snap_fileset() fileset.append("-include/") fileset.append("-share/") return fileset def pull(self): super().pull() os.makedirs(self._ruby_part_dir, exist_ok=True) logger.info("Fetching ruby {}...".format(self._ruby_version)) self._ruby_tar.download() logger.info("Building/installing ruby...") self._ruby_install(builddir=self._ruby_part_dir) self._gem_install() if self.options.use_bundler: self._bundle_install() def env(self, root): env = super().env(root) for key, value in self._env_dict(root).items(): env.append('{}="{}"'.format(key, value)) return env def _env_dict(self, root): env = dict() rubydir = os.path.join(root, "lib", "ruby") # Patch versions of ruby continue to use the minor version's RUBYLIB, # GEM_HOME, and GEM_PATH. Fortunately there should just be one, so we # can detect it by globbing instead of trying to determine what the # minor version is programmatically versions = glob.glob(os.path.join(rubydir, "gems", "*")) # Before Ruby has been pulled/installed, no versions will be found. # If that's the case, we won't define any Ruby-specific variables yet if len(versions) == 1: ruby_version = os.path.basename(versions[0]) rubylib = os.path.join(rubydir, ruby_version) # Ruby uses some pretty convoluted rules for determining its # arch-specific RUBYLIB. Rather than try and duplicate that logic # here, let's just look for a file that we know is in there: # rbconfig.rb. There should only be one. paths = glob.glob(os.path.join(rubylib, "*", "rbconfig.rb")) if len(paths) != 1: raise SnapcraftEnvironmentError( "Expected a single rbconfig.rb, but found {}".format(len(paths)) ) env["RUBYLIB"] = "{}:{}".format(rubylib, os.path.dirname(paths[0])) env["GEM_HOME"] = os.path.join(rubydir, "gems", ruby_version) env["GEM_PATH"] = os.path.join(rubydir, "gems", ruby_version) elif len(versions) > 1: raise SnapcraftEnvironmentError( "Expected a single Ruby version, but found {}".format(len(versions)) ) return env def _run(self, command, **kwargs): """Regenerate the build environment, then run requested command. Without this function, the build environment would not be regenerated and thus the newly installed Ruby would not be discovered. """ env = os.environ.copy() env.update(self._env_dict(self.installdir)) self.run(command, env=env, **kwargs) def _ruby_install(self, builddir): self._ruby_tar.provision(builddir, clean_target=False, keep_tarball=True) self._run(["./configure", "--disable-install-rdoc", "--prefix=/"], cwd=builddir) self._run(["make", "-j{}".format(self.parallel_build_count)], cwd=builddir) self._run( ["make", "install", "DESTDIR={}".format(self.installdir)], cwd=builddir ) # Fix all shebangs to use the in-snap ruby file_utils.replace_in_file( self.installdir, re.compile(r""), re.compile(r"^#!.*ruby"), r"#!/usr/bin/env ruby", ) def _gem_install(self): if self.options.use_bundler: self._gems = self._gems + ["bundler"] if self._gems: logger.info("Installing gems...") gem_install_cmd = [ os.path.join(self.installdir, "bin", "ruby"), os.path.join(self.installdir, "bin", "gem"), "install", "--env-shebang", ] self._run(gem_install_cmd + self._gems) def _bundle_install(self): bundle_install_cmd = [ os.path.join(self.installdir, "bin", "ruby"), os.path.join(self.installdir, "bin", "bundle"), "install", ] self._run(bundle_install_cmd)