• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / version-bumper / 11860111625

15 Nov 2024 04:45PM UTC coverage: 66.911% (-21.1%) from 88.054%
11860111625

Pull #12

github

web-flow
Merge f526460ea into 70d8e62f5
Pull Request #12: [FEATURE] Introduce version range detector component

9 of 173 new or added lines in 14 files covered. (5.2%)

1 existing line in 1 file now uncovered.

457 of 683 relevant lines covered (66.91%)

2.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Version/VersionRangeDetector.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Composer package "eliashaeussler/version-bumper".
7
 *
8
 * Copyright (C) 2024 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\VersionBumper\Version;
25

26
use EliasHaeussler\VersionBumper\Config;
27
use EliasHaeussler\VersionBumper\Enum;
28
use EliasHaeussler\VersionBumper\Exception;
29
use GitElephant\Command;
30
use GitElephant\Objects;
31
use GitElephant\Repository;
32
use RuntimeException;
33

34
use function array_filter;
35
use function array_map;
36
use function array_values;
37
use function iterator_to_array;
38
use function sprintf;
39

40
/**
41
 * VersionRangeDetector.
42
 *
43
 * @author Elias Häußler <elias@haeussler.dev>
44
 * @license GPL-3.0-or-later
45
 */
46
final class VersionRangeDetector
47
{
NEW
48
    public function __construct(
×
49
        private readonly ?Command\Caller\CallerInterface $caller = null,
NEW
50
    ) {}
×
51

52
    /**
53
     * @param list<Config\VersionRangeIndicator> $indicators
54
     *
55
     * @throws Exception\CannotFetchGitCommits
56
     * @throws Exception\CannotFetchGitTag
57
     * @throws Exception\CannotFetchLatestGitTag
58
     * @throws Exception\GitTagDoesNotExist
59
     * @throws Exception\NoGitTagsFound
60
     */
NEW
61
    public function detect(string $rootPath, array $indicators, ?string $since = null): ?Enum\VersionRange
×
62
    {
NEW
63
        $repository = new Repository($rootPath);
×
NEW
64
        $detectedRanges = [];
×
65

66
        // Inject custom repository caller
NEW
67
        if (null !== $this->caller) {
×
NEW
68
            $repository->setCaller($this->caller);
×
69
        }
70

71
        // Fetch tag used for comparison
NEW
72
        if (null !== $since) {
×
NEW
73
            $tag = $this->fetchTag($since, $repository) ?? throw new Exception\GitTagDoesNotExist($since);
×
74
        } else {
NEW
75
            $tag = $this->fetchLatestVersionTag($repository) ?? throw new Exception\NoGitTagsFound();
×
76
        }
77

78
        // Fetch relevant Git information
NEW
79
        $commitMessages = $this->fetchCommitMessages($tag, $repository);
×
NEW
80
        $diff = $repository->getDiff();
×
81

NEW
82
        $detectors = [
×
NEW
83
            new RangeDetection\CommitMessagesRangeDetection($commitMessages),
×
NEW
84
            new RangeDetection\DiffRangeDetection($diff),
×
NEW
85
        ];
×
86

NEW
87
        foreach ($indicators as $indicator) {
×
NEW
88
            $strategy = $indicator->strategy();
×
NEW
89
            $matchedRangeDetections = 0;
×
NEW
90
            $unmatchedRangeDetections = 0;
×
91

92
            // Evaluate all configured patterns
NEW
93
            foreach ($indicator->patterns() as $pattern) {
×
NEW
94
                foreach ($detectors as $detector) {
×
NEW
95
                    if (!$detector->supports($pattern)) {
×
NEW
96
                        continue;
×
97
                    }
98

NEW
99
                    if ($detector->matches($pattern)) {
×
NEW
100
                        ++$matchedRangeDetections;
×
101
                    } else {
NEW
102
                        ++$unmatchedRangeDetections;
×
103
                    }
104
                }
105
            }
106

107
            // Evaluate matched detections against configured strategy
NEW
108
            $indicatorMatches = match ($strategy) {
×
NEW
109
                Enum\VersionRangeIndicatorStrategy::MatchAll => 0 === $unmatchedRangeDetections,
×
NEW
110
                Enum\VersionRangeIndicatorStrategy::MatchAny => $matchedRangeDetections > 0,
×
NEW
111
                Enum\VersionRangeIndicatorStrategy::MatchNone => 0 === $matchedRangeDetections,
×
NEW
112
            };
×
113

114
            // Add range if indicator matches
NEW
115
            if ($indicatorMatches) {
×
NEW
116
                $detectedRanges[] = $indicator->range();
×
117
            }
118
        }
119

NEW
120
        if ([] !== $detectedRanges) {
×
NEW
121
            return Enum\VersionRange::getHighest(...$detectedRanges);
×
122
        }
123

NEW
124
        return null;
×
125
    }
126

127
    /**
128
     * @throws Exception\CannotFetchGitTag
129
     */
NEW
130
    private function fetchTag(string $tagName, Repository $repository): ?Objects\Tag
×
131
    {
132
        try {
NEW
133
            return $repository->getTag($tagName);
×
NEW
134
        } catch (RuntimeException) {
×
NEW
135
            throw new Exception\CannotFetchGitTag($tagName);
×
136
        }
137
    }
138

139
    /**
140
     * @throws Exception\CannotFetchLatestGitTag
141
     */
NEW
142
    private function fetchLatestVersionTag(Repository $repository): ?Objects\Tag
×
143
    {
144
        try {
NEW
145
            return $repository->getLastTag();
×
NEW
146
        } catch (RuntimeException) {
×
NEW
147
            throw new Exception\CannotFetchLatestGitTag();
×
148
        }
149
    }
150

151
    /**
152
     * @return list<string>
153
     *
154
     * @throws Exception\CannotFetchGitCommits
155
     */
NEW
156
    private function fetchCommitMessages(Objects\Tag $tag, Repository $repository): array
×
157
    {
NEW
158
        $diff = sprintf('%s..HEAD', $tag);
×
159

160
        try {
NEW
161
            $logRange = $repository->getLogRange($tag->getFullRef(), 'HEAD', null, -1, 0);
×
NEW
162
        } catch (\Exception) {
×
NEW
163
            throw new Exception\CannotFetchGitCommits($diff);
×
164
        }
165

NEW
166
        return array_values(
×
NEW
167
            array_filter(
×
NEW
168
                array_map(
×
NEW
169
                    static fn (?Objects\Commit $commit) => $commit?->getMessage()?->getShortMessage(),
×
NEW
170
                    iterator_to_array($logRange),
×
NEW
171
                ),
×
NEW
172
                static fn (?string $message) => null !== $message,
×
NEW
173
            ),
×
NEW
174
        );
×
175
    }
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc