-
Notifications
You must be signed in to change notification settings - Fork 7
/
commitinfo.go
123 lines (108 loc) · 4.82 KB
/
commitinfo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package commitinfo
import (
"fmt"
"github.com/lunarway/release-manager/internal/intent"
"github.com/lunarway/release-manager/internal/regexp"
"github.com/pkg/errors"
)
const (
FieldService = "Service"
FieldEnvironment = "Environment"
FieldArtifactID = "Artifact-ID"
FieldArtifactReleasedBy = "Artifact-released-by"
FieldArtifactCreatedBy = "Artifact-created-by"
)
type CommitInfo struct {
ArtifactID string
ArtifactCreatedBy PersonInfo
ReleasedBy PersonInfo
Service string
Environment string
Intent intent.Intent
}
func (i CommitInfo) String() string {
var releaseType string
switch i.Intent.Type {
case intent.TypeRollback:
releaseType = "rollback"
case intent.TypeAutoRelease:
releaseType = "auto release"
default:
releaseType = "release"
}
cci := ConventionalCommitInfo{
Message: fmt.Sprintf("[%s/%s] %s %s by %s", i.Environment, i.Service, releaseType, i.ArtifactID, i.ReleasedBy.Email),
Fields: []Field{
NewField(FieldService, i.Service),
NewField(FieldEnvironment, i.Environment),
NewField(FieldArtifactID, i.ArtifactID),
NewField(FieldArtifactReleasedBy, i.ReleasedBy.String()),
NewField(FieldArtifactCreatedBy, i.ArtifactCreatedBy.String()),
},
}
addIntentToConventionalCommitInfo(i.Intent, &cci)
return cci.String()
}
// ParseCommitInfo takes a full git commit message in the ConventionalCommit format and tries to extract release information from it
// It will extract it using conventional commit fields first, but if the correct fields isn't there it will fallback to parsing message
// for backward compatability reasons.
// The following backward-compatibility actions are done in the parsing (if not found in the fields!):
// * If `artifact` is written in title, the commit info is considered a "no match"
// * If `rollback` is written in title, the Intent is considered to be rollback intent, as well as the PreviousArtifactID is attempted
// to be extracted
// * Environment and Service name is extracted from the `[<env>/<service>]`-brackets in the title
// * User email in title is interpreted as releaser
func ParseCommitInfo(commitMessage string) (CommitInfo, error) {
convInfo, err := ParseConventionalCommit(commitMessage)
if err != nil {
return CommitInfo{}, err
}
matches := parseCommitInfoFromCommitMessageRegex.FindStringSubmatch(convInfo.Message)
if matches == nil && !convInfo.HasField(FieldReleaseIntent) {
return CommitInfo{}, errors.Wrap(ErrNoMatch, fmt.Sprintf("commit message '%s' do not have a Release-intent field and did not match expected message structure", convInfo.Message))
}
if matches != nil && matches[parseCommitInfoFromCommitMessageRegexLookup.Type] == "artifact" {
return CommitInfo{}, errors.Wrap(ErrNoMatch, fmt.Sprintf("commit type '%s' is not considered a match", matches[parseCommitInfoFromCommitMessageRegexLookup.Type]))
}
artifactCreatedBy, err := ParsePerson(convInfo.Field("Artifact-created-by"))
if err != nil && !errors.Is(err, ErrNoMatch) {
return CommitInfo{}, errors.Wrap(err, fmt.Sprintf("commit got unknown parsing error of %s with content '%s'", "Artifact-created-by", convInfo.Field("Artifact-created-by")))
}
releasedBy, err := ParsePerson(convInfo.Field("Artifact-released-by"))
if err != nil && !errors.Is(err, ErrNoMatch) {
return CommitInfo{}, errors.Wrap(err, fmt.Sprintf("commit got unknown parsing error of %s with content '%s'", "Artifact-released-by", convInfo.Field("Artifact-released-by")))
}
intentObj := parseIntent(convInfo, matches)
service := convInfo.Field(FieldService)
if matches != nil && service == "" {
service = matches[parseCommitInfoFromCommitMessageRegexLookup.Service]
}
environment := convInfo.Field(FieldEnvironment)
if matches != nil && environment == "" {
environment = matches[parseCommitInfoFromCommitMessageRegexLookup.Environment]
}
artifactID := convInfo.Field(FieldArtifactID)
if matches != nil && artifactID == "" {
artifactID = matches[parseCommitInfoFromCommitMessageRegexLookup.ArtifactID]
}
if matches != nil && releasedBy.Email == "" {
releasedBy = NewPersonInfo("", matches[parseCommitInfoFromCommitMessageRegexLookup.ReleaseByEmail])
}
return CommitInfo{
Intent: intentObj,
Service: service,
Environment: environment,
ArtifactID: artifactID,
ArtifactCreatedBy: artifactCreatedBy,
ReleasedBy: releasedBy,
}, nil
}
var parseCommitInfoFromCommitMessageRegexLookup = struct {
Environment int
Service int
ArtifactID int
PreviousArtifactID int
Type int
ReleaseByEmail int
}{}
var parseCommitInfoFromCommitMessageRegex = regexp.MustCompile(`^\[(?P<Environment>[^/]+)/(?P<Service>.*)\] (?P<Type>[a-z ]+)( (?P<PreviousArtifactID>[^ ]+) to)? (?P<ArtifactID>[^ ]+)( by (?P<ReleaseByEmail>.*))?$`, &parseCommitInfoFromCommitMessageRegexLookup)