Completed
Pull Request — master (#127)
by Michael
07:53 queued 07:21
created

BumpVersion.load()   B

Complexity

Conditions 4

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
c 2
b 0
f 0
dl 0
loc 32
ccs 14
cts 14
cp 1
crap 4
rs 8.5806
1 3
import re
0 ignored issues
show
Bug introduced by
There seems to be a cyclic import (changes -> changes.cli -> changes.commands.publish).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (changes -> changes.cli -> changes.commands.status).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (changes -> changes.cli -> changes.commands.stage).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
2 3
import textwrap
3 3
from configparser import RawConfigParser
4 3
from enum import Enum
5 3
from pathlib import Path
6
7 3
import attr
8 3
import click
9
10
11 3
class ReleaseType(str, Enum):
12 3
    NO_CHANGE = 'no-changes'
13 3
    BREAKING_CHANGE = 'breaking'
14 3
    FEATURE = 'feature'
15 3
    FIX = 'fix'
16
17
18 3
@attr.s
19 3
class Release(object):
20 3
    release_date = attr.ib()
21 3
    version = attr.ib()
22 3
    description = attr.ib(default=attr.Factory(str))
23 3
    name = attr.ib(default=attr.Factory(str))
24 3
    notes = attr.ib(default=attr.Factory(dict))
25 3
    release_file_path = attr.ib(default='')
26
27 3
    bumpversion_part = attr.ib(default=None)
28 3
    release_type = attr.ib(default=None)
29
30 3
    @property
31
    def title(self):
32 3
        return '{version} ({release_date})'.format(
33
            version=self.version,
34
            release_date=self.release_date
35
        ) + ((' ' + self.name) if self.name else '')
36
37 3
    @property
38
    def release_note_filename(self):
39 3
        return '{version}-{release_date}'.format(
40
            version=self.version,
41
            release_date=self.release_date
42
        ) + (('-' + self.name) if self.name else '')
43
44 3
    @classmethod
45
    def generate_notes(cls, project_labels, pull_requests_since_latest_version):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (80/79).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
46 3
        for label, properties in project_labels.items():
47 3
            pull_requests_with_label = [
48
                pull_request
49
                for pull_request in pull_requests_since_latest_version
50
                if label in pull_request.label_names
51
            ]
52
53 3
            project_labels[label]['pull_requests'] = pull_requests_with_label
54
55 3
        return project_labels
56
57
58 3
@attr.s
59 3
class BumpVersion(object):
60 3
    DRAFT_OPTIONS = [
61
        '--dry-run', '--verbose',
62
        '--no-commit', '--no-tag',
63
        '--allow-dirty',
64
    ]
65 3
    STAGE_OPTIONS = [
66
        '--verbose', '--allow-dirty',
67
        '--no-commit', '--no-tag',
68
    ]
69
70 3
    current_version = attr.ib()
71 3
    version_files_to_replace = attr.ib(default=attr.Factory(list))
72
73 3
    @classmethod
74
    def load(cls, latest_version):
75
        # TODO: look in other supported bumpversion config locations
76 3
        bumpversion = None
77 3
        bumpversion_config_path = Path('.bumpversion.cfg')
78 3
        if not bumpversion_config_path.exists():
79 3
            user_supplied_versioned_file_paths = []
80
81 3
            version_file_path_answer = None
82 3
            input_terminator = '.'
83 3
            while not version_file_path_answer == input_terminator:
84 3
                version_file_path_answer = click.prompt(
85
                    'Enter a path to a file that contains a version number '
86
                    "(enter a path of '.' when you're done selecting files)",
87
                    type=click.Path(
88
                        exists=True,
89
                        dir_okay=True,
90
                        file_okay=True,
91
                        readable=True
92
                    )
93
                )
94
95 3
                if version_file_path_answer != input_terminator:
96 3
                    user_supplied_versioned_file_paths.append(version_file_path_answer)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (87/79).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
97
98 3
            bumpversion = cls(
99
                current_version=latest_version,
100
                version_files_to_replace=user_supplied_versioned_file_paths,
101
            )
102 3
            bumpversion.write_to_file(bumpversion_config_path)
103
104 3
        return bumpversion
105
106 3
    @classmethod
107 3
    def read_from_file(cls, config_path: Path):
108 3
        config = RawConfigParser('')
109 3
        config.readfp(config_path.open('rt', encoding='utf-8'))
110
111 3
        current_version = config.get("bumpversion", 'current_version')
112
113 3
        filenames = []
114 3
        for section_name in config.sections():
115
116 3
            section_name_match = re.compile("^bumpversion:(file|part):(.+)").match(section_name)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (96/79).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
117
118 3
            if not section_name_match:
119 3
                continue
120
121 3
            section_prefix, section_value = section_name_match.groups()
122
123 3
            if section_prefix == "file":
124 3
                filenames.append(section_value)
125
126 3
        return cls(
127
            current_version=current_version,
128
            version_files_to_replace=filenames,
129
        )
130
131 3
    def write_to_file(self, config_path: Path):
132 3
        bumpversion_cfg = textwrap.dedent(
133
            """\
134
            [bumpversion]
135
            current_version = {current_version}
136
137
            """
138
        ).format(**attr.asdict(self))
139
140 3
        bumpversion_files = '\n\n'.join([
141
            '[bumpversion:file:{}]'.format(file_name)
142
            for file_name in self.version_files_to_replace
143
        ])
144
145 3
        config_path.write_text(
146
            bumpversion_cfg + bumpversion_files
147
        )
148