コード例 #1
0
 def test_without_title(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title=None,
     )
     self.assertNotIn('This is the title', result)
コード例 #2
0
ファイル: test_formatter.py プロジェクト: openstack/reno
 def test_without_title(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title=None,
     )
     self.assertNotIn('This is the title', result)
コード例 #3
0
def report_cmd(args):
    "Generates a release notes report"
    reporoot = args.reporoot.rstrip('/') + '/'
    notesdir = utils.get_notes_dir(args)
    collapse = args.collapse_pre_releases
    ldr = loader.Loader(
        reporoot=reporoot,
        notesdir=notesdir,
        branch=args.branch,
        collapse_pre_releases=collapse,
        earliest_version=args.earliest_version,
    )
    if args.version:
        versions = args.version
    else:
        versions = ldr.versions
    text = formatter.format_report(
        ldr,
        versions,
        title='Release Notes',
    )
    if args.output:
        with open(args.output, 'w') as f:
            f.write(text)
    else:
        print(text)
    return
コード例 #4
0
ファイル: test_formatter.py プロジェクト: openstack/reno
 def test_versions(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('0.0.0\n=====', result)
     self.assertIn('1.0.0\n=====', result)
コード例 #5
0
ファイル: test_formatter.py プロジェクト: kpireporter/reno
 def test_with_title(self):
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('This is the title', result)
コード例 #6
0
 def test_versions(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('0.0.0\n=====', result)
     self.assertIn('1.0.0\n=====', result)
コード例 #7
0
 def test_without_title(self):
     result = formatter.format_report(
         reporoot=None,
         scanner_output=self.scanner_output,
         versions_to_include=self.versions,
         title=None,
     )
     self.assertNotIn('This is the title', result)
コード例 #8
0
ファイル: sphinxext.py プロジェクト: openstack/reno
    def run(self):
        env = self.state.document.settings.env
        app = env.app

        def info(msg):
            app.info('[reno] %s' % (msg,))

        title = ' '.join(self.content)
        branch = self.options.get('branch')
        reporoot_opt = self.options.get('reporoot', '.')
        reporoot = os.path.abspath(reporoot_opt)
        relnotessubdir = self.options.get('relnotessubdir',
                                          defaults.RELEASE_NOTES_SUBDIR)
        conf = config.Config(reporoot, relnotessubdir)
        opt_overrides = {}
        if 'notesdir' in self.options:
            opt_overrides['notesdir'] = self.options.get('notesdir')
        version_opt = self.options.get('version')
        # FIXME(dhellmann): Force these flags True for now and figure
        # out how Sphinx passes a "false" flag later.
        # 'collapse-pre-releases' in self.options
        opt_overrides['collapse_pre_releases'] = True
        opt_overrides['stop_at_branch_base'] = True
        if 'earliest-version' in self.options:
            opt_overrides['earliest_version'] = self.options.get(
                'earliest-version')
        if branch:
            opt_overrides['branch'] = branch
        conf.override(**opt_overrides)

        notesdir = os.path.join(relnotessubdir, conf.notesdir)
        info('scanning %s for %s release notes' %
             (os.path.join(conf.reporoot, notesdir),
              branch or 'current branch'))

        ldr = loader.Loader(conf)
        if version_opt is not None:
            versions = [
                v.strip()
                for v in version_opt.split(',')
            ]
        else:
            versions = ldr.versions
        info('got versions %s' % (versions,))
        text = formatter.format_report(
            ldr,
            versions,
            title=title,
        )
        source_name = '<' + __name__ + '>'
        result = statemachine.ViewList()
        for line in text.splitlines():
            result.append(line, source_name)

        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children
コード例 #9
0
 def test_versions(self):
     result = formatter.format_report(
         reporoot=None,
         scanner_output=self.scanner_output,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('0.0.0\n=====', result)
     self.assertIn('1.0.0\n=====', result)
コード例 #10
0
ファイル: test_formatter.py プロジェクト: kpireporter/reno
 def test_without_title(self):
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
     )
     self.assertIn('.. _relnotes_0.0.0:', result)
     self.assertIn('.. _relnotes_0.0.0_Prelude:', result)
     self.assertIn('.. _relnotes_1.0.0:', result)
     self.assertIn('.. _relnotes_1.0.0_Known Issues:', result)
コード例 #11
0
ファイル: test_formatter.py プロジェクト: kpireporter/reno
 def test_with_title(self):
     self.c.override(unreleased_version_title='Not Released')
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('Not Released', result)
     self.assertNotIn('0.1.0-1', result)
     self.assertIn('.. _This is the title_Not Released:', result)
コード例 #12
0
ファイル: test_formatter.py プロジェクト: kpireporter/reno
 def test_with_branch(self):
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
         branch='stable/queens',
     )
     self.assertIn('.. _relnotes_0.0.0_stable_queens:', result)
     self.assertIn('.. _relnotes_0.0.0_stable_queens_Prelude:', result)
     self.assertIn('.. _relnotes_1.0.0_stable_queens:', result)
     self.assertIn('.. _relnotes_1.0.0_stable_queens_Known Issues:', result)
コード例 #13
0
 def test_section_order(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title=None,
     )
     prelude_pos = result.index('This is the prelude.')
     issues_pos = result.index('This is the first issue.')
     features_pos = result.index('We added a feature!')
     expected = [prelude_pos, features_pos, issues_pos]
     actual = list(sorted([prelude_pos, features_pos, issues_pos]))
     self.assertEqual(expected, actual)
コード例 #14
0
ファイル: test_formatter.py プロジェクト: openstack/reno
 def test_section_order(self):
     result = formatter.format_report(
         loader=self.ldr,
         versions_to_include=self.versions,
         title=None,
     )
     prelude_pos = result.index('This is the prelude.')
     issues_pos = result.index('This is the first issue.')
     features_pos = result.index('We added a feature!')
     expected = [prelude_pos, features_pos, issues_pos]
     actual = list(sorted([prelude_pos, features_pos, issues_pos]))
     self.assertEqual(expected, actual)
コード例 #15
0
ファイル: test_formatter.py プロジェクト: kpireporter/reno
 def test_with_title(self):
     self.c.override(unreleased_version_title='Not Released')
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
         title='This is the title',
     )
     self.assertIn('.. _This is the title_0.0.0:', result)
     self.assertIn('.. _This is the title_0.0.0_Prelude:', result)
     self.assertIn('.. _This is the title_1.0.0:', result)
     self.assertIn('.. _This is the title_1.0.0_Known Issues:', result)
コード例 #16
0
    def run(self):
        env = self.state.document.settings.env
        app = env.app

        def info(msg):
            app.info('[reno] %s' % (msg,))

        title = ' '.join(self.content)
        branch = self.options.get('branch')
        reporoot_opt = self.options.get('reporoot', '.')
        reporoot = os.path.abspath(reporoot_opt)
        relnotessubdir = self.options.get('relnotessubdir',
                                          defaults.RELEASE_NOTES_SUBDIR)
        notessubdir = self.options.get('notesdir', defaults.NOTES_SUBDIR)
        version_opt = self.options.get('version')
        # FIXME(dhellmann): Force this flag True for now and figure
        # out how Sphinx passes a "false" flag later.
        collapse = True  # 'collapse-pre-releases' in self.options
        earliest_version = self.options.get('earliest-version')

        notesdir = os.path.join(relnotessubdir, notessubdir)
        info('scanning %s for %s release notes' %
             (os.path.join(reporoot, notesdir), branch or 'current branch'))

        ldr = loader.Loader(
            reporoot=reporoot,
            notesdir=notesdir,
            branch=branch,
            collapse_pre_releases=collapse,
            earliest_version=earliest_version,
        )
        if version_opt is not None:
            versions = [
                v.strip()
                for v in version_opt.split(',')
            ]
        else:
            versions = ldr.versions
        text = formatter.format_report(
            ldr,
            versions,
            title=title,
        )
        source_name = '<' + __name__ + '>'
        result = ViewList()
        for line in text.splitlines():
            result.append(line, source_name)

        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children
コード例 #17
0
 def test_custom_section_order(self):
     result = formatter.format_report(
         loader=self.ldr,
         config=self.c,
         versions_to_include=self.versions,
         title=None,
     )
     prelude_pos = result.index('This is the prelude.')
     api_pos = result.index('API Changes')
     features_pos = result.index('New Features')
     expected = [prelude_pos, api_pos, features_pos]
     actual = list(sorted([prelude_pos, features_pos, api_pos]))
     self.assertEqual(expected, actual)
コード例 #18
0
ファイル: report.py プロジェクト: openstack/reno
def report_cmd(args, conf):
    "Generates a release notes report"
    ldr = loader.Loader(conf)
    if args.version:
        versions = args.version
    else:
        versions = ldr.versions
    text = formatter.format_report(
        ldr,
        versions,
        title='Release Notes',
    )
    if args.output:
        with open(args.output, 'w') as f:
            f.write(text)
    else:
        print(text)
    return
コード例 #19
0
ファイル: sphinxext.py プロジェクト: bdrich/neutron-lbaas
    def run(self):
        env = self.state.document.settings.env
        app = env.app

        def info(msg):
            app.info('[reno] %s' % (msg,))

        title = ' '.join(self.content)
        branch = self.options.get('branch')
        reporoot_opt = self.options.get('reporoot', '.')
        reporoot = os.path.abspath(reporoot_opt)
        relnotessubdir = self.options.get('relnotessubdir',
                                          defaults.RELEASE_NOTES_SUBDIR)
        notessubdir = self.options.get('notesdir', defaults.NOTES_SUBDIR)
        version_opt = self.options.get('version')

        notesdir = os.path.join(relnotessubdir, notessubdir)
        info('scanning %s for %s release notes' %
             (os.path.join(reporoot, notesdir), branch or 'current branch'))

        notes = scanner.get_notes_by_version(reporoot, notesdir, branch)
        if version_opt is not None:
            versions = [
                v.strip()
                for v in version_opt.split(',')
            ]
        else:
            versions = notes.keys()
        text = formatter.format_report(
            reporoot,
            notes,
            versions,
            title=title,
        )
        source_name = '<' + __name__ + '>'
        result = ViewList()
        for line in text.splitlines():
            result.append(line, source_name)

        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children
コード例 #20
0
ファイル: report.py プロジェクト: 571451370/devstack_mitaka
def report_cmd(args, conf):
    "Generates a release notes report"
    ldr = loader.Loader(conf)
    if args.version:
        versions = args.version
    else:
        versions = ldr.versions
    text = formatter.format_report(
        ldr,
        conf,
        versions,
        title=args.title,
        show_source=args.show_source,
    )
    if args.output:
        with open(args.output, 'w') as f:
            f.write(text)
    else:
        print(text)
    return
コード例 #21
0
ファイル: report.py プロジェクト: bdrich/neutron-lbaas
def report_cmd(args):
    "Generates a release notes report"
    reporoot = args.reporoot.rstrip('/') + '/'
    notesdir = utils.get_notes_dir(args)
    notes = scanner.get_notes_by_version(reporoot, notesdir, args.branch)
    if args.version:
        versions = args.version
    else:
        versions = notes.keys()
    text = formatter.format_report(
        reporoot,
        notes,
        versions,
        title='Release Notes',
    )
    if args.output:
        with open(args.output, 'w') as f:
            f.write(text)
    else:
        print(text)
    return
コード例 #22
0
ファイル: setup_command.py プロジェクト: kpireporter/reno
    def run(self):
        conf = config.Config(self.repo_root, self.rel_notes_dir)

        # Generate the cache using the configuration options found
        # in the release notes directory and the default output
        # filename.
        cache_filename = cache.write_cache_db(
            conf=conf,
            versions_to_include=[],  # include all versions
            outfilename=None,  # generate the default name
        )
        log.info('wrote cache file to %s', cache_filename)

        ldr = loader.Loader(conf)
        text = formatter.format_report(
            ldr,
            conf,
            ldr.versions,
            title=self.distribution.metadata.name,
        )
        with open(self.output_file, 'w') as f:
            f.write(text)
        log.info('wrote release notes to %s', self.output_file)
コード例 #23
0
ファイル: conf.py プロジェクト: goodspark/dd-trace-py
    def run(self):
        # type: () -> nodes.Node
        """
        Run to generate the output from .. ddtrace-release-notes:: directive


        1. Determine the max version cutoff we need to report for

           We determine this by traversing the git log until we
           find the first dev or release branch ref.

           If we are generating for 1.x branch we will use 2.0 as the cutoff.

           If we are generating for 0.60 branch we will use 0.61 as the cutoff.

           We do this to ensure if we are generating notes for older versions
           we do no include all up to date release notes. Think releasing 0.57.2
           when there is 0.58.0, 0.59.0, 1.0.0, etc we only want notes for < 0.58.

        2. Iterate through all release branches

           A release branch is one that matches the ``^[0-9]+.[0-9]+``` pattern

           Skip any that do not meet the max version cutoff.

        3. Determine the earliest version to report for each release branch

           If the release has only RC releases then use ``.0rc1`` as the earliest
           version. If there are non-RC releases then use ``.0`` version as the
           earliest.

           We do this because we want reno to only report notes that are for that
           given release branch but it will collapse RC releases if there is a
           non-RC tag on that branch. So there isn't a consistent "earliest version"
           we can use for in-progress/dev branches as well as released branches.


        4. Generate a reno config for reporting and generate the notes for each branch
        """
        # This is where we will aggregate the generated notes
        title = " ".join(self.content)
        result = statemachine.ViewList()

        # Determine the max version we want to report for
        max_version = self._get_report_max_version()
        LOG.info("capping max report version to %r", max_version)

        # For each release branch, starting with the newest
        for version, ref in self._release_branches:
            # If this version is equal to or greater than the max version we want to report for
            if max_version is not None and version >= max_version:
                LOG.info("skipping %s >= %s", version, max_version)
                continue

            # Older versions did not have reno release notes
            # DEV: Reno will fail if we try to run on a branch with no notes
            if (version.major, version.minor) < (0, 44):
                LOG.info("skipping older version %s", version)
                continue

            # Parse the branch name from the ref, we want origin/{major}.{minor}[-dev]
            _, _, branch = ref.partition("refs/remotes/")

            # Determine the earliest release tag for this version
            earliest_version = self._get_earliest_version(version)
            if not earliest_version:
                LOG.info("no release tags found for %s", version)
                continue

            # Setup reno config
            conf = config.Config(self._repo.path, "releasenotes")
            conf.override(
                branch=branch,
                collapse_pre_releases=True,
                stop_at_branch_base=True,
                earliest_version=earliest_version,
            )
            LOG.info(
                "scanning %s for %s release notes, stopping at %s",
                os.path.join(self._repo.path, "releasenotes/notes"),
                branch,
                earliest_version,
            )

            # Generate the formatted RST
            with loader.Loader(conf) as ldr:
                versions = ldr.versions
                LOG.info("got versions %s", versions)
                text = formatter.format_report(
                    ldr,
                    conf,
                    versions,
                    title=title,
                    branch=branch,
                )

            source_name = "<%s %s>" % (__name__, branch or "current branch")
            for line_num, line in enumerate(text.splitlines(), 1):
                LOG.debug("%4d: %s", line_num, line)
                result.append(line, source_name, line_num)

        # Generate the RST nodes to return for rendering
        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children
コード例 #24
0
ファイル: sphinxext.py プロジェクト: kastenhq/reno
    def run(self):
        title = ' '.join(self.content)
        branch = self.options.get('branch')
        relnotessubdir = self.options.get(
            'relnotessubdir',
            defaults.RELEASE_NOTES_SUBDIR,
        )
        reporoot = self._find_reporoot(
            self.options.get('reporoot', '.'),
            relnotessubdir,
        )
        ignore_notes = [
            name.strip()
            for name in self.options.get('ignore-notes', '').split(',')
        ]
        conf = config.Config(reporoot, relnotessubdir)
        opt_overrides = {}
        if 'notesdir' in self.options:
            opt_overrides['notesdir'] = self.options.get('notesdir')
        version_opt = self.options.get('version')
        # FIXME(dhellmann): Force these flags True for now and figure
        # out how Sphinx passes a "false" flag later.
        # 'collapse-pre-releases' in self.options
        opt_overrides['collapse_pre_releases'] = True
        # Only stop at the branch base if we have not been told
        # explicitly which versions to include.
        opt_overrides['stop_at_branch_base'] = (version_opt is None)
        if 'earliest-version' in self.options:
            opt_overrides['earliest_version'] = self.options.get(
                'earliest-version')
        if 'unreleased-version-title' in self.options:
            opt_overrides['unreleased_version_title'] = self.options.get(
                'unreleased-version-title')

        if branch:
            opt_overrides['branch'] = branch
        if ignore_notes:
            opt_overrides['ignore_notes'] = ignore_notes
        conf.override(**opt_overrides)

        notesdir = os.path.join(relnotessubdir, conf.notesdir)
        LOG.info('scanning %s for %s release notes' % (os.path.join(
            conf.reporoot, notesdir), branch or 'current branch'))

        ldr = loader.Loader(conf)
        if version_opt is not None:
            versions = [v.strip() for v in version_opt.split(',')]
        else:
            versions = ldr.versions
        LOG.info('got versions %s' % (versions, ))
        text = formatter.format_report(
            ldr,
            conf,
            versions,
            title=title,
            branch=branch,
        )
        source_name = '<%s %s>' % (__name__, branch or 'current branch')
        result = statemachine.ViewList()
        for line_num, line in enumerate(text.splitlines(), 1):
            LOG.debug('%4d: %s', line_num, line)
            result.append(line, source_name, line_num)

        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children
コード例 #25
0
def generate_release_notes(
    repo,
    repo_path,
    start_revision,
    end_revision,
    show_dates,
    skip_requirement_merges,
    is_stable,
    series,
    email,
    email_from,
    email_reply_to,
    email_tags,
    include_pypi_link,
    changes_only,
    first_release,
    deliverable_file,
    description,
    publishing_dir_name,
):
    """Return the text of the release notes.

    :param repo: The name of the repo.
    :param repo_path: Path to the repo repository on disk.
    :param start_revision: First reference for finding change log.
    :param end_revision: Final reference for finding change log.
    :param show_dates: Boolean indicating whether or not to show dates
        in the output.
    :param skip_requirement_merges: Boolean indicating whether to
        skip merge commits for requirements changes.
    :param is_stable: Boolean indicating whether this is a stable
        series or not.
    :param series: String holding the name of the series.
    :param email: Boolean indicating whether the output format should
        be an email message.
    :param email_from: String containing the sender email address.
    :param email_reply_to: String containing the email reply-to address.
    :param email_tags: String containing the email header topic tags to add.
    :param include_pypi_link: Boolean indicating whether or not to
        include an automatically generated link to the PyPI package
        page.
    :param changes_only: Boolean indicating whether to limit output to
        the list of changes, without any extra data.
    :param first_release: Boolean indicating whether this is the first
        release of the project
    :param deliverable_file: The deliverable file path from the repo root.
    :param description: Description of the repo
    :param publishing_dir_name: The directory on publishings.openstack.org
        containing the package.
    """
    repo_name = repo.split('/')[-1]
    # Determine if this is a release candidate or not.
    is_release_candidate = 'rc' in end_revision

    # Do not mention the series in independent model since there is none
    if series == 'independent':
        series = ''

    if not email_from:
        raise RuntimeError('No email-from specified')

    # Get the commits that are in the desired range...
    git_range = "%s..%s" % (start_revision, end_revision)
    if show_dates:
        format = "--format=%h %ci %s"
    else:
        format = "--oneline"
    cmd = ["git", "log", "--no-color", format, "--no-merges", git_range]
    stdout = run_cmd(cmd, cwd=repo_path)
    changes = []
    for commit_line in stdout.splitlines():
        commit_line = commit_line.strip()
        if not commit_line or is_skippable_commit(skip_requirement_merges,
                                                  commit_line):
            continue
        else:
            changes.append(commit_line)

    # Filter out any requirement file changes...
    requirement_changes = []
    requirement_files = list(
        glob.glob(os.path.join(repo_path, '*requirements*.txt')))
    if requirement_files:
        cmd = ['git', 'diff', '-U0', '--no-color', git_range]
        cmd.extend(requirement_files)
        stdout = run_cmd(cmd, cwd=repo_path)
        requirement_changes = [
            line.strip() for line in stdout.splitlines() if line.strip()
        ]

    # Get statistics about the range given...
    cmd = ['git', 'diff', '--stat', '--no-color', git_range]
    stdout = run_cmd(cmd, cwd=repo_path)
    diff_stats = []
    for line in stdout.splitlines():
        line = line.strip()
        if not line or line.find("tests") != -1 or line.startswith("doc"):
            continue
        diff_stats.append(line)

    # Extract + valdiate needed sections...
    sections = parse_deliverable(series,
                                 repo_name,
                                 deliverable_file=deliverable_file)
    change_header = ["Changes in %s %s" % (repo, git_range)]
    change_header.append("-" * len(change_header[0]))

    # Look for reno notes for this version.
    if not changes_only:
        logging.getLogger('reno').setLevel(logging.WARNING)
        cfg = reno_config.Config(reporoot=repo_path, )
        branch = None
        if is_stable and series:
            branch = 'origin/stable/%s' % series
        cfg.override(branch=branch)
        ldr = loader.Loader(conf=cfg, ignore_cache=True)
        if end_revision in ldr.versions:
            rst_notes = formatter.format_report(
                loader=ldr,
                config=cfg,
                versions_to_include=[end_revision],
            )
            reno_notes = rst2txt.convert(rst_notes).decode('utf-8')
        else:
            LOG.warning(
                ('Did not find revision %r in list of versions '
                 'with release notes %r, skipping reno'),
                end_revision,
                ldr.versions,
            )
            reno_notes = ''
    else:
        reno_notes = ''

    # The recipient for announcements should always be the
    # [email protected] ML (except for
    # release-test)
    email_to = '*****@*****.**'
    if repo_name == 'openstack-release-test':
        email_to = '*****@*****.**'

    params = dict(sections)
    params.update({
        'project': repo,
        'description': description,
        'end_rev': end_revision,
        'range': git_range,
        'lib': repo_path,
        'skip_requirement_merges': skip_requirement_merges,
        'changes': changes,
        'requirement_changes': requirement_changes,
        'diff_stats': diff_stats,
        'change_header': "\n".join(change_header),
        'emotion': random.choice(EMOTIONS),
        'stable_series': is_stable,
        'series': series,
        'email': email,
        'email_from': email_from,
        'email_to': email_to,
        'email_reply_to': email_reply_to,
        'email_tags': email_tags,
        'reno_notes': reno_notes,
        'first_release': first_release,
        'publishing_dir_name': publishing_dir_name,
    })
    if include_pypi_link:
        params['pypi_url'] = PYPI_URL_TPL % repo_name
    else:
        params['pypi_url'] = None

    response = []
    if changes_only:
        response.append(expand_template(CHANGES_ONLY_TPL, params))
    else:
        if email:
            email_header = expand_template(EMAIL_HEADER_TPL.strip(), params)
            response.append(email_header.lstrip())
        if is_release_candidate:
            response.append(expand_template(RELEASE_CANDIDATE_TPL, params))
        else:
            header = expand_template(HEADER_RELEASE_TPL.strip(), params)
            response.append(parawrap.fill(header))
            response.append(expand_template(CHANGE_RELEASE_TPL, params))
    return '\n'.join(response)
コード例 #26
0
def generate_release_notes(library, library_path,
                           start_revision, end_revision,
                           show_dates, skip_requirement_merges,
                           is_stable, series,
                           email, email_from,
                           email_to, email_reply_to, email_tags,
                           include_pypi_link,
                           changes_only,
                           first_release,
                           ):
    """Return the text of the release notes.

    :param library: The name of the library.
    :param library_path: Path to the library repository on disk.
    :param start_revision: First reference for finding change log.
    :param end_revision: Final reference for finding change log.
    :param show_dates: Boolean indicating whether or not to show dates
        in the output.
    :param skip_requirement_merges: Boolean indicating whether to
        skip merge commits for requirements changes.
    :param is_stable: Boolean indicating whether this is a stable
        series or not.
    :param series: String holding the name of the series.
    :param email: Boolean indicating whether the output format should
        be an email message.
    :param email_from: String containing the sender email address.
    :param email_to: String containing the email recipient.
    :param email_reply_to: String containing the email reply-to address.
    :param email_tags: String containing the email header topic tags to add.
    :param include_pypi_link: Boolean indicating whether or not to
        include an automatically generated link to the PyPI package
        page.
    :param changes_only: Boolean indicating whether to limit output to
        the list of changes, without any extra data.
    :param first_release: Boolean indicating whether this is the first
        release of the project

    """

    # Do not mention the series in independent model since there is none
    if series == 'independent':
        series = ''

    if not os.path.isfile(os.path.join(library_path, "setup.py")):
        raise RuntimeError("No 'setup.py' file found in %s\n" % library_path)

    if not email_from:
        raise RuntimeError('No email-from specified')

    # Get the python library/program description...
    cmd = [sys.executable, 'setup.py', '--description']
    stdout, stderr = run_cmd(cmd, cwd=library_path)
    description = stdout.strip()

    # Get the python library/program name
    cmd = [sys.executable, 'setup.py', '--name']
    stdout, stderr = run_cmd(cmd, cwd=library_path)
    library_name = stdout.strip()

    # Get the commits that are in the desired range...
    git_range = "%s..%s" % (start_revision, end_revision)
    if show_dates:
        format = "--format=%h %ci %s"
    else:
        format = "--oneline"
    cmd = ["git", "log", "--no-color", format, "--no-merges", git_range]
    stdout, stderr = run_cmd(cmd, cwd=library_path)
    changes = []
    for commit_line in stdout.splitlines():
        commit_line = commit_line.strip()
        if not commit_line or is_skippable_commit(skip_requirement_merges,
                                                  commit_line):
            continue
        else:
            changes.append(commit_line)

    # Filter out any requirement file changes...
    requirement_changes = []
    requirement_files = list(glob.glob(os.path.join(library_path,
                                                    '*requirements*.txt')))
    if requirement_files:
        cmd = ['git', 'diff', '-U0', '--no-color', git_range]
        cmd.extend(requirement_files)
        stdout, stderr = run_cmd(cmd, cwd=library_path)
        requirement_changes = [line.strip()
                               for line in stdout.splitlines() if line.strip()]

    # Get statistics about the range given...
    cmd = ['git', 'diff', '--stat', '--no-color', git_range]
    stdout, stderr = run_cmd(cmd, cwd=library_path)
    diff_stats = []
    for line in stdout.splitlines():
        line = line.strip()
        if not line or line.find("tests") != -1 or line.startswith("doc"):
            continue
        diff_stats.append(line)

    # Extract + valdiate needed sections from readme...
    readme_sections = parse_readme(library_path)
    change_header = ["Changes in %s %s" % (library_name, git_range)]
    change_header.append("-" * len(change_header[0]))

    # Look for reno notes for this version.
    branch = None
    if is_stable and series:
        branch = 'origin/stable/%s' % series
    scanner_output = scanner.get_notes_by_version(
        reporoot=library_path,
        notesdir='%s/%s' % (reno_defaults.RELEASE_NOTES_SUBDIR,
                            reno_defaults.NOTES_SUBDIR),
        branch=branch,
    )
    if end_revision in scanner_output:
        rst_notes = formatter.format_report(
            reporoot=library_path,
            scanner_output=scanner_output,
            versions_to_include=[end_revision],
        )
        reno_notes = rst2txt.convert(rst_notes)
    else:
        reno_notes = ''

    params = dict(readme_sections)
    params.update({
        'project': library_name,
        'description': description,
        'end_rev': end_revision,
        'range': git_range,
        'lib': library_path,
        'skip_requirement_merges': skip_requirement_merges,
        'changes': changes,
        'requirement_changes': requirement_changes,
        'diff_stats': diff_stats,
        'change_header': "\n".join(change_header),
        'emotion': random.choice(EMOTIONS),
        'stable_series': is_stable,
        'series': series,
        'email': email,
        'email_from': email_from,
        'email_to': email_to,
        'email_reply_to': email_reply_to,
        'email_tags': email_tags,
        'reno_notes': reno_notes,
        'first_release': first_release,
    })
    if include_pypi_link:
        params['pypi_url'] = PYPI_URL_TPL % library_name
    else:
        params['pypi_url'] = None

    response = []
    if changes_only:
        response.append(expand_template(CHANGES_ONLY_TPL, params))
    else:
        if email:
            email_header = expand_template(EMAIL_HEADER_TPL.strip(), params)
            response.append(email_header.lstrip())
        header = expand_template(HEADER_RELEASE_TPL.strip(), params)
        response.append(parawrap.fill(header))
        response.append(expand_template(CHANGE_RELEASE_TPL, params))
    return '\n'.join(response)
コード例 #27
0
    def run(self):
        env = self.state.document.settings.env
        app = env.app

        def info(msg):
            app.info('[reno] %s' % (msg, ))

        title = ' '.join(self.content)
        branch = self.options.get('branch')
        reporoot_opt = self.options.get('reporoot', '.')
        reporoot = os.path.abspath(reporoot_opt)
        # When building on RTD.org the root directory may not be
        # the current directory, so look for it.
        reporoot = repo.Repo.discover(reporoot).path
        relnotessubdir = self.options.get('relnotessubdir',
                                          defaults.RELEASE_NOTES_SUBDIR)
        ignore_notes = [
            name.strip()
            for name in self.options.get('ignore-notes', '').split(',')
        ]
        conf = config.Config(reporoot, relnotessubdir)
        opt_overrides = {}
        if 'notesdir' in self.options:
            opt_overrides['notesdir'] = self.options.get('notesdir')
        version_opt = self.options.get('version')
        # FIXME(dhellmann): Force these flags True for now and figure
        # out how Sphinx passes a "false" flag later.
        # 'collapse-pre-releases' in self.options
        opt_overrides['collapse_pre_releases'] = True
        # Only stop at the branch base if we have not been told
        # explicitly which versions to include.
        opt_overrides['stop_at_branch_base'] = (version_opt is None)
        if 'earliest-version' in self.options:
            opt_overrides['earliest_version'] = self.options.get(
                'earliest-version')
        if branch:
            opt_overrides['branch'] = branch
        if ignore_notes:
            opt_overrides['ignore_notes'] = ignore_notes
        conf.override(**opt_overrides)

        notesdir = os.path.join(relnotessubdir, conf.notesdir)
        info('scanning %s for %s release notes' % (os.path.join(
            conf.reporoot, notesdir), branch or 'current branch'))

        ldr = loader.Loader(conf)
        if version_opt is not None:
            versions = [v.strip() for v in version_opt.split(',')]
        else:
            versions = ldr.versions
        info('got versions %s' % (versions, ))
        text = formatter.format_report(
            ldr,
            conf,
            versions,
            title=title,
        )
        source_name = '<%s %s>' % (__name__, branch or 'current branch')
        result = statemachine.ViewList()
        for line in text.splitlines():
            result.append(line, source_name)

        node = nodes.section()
        node.document = self.state.document
        nested_parse_with_titles(self.state, result, node)
        return node.children