Skip to content

Commit 3795c4b

Browse files
authored
Feature/license header (#153)
* updated license header check action * updated action * updated license checking logic * action update * action update * updated license commit * commented gpg sign for testing * updated git details * removed signing * Removed autofix * updated autofix * common action update * Implemented auto-fix option * binary update * Updated license action * dockerfile update * action update * updated image building * workflow update * docker command update * dockerfile update * dockerfile update * added entrypoint * updated dockerfile * fixed review comments * Removed unnecessary logs * Added documentation * doc update
1 parent 32c4272 commit 3795c4b

File tree

9 files changed

+246
-54
lines changed

9 files changed

+246
-54
lines changed

.github/workflows/check-license-header.yaml

Lines changed: 0 additions & 54 deletions
This file was deleted.

.github/workflows/go-common.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ name: Common Workflows
1111

1212
on:
1313
workflow_call:
14+
inputs:
15+
autofix:
16+
required: false
17+
type: string
1418

1519
# Set defaults
1620
env:
@@ -124,3 +128,14 @@ jobs:
124128
# dependabot_automerge:
125129
# name: Automerge Dependabot PRs
126130
# uses: dell/common-github-actions/.github/workflows/dependabot-automerge.yml@main
131+
132+
check-license-header:
133+
runs-on: ubuntu-latest
134+
steps:
135+
- name: Checkout repository
136+
uses: actions/checkout@v4
137+
138+
- name: Check license headers
139+
uses: dell/common-github-actions/license-checker@main
140+
with:
141+
autofix: false

license-checker/Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM golang:1.24
2+
3+
LABEL "com.github.actions.name"="license-checker"
4+
LABEL "com.github.actions.description"="Checks for license header"
5+
LABEL "com.github.actions.icon"="eye"
6+
LABEL "com.github.actions.color"="gray-dark"
7+
8+
LABEL version="1.0.0"
9+
10+
WORKDIR /app
11+
12+
COPY go.mod go.sum main.go LICENSE-HEADER.txt ./
13+
RUN go build .
14+
15+
ENTRYPOINT ["/app/license-checker"]

license-checker/LICENSE-HEADER.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
*
3+
* Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/

license-checker/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# License header checker GitHub Action
2+
3+
This GitHub Action can be used to check the license header for the source codes (.go files only).
4+
5+
To enable this Action, you can create a .yml file under your repo's .github/workflows directory.
6+
Simple example:
7+
8+
```yaml
9+
name: License Header Checker
10+
11+
on:
12+
push:
13+
branches: [ main ]
14+
pull_request:
15+
branches: [ main ]
16+
17+
jobs:
18+
19+
check-license-header:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
25+
- name: Check license headers
26+
uses: dell/common-github-actions/license-checker@main
27+
with:
28+
autofix: false
29+
```
30+
31+
Arguments described below -- all are optional:
32+
33+
`autofix` specifies whether to enable auto-fix the missing headers in the source code or not. This is optional. The default value will be `False` if nothing is supplied
34+
35+
Note: To run this action locally clone this repo and run main.go present inside license-checker
36+
1. To run in check mode : `./license-checker`
37+
2. To run in autofix mode : `./license-checker --auto-fix`

license-checker/action.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: "License Header Checker"
2+
description: "Checks for license header"
3+
4+
inputs:
5+
autofix:
6+
required: false
7+
description: "Enable autofix"
8+
9+
runs:
10+
using: "docker"
11+
image: "Dockerfile"
12+
args:
13+
- ${{ inputs.autofix }}

license-checker/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/dell/license-checker
2+
3+
go 1.24

license-checker/go.sum

Whitespace-only changes.

license-checker/main.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
*
3+
* Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package main
20+
21+
import (
22+
"bufio"
23+
"flag"
24+
"fmt"
25+
"io/fs"
26+
"os"
27+
"path/filepath"
28+
"regexp"
29+
"strings"
30+
)
31+
32+
func readLicenseHeader(filePath string) (string, error) {
33+
file, err := os.Open(filePath)
34+
if err != nil {
35+
return "", err
36+
}
37+
defer file.Close()
38+
39+
var headerLines []string
40+
scanner := bufio.NewScanner(file)
41+
for scanner.Scan() {
42+
headerLines = append(headerLines, scanner.Text())
43+
}
44+
if err := scanner.Err(); err != nil {
45+
return "", err
46+
}
47+
return strings.Join(headerLines, "\n"), nil
48+
}
49+
50+
func checkLicenseHeader(filePath, licenseHeader string) (bool, error) {
51+
file, err := os.Open(filePath)
52+
if err != nil {
53+
return false, err
54+
}
55+
defer file.Close()
56+
57+
scanner := bufio.NewScanner(file)
58+
headerLines := strings.Split(licenseHeader, "\n")
59+
for i := 0; scanner.Scan() && i < len(headerLines); i++ {
60+
if !strings.Contains(headerLines[i], "Copyright") {
61+
if strings.TrimSpace(scanner.Text()) != strings.TrimSpace(headerLines[i]) {
62+
return false, nil
63+
}
64+
} else {
65+
// Check for the copyright year using regex
66+
re := regexp.MustCompile(`Copyright © \d{4}`)
67+
if !re.MatchString(scanner.Text()) {
68+
return false, nil
69+
}
70+
}
71+
}
72+
return true, scanner.Err()
73+
}
74+
75+
func listGoFiles(root string) ([]string, error) {
76+
var files []string
77+
err := filepath.WalkDir(root, func(path string, dirEntry fs.DirEntry, err error) error {
78+
if err != nil {
79+
return err
80+
}
81+
if !dirEntry.IsDir() &&
82+
strings.HasSuffix(dirEntry.Name(), ".go") &&
83+
!strings.Contains(dirEntry.Name(), "mock") &&
84+
!strings.Contains(dirEntry.Name(), "generated") {
85+
files = append(files, path)
86+
}
87+
return nil
88+
})
89+
return files, err
90+
}
91+
func autofixLicenseHeader(filePath, licenseHeader string) error {
92+
input, err := os.ReadFile(filePath)
93+
if err != nil {
94+
return err
95+
}
96+
97+
output := licenseHeader + "\n\n" + string(input)
98+
return os.WriteFile(filePath, []byte(output), 0644)
99+
}
100+
101+
func main() {
102+
isAutofixEnabled := flag.Bool("auto-fix", false, "Autofix enabled")
103+
flag.Parse()
104+
105+
licenseFile := "/app/LICENSE-HEADER.txt" // Change this to the path of your license file
106+
107+
licenseHeader, err := readLicenseHeader(licenseFile)
108+
if err != nil {
109+
fmt.Println("Error reading license file:", err)
110+
return
111+
}
112+
113+
root := "." // Change this to the directory you want to search
114+
files, err := listGoFiles(root)
115+
if err != nil {
116+
fmt.Println("Error:", err)
117+
return
118+
}
119+
fmt.Println("Checking license header for the following files:")
120+
for _, file := range files {
121+
fmt.Println(file)
122+
}
123+
var hasLicense bool
124+
for _, file := range files {
125+
hasLicense, err = checkLicenseHeader(file, licenseHeader)
126+
if err != nil {
127+
fmt.Printf("Error checking file %s: %v\n", file, err)
128+
continue
129+
}
130+
if !hasLicense {
131+
fmt.Printf("Missing or incorrect license header: %s\n", file)
132+
// if auto-fix is enabled then only we will fix the license headers else just report valid header and exit
133+
if *isAutofixEnabled {
134+
err := autofixLicenseHeader(file, licenseHeader)
135+
if err != nil {
136+
fmt.Printf("Error updating license header for file %s: %v\n", file, err)
137+
} else {
138+
fmt.Printf("License header updated for file: %s\n", file)
139+
}
140+
}
141+
}
142+
}
143+
if !hasLicense {
144+
os.Exit(1)
145+
}
146+
}

0 commit comments

Comments
 (0)