Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
e26143661c | |||
64260f301a | |||
9b1a6a928e | |||
c84b1ecd26 | |||
8981c8367e | |||
19b376ac40 | |||
044823552b | |||
2c943b845f | |||
ce06de60eb | |||
80ab70da04 | |||
6d9c48078c | |||
da4a8b3030 | |||
72b5dd18f3 | |||
7ded9bab13 | |||
3253d5ac63 | |||
142ef2e8f8 | |||
50c451f69e | |||
c9fd83e531 | |||
5fd5b36be7 | |||
6c118fb9ff | |||
aa1601e751 | |||
09380fb8d0 | |||
|
d0e7c0bec7 |
7 changed files with 165 additions and 0 deletions
21
.gitea/workflows/pr.yml
Normal file
21
.gitea/workflows/pr.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: "Lint Pull Request"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint Pr Title
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: github.base_ref != 'release'
|
||||
|
||||
steps:
|
||||
- name: Install go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.2
|
||||
|
||||
- name: Run Pull Request Lint Action
|
||||
uses: https://git.kjan.de/actions/pull-request-lint@main
|
5
action.yml
Normal file
5
action.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
name: 'Pull Request Lint'
|
||||
description: 'A simple Gitea action written in go'
|
||||
runs:
|
||||
using: 'go'
|
||||
main: 'main.go'
|
|
@ -1,3 +1,10 @@
|
|||
## [0.2.1](https://git.kjan.de/actions/pull-request-lint/compare/v0.2.0...v0.2.1) (2025-04-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix release pipeline ([bc0bf00](https://git.kjan.de/actions/pull-request-lint/commit/bc0bf00947c563652b674cab947f863c5c477dd2))
|
||||
|
||||
# [0.2.0](https://git.kjan.de/actions/pull-request-lint/compare/v0.1.0...v0.2.0) (2025-04-09)
|
||||
|
||||
|
||||
|
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module git.kjan.de/actions/pull-request-lint
|
||||
|
||||
go 1.24.2
|
0
go.sum
Normal file
0
go.sum
Normal file
83
internal/validation/validation.go
Normal file
83
internal/validation/validation.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ValidateConventionalCommit(commit string) error {
|
||||
// Regex to match the commit format
|
||||
// type(scope)!: description
|
||||
// or
|
||||
// type!: description
|
||||
// or
|
||||
// type: description
|
||||
re := regexp.MustCompile(`^(?P<type>[a-z]+)(?P<scope>\([a-z]+\))?(?P<breaking>!)?: (?P<description>[a-z].+)$`)
|
||||
match := re.FindStringSubmatch(commit)
|
||||
|
||||
if len(match) == 0 {
|
||||
return fmt.Errorf("Invalid PR title")
|
||||
}
|
||||
|
||||
typeIndex := re.SubexpIndex("type")
|
||||
scopeIndex := re.SubexpIndex("scope")
|
||||
breakingIndex := re.SubexpIndex("breaking")
|
||||
descriptionIndex := re.SubexpIndex("description")
|
||||
|
||||
commitType := match[typeIndex]
|
||||
scope := match[scopeIndex]
|
||||
breaking := match[breakingIndex]
|
||||
description := match[descriptionIndex]
|
||||
|
||||
// Type MUST be lowercase
|
||||
if commitType != strings.ToLower(commitType) {
|
||||
return fmt.Errorf("type must be lowercase")
|
||||
}
|
||||
|
||||
// Description MUST start with lowercase
|
||||
if description != strings.ToLower(description) {
|
||||
return fmt.Errorf("description must start with lowercase")
|
||||
}
|
||||
|
||||
// Scope MUST be lowercase
|
||||
if scope != "" && scope != strings.ToLower(scope) {
|
||||
return fmt.Errorf("scope must be lowercase")
|
||||
}
|
||||
|
||||
// Check for breaking change indicator
|
||||
hasBreakingChangeFooter := strings.Contains(commit, "BREAKING CHANGE:")
|
||||
if breaking == "!" && hasBreakingChangeFooter {
|
||||
return fmt.Errorf("breaking change indicator and footer are mutually exclusive")
|
||||
}
|
||||
|
||||
lines := strings.Split(commit, "\n")
|
||||
if len(lines) > 1 {
|
||||
body := strings.Join(lines[1:], "\n")
|
||||
// Check if body is separated from description by a blank line
|
||||
if !strings.HasPrefix(body, "\n") {
|
||||
return fmt.Errorf("body must be separated from description by a blank line")
|
||||
}
|
||||
}
|
||||
|
||||
// Check for footers
|
||||
footerRegex := regexp.MustCompile(`(?m)^(?P<token>[A-Za-z-]+|BREAKING CHANGE): (?P<value>.*)$`)
|
||||
footerMatches := footerRegex.FindAllStringSubmatch(commit, -1)
|
||||
|
||||
for _, footerMatch := range footerMatches {
|
||||
tokenIndex := footerRegex.SubexpIndex("token")
|
||||
token := footerMatch[tokenIndex]
|
||||
|
||||
// BREAKING-CHANGE MUST be synonymous with BREAKING CHANGE
|
||||
if token == "BREAKING-CHANGE" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Token MUST use - in place of whitespace characters, except for BREAKING CHANGE
|
||||
if token != "BREAKING CHANGE" && strings.Contains(token, " ") {
|
||||
return fmt.Errorf("footer token must use - in place of whitespace characters")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
46
main.go
Normal file
46
main.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.kjan.de/actions/pull-request-lint/internal/validation"
|
||||
)
|
||||
|
||||
type GithubEvent struct {
|
||||
PullRequest struct {
|
||||
Title string `json:"title"`
|
||||
} `json:"pull_request"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
eventPath := os.Getenv("GITHUB_EVENT_PATH")
|
||||
if eventPath == "" {
|
||||
fmt.Println("GITHUB_EVENT_PATH not set")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
eventFile, err := os.Open(eventPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error opening %s: %v\n", eventPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer eventFile.Close()
|
||||
|
||||
var event GithubEvent
|
||||
decoder := json.NewDecoder(eventFile)
|
||||
err = decoder.Decode(&event)
|
||||
if err != nil {
|
||||
fmt.Printf("Error decoding event.json: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
prTitle := event.PullRequest.Title
|
||||
semanticValidationErr := validation.ValidateConventionalCommit(prTitle)
|
||||
if semanticValidationErr != nil {
|
||||
fmt.Println(semanticValidationErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue