<commit_before># -*- test-case-name: admin.test.test_packaging -*-
# Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Helper utilities for Flocker packaging.

Notes:

    Motivation:
    * We depend on libraries which are not packaged for the target OS.
    * We depend on newer versions of libraries which have not yet been included
      in the target OS.

    Disadvantages:
    * We won't be able to take advantage of library security updates shipped by
      the target OS.
      * But by shipping our own separate dependency packages we will need to be
        responsible for shipping security patches in those packages.
      * And rather than being responsible only for the security of Flocker, we
        become responsible for the security of all other packages that depend
        on that package.
    * Packages will be larger.

    Followup Issues:
    * Update all pinned dependencies to instead be minimum dependencies.
      * This means that as and when sufficiently new versions of our
        dependencies are introduced upstream, we can remove them from our sumo
        build.
      * We'll need to keep track of which of our dependencies are provided on
        each platform and somehow omit those from the build for that
        platform.
      * Those dependencies which are either too old or which are not packaged
        will be imported from the sumo virtualenv in preference.
      * Eventually we hope that all our dependencies will filter upstream and
        we will no longer have to bundle them; at which point the `flocker`
        package itself may be ready to be packaged by upstream distributions.

    Ticket refs:
         * https://github.com/ClusterHQ/flocker/issues/88

    Issue: CI integration (??):
    Update buildbot to build RPMs using new build scripts
    * Issue: create deb, mac, gentoo build slave
    * Issue: install from resulting package from repo and run test suite

    Issue: Client package build (??):
    Sumo packaging of flocker-deploy
    * For deb, RPM, and mac (via homebrew or ...)
    * Proper mac packages. See
      http://stackoverflow.com/questions/11487596/making-os-x-installer-packages-like-a-pro-xcode4-developer-id-mountain-lion-re

    Client package CI integration

    Misc:
    * separate stable and testing repos for deb and rpm
    * update python-flocker.spec.in requirements (remove most of them)
    * maybe even remove the spec file template and generate_spec function
      entirely (do we need it?)
    * do we still need to build an SRPM?
    * automatically build a wheel
    * automatically build an sdist

    Similar / Related Systems:
    * https://github.com/opscode/omnibus (Package Ruby apps with their dependencies)
    * https://github.com/bernd/fpm-cookery
    * http://dh-virtualenv.readthedocs.org/en/latest/info.html
    * https://github.com/mozilla/socorro/blob/master/scripts/install.sh and
      https://github.com/mozilla/socorro/blob/master/scripts/package.sh and
      http://socorro.readthedocs.org/en/latest/installation/install-src-prod.html

    TODO:
    * Build fpm platform packages for quicker installation on build slaves
      https://github.com/hatt/omnibus-fpm
    *
"""
import platform
import sys
from subprocess import check_output, check_call
from tempfile import mkdtemp
from textwrap import dedent


from twisted.python.constants import ValueConstant, Values
from twisted.python.filepath import FilePath
from twisted.python import usage, log

from characteristic import attributes, Attribute
import virtualenv

from .release import make_rpm_version


class PackageTypes(Values):
    """
    Constants representing supported target packaging formats.
    """
    RPM = ValueConstant('rpm')
    DEB = ValueConstant('deb')


# Associate package formats with platform operating systems.
PACKAGE_TYPE_MAP = {
    PackageTypes.RPM: ('fedora', 'centos'),
    PackageTypes.DEB: ('ubuntu',),
}


@attributes(['name', 'version'])
class Distribution(object):
    """
    A linux distribution.

    :ivar bytes name: The name of the distribution.
    :ivar bytes version: The version of the distribution.
    """

    @classmethod
    def _get_current_distribution(klass):
        """
        :return: A ``Distribution`` representing the current platform.
        """
        name, version, id = (
            platform.linux_distribution(full_distribution_name=False))
        return klass(name=name.lower(), version=version)


CURRENT_DISTRIBUTION = Distribution._get_current_distribution()


def _native_package_type():
    """
    :return: The ``bytes`` name of the native package format for this platform.
    """
    distribution_name = CURRENT_DISTRIBUTION.name.lower()

    for package_type, distribution_names in PACKAGE_TYPE_MAP.items():
        if distribution_name.lower() in distribution_names:
            return package_type
    else:
        raise ValueError("Unknown distribution.", distribution_name)


@attributes(['steps'])
class BuildSequence(object):
    """
    Run the supplied ``steps`` consecutively.

    :ivar tuple steps: A sequence of steps.
    """
    def run(self):
        for step in self.steps:
            step.run()


def run_command(args, env=None, cwd=None):
    log.msg(
        format="Running %(args)r with environment %(env)r "
               "and working directory %(cwd)s",
        args=args, env=env, cwd=cwd)
    return check_output(
        args=args,
        env=env,
        cwd=cwd,
    )


@attributes([
    Attribute('package'),
    Attribute('compare', default_value=None),
    Attribute('version', default_value=None)])
class Dependency(object):
    """
    Package dependency
    """
    def __init__(self):
        if (self.compare is None) != (self.version is None):
            raise ValueError(
                "Must specify both or neither compare and version.")

    def format(self, package_type):
        if package_type == PackageTypes.DEB:
            if self.version:
                return "%s (%s %s)" % (<commit_msg>docstrings<commit_after># -*- test-case-name: admin.test.test_packaging -*-
# Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Helper utilities for Flocker packaging.

Notes:

    Motivation:
    * We depend on libraries which are not packaged for the target OS.
    * We depend on newer versions of libraries which have not yet been included
      in the target OS.

    Disadvantages:
    * We won't be able to take advantage of library security updates shipped by
      the target OS.
      * But by shipping our own separate dependency packages we will need to be
        responsible for shipping security patches in those packages.
      * And rather than being responsible only for the security of Flocker, we
        become responsible for the security of all other packages that depend
        on that package.
    * Packages will be larger.

    Followup Issues:
    * Update all pinned dependencies to instead be minimum dependencies.
      * This means that as and when sufficiently new versions of our
        dependencies are introduced upstream, we can remove them from our sumo
        build.
      * We'll need to keep track of which of our dependencies are provided on
        each platform and somehow omit those from the build for that
        platform.
      * Those dependencies which are either too old or which are not packaged
        will be imported from the sumo virtualenv in preference.
      * Eventually we hope that all our dependencies will filter upstream and
        we will no longer have to bundle them; at which point the `flocker`
        package itself may be ready to be packaged by upstream distributions.

    Ticket refs:
         * https://github.com/ClusterHQ/flocker/issues/88

    Issue: CI integration (??):
    Update buildbot to build RPMs using new build scripts
    * Issue: create deb, mac, gentoo build slave
    * Issue: install from resulting package from repo and run test suite

    Issue: Client package build (??):
    Sumo packaging of flocker-deploy
    * For deb, RPM, and mac (via homebrew or ...)
    * Proper mac packages. See
      http://stackoverflow.com/questions/11487596/making-os-x-installer-packages-like-a-pro-xcode4-developer-id-mountain-lion-re

    Client package CI integration

    Misc:
    * separate stable and testing repos for deb and rpm
    * update python-flocker.spec.in requirements (remove most of them)
    * maybe even remove the spec file template and generate_spec function
      entirely (do we need it?)
    * do we still need to build an SRPM?
    * automatically build a wheel
    * automatically build an sdist

    Similar / Related Systems:
    * https://github.com/opscode/omnibus (Package Ruby apps with their dependencies)
    * https://github.com/bernd/fpm-cookery
    * http://dh-virtualenv.readthedocs.org/en/latest/info.html
    * https://github.com/mozilla/socorro/blob/master/scripts/install.sh and
      https://github.com/mozilla/socorro/blob/master/scripts/package.sh and
      http://socorro.readthedocs.org/en/latest/installation/install-src-prod.html

    TODO:
    * Build fpm platform packages for quicker installation on build slaves
      https://github.com/hatt/omnibus-fpm
    *
"""
import platform
import sys
from subprocess import check_output, check_call
from tempfile import mkdtemp
from textwrap import dedent


from twisted.python.constants import ValueConstant, Values
from twisted.python.filepath import FilePath
from twisted.python import usage, log

from characteristic import attributes, Attribute
import virtualenv

from .release import make_rpm_version


class PackageTypes(Values):
    """
    Constants representing supported target packaging formats.
    """
    RPM = ValueConstant('rpm')
    DEB = ValueConstant('deb')


# Associate package formats with platform operating systems.
PACKAGE_TYPE_MAP = {
    PackageTypes.RPM: ('fedora', 'centos'),
    PackageTypes.DEB: ('ubuntu',),
}


@attributes(['name', 'version'])
class Distribution(object):
    """
    A linux distribution.

    :ivar bytes name: The name of the distribution.
    :ivar bytes version: The version of the distribution.
    """

    @classmethod
    def _get_current_distribution(klass):
        """
        :return: A ``Distribution`` representing the current platform.
        """
        name, version, id = (
            platform.linux_distribution(full_distribution_name=False))
        return klass(name=name.lower(), version=version)


CURRENT_DISTRIBUTION = Distribution._get_current_distribution()


def _native_package_type():
    """
    :return: The ``bytes`` name of the native package format for this platform.
    """
    distribution_name = CURRENT_DISTRIBUTION.name.lower()

    for package_type, distribution_names in PACKAGE_TYPE_MAP.items():
        if distribution_name.lower() in distribution_names:
            return package_type
    else:
        raise ValueError("Unknown distribution.", distribution_name)


@attributes(['steps'])
class BuildSequence(object):
    """
    Run the supplied ``steps`` consecutively.

    :ivar tuple steps: A sequence of steps.
    """
    def run(self):
        for step in self.steps:
            step.run()


def run_command(args, env=None, cwd=None):
    """
    Run a subprocess and return its output. The command line and its environment
    are logged for debugging purposes.

    :return: The output of the command.
    """
    log.msg(
        format="Running %(args)r with environment %(env)r "
               "and working directory %(cwd)s",
        args=args, env=env, cwd=cwd)
    return check_output(args=args, env=env, cwd=cwd,)


@attributes([
    Attribute('package'),
    Attribute('compare', default_value=None),
    Attribute('version', default_value=None)])
class Dependency(object):
    """
    Package dependency
    """
    def __init__(self):
        if (self.compare is None) != (self.version is None):
            raise ValueError(
                "Must specify both or neither compare and version.")

    def format(self, package_type):
        if package_type == PackageTypes.DEB:
            if self.version:
                return "%s (%s %s)" % (
{'fraction_non_alphanumeric': 0.08296405711472181, 'fraction_numerical': 0.0018053504021007714, 'mean_word_length': 3.004929346040092, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2178746', 'n_tokens_mistral': 3330, 'n_tokens_neox': 3171, 'n_words': 1207}