forked from moby/buildkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dockerfile: generate lint rules documentation
Signed-off-by: CrazyMax <[email protected]>
- Loading branch information
1 parent
49b935f
commit 7283eab
Showing
6 changed files
with
302 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
frontend/dockerfile/docs/rules/file-consistent-command-casing.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
--- | ||
title: FileConsistentCommandCasing | ||
description: All commands within the Dockerfile should use the same casing (either upper or lower) | ||
--- | ||
|
||
Example warning: | ||
|
||
```text | ||
Command 'foo' should match the case of the command majority (uppercase) | ||
``` | ||
|
||
Instructions within a Dockerfile should have consistent casing through out the | ||
entire files. Instructions are not case-sensitive, but the convention is to use | ||
uppercase for instruction keywords to make it easier to distinguish keywords | ||
from arguments. | ||
|
||
Whether you prefer instructions to be uppercase or lowercase, you should make | ||
sure you use consistent casing to help improve readability of the Dockerfile. | ||
|
||
## Examples | ||
|
||
❌ Bad: mixing uppercase and lowercase | ||
|
||
```dockerfile | ||
FROM alpine:latest AS builder | ||
run apk --no-cache add build-base | ||
|
||
FROM builder AS build1 | ||
copy source1.cpp source.cpp | ||
``` | ||
|
||
✅ Good: all uppercase | ||
|
||
```dockerfile | ||
FROM alpine:latest AS builder | ||
RUN apk --no-cache add build-base | ||
|
||
FROM builder AS build1 | ||
COPY source1.cpp source.cpp | ||
``` | ||
|
||
✅ Good: all lowercase | ||
|
||
```dockerfile | ||
from alpine:latest as builder | ||
run apk --no-cache add build-base | ||
|
||
from builder as build1 | ||
copy source1.cpp source.cpp | ||
``` | ||
|
||
## Related errors | ||
|
||
- [`FromAsCasing`](./from-as-casing.md) | ||
|
49 changes: 49 additions & 0 deletions
49
frontend/dockerfile/linter/docs/FileConsistentCommandCasing.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
Example warning: | ||
|
||
```text | ||
Command 'foo' should match the case of the command majority (uppercase) | ||
``` | ||
|
||
Instructions within a Dockerfile should have consistent casing through out the | ||
entire files. Instructions are not case-sensitive, but the convention is to use | ||
uppercase for instruction keywords to make it easier to distinguish keywords | ||
from arguments. | ||
|
||
Whether you prefer instructions to be uppercase or lowercase, you should make | ||
sure you use consistent casing to help improve readability of the Dockerfile. | ||
|
||
## Examples | ||
|
||
❌ Bad: mixing uppercase and lowercase | ||
|
||
```dockerfile | ||
FROM alpine:latest AS builder | ||
run apk --no-cache add build-base | ||
|
||
FROM builder AS build1 | ||
copy source1.cpp source.cpp | ||
``` | ||
|
||
✅ Good: all uppercase | ||
|
||
```dockerfile | ||
FROM alpine:latest AS builder | ||
RUN apk --no-cache add build-base | ||
|
||
FROM builder AS build1 | ||
COPY source1.cpp source.cpp | ||
``` | ||
|
||
✅ Good: all lowercase | ||
|
||
```dockerfile | ||
from alpine:latest as builder | ||
run apk --no-cache add build-base | ||
|
||
from builder as build1 | ||
copy source1.cpp source.cpp | ||
``` | ||
|
||
## Related errors | ||
|
||
- [`FromAsCasing`](./from-as-casing.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//go:build ignore | ||
// +build ignore | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"go/ast" | ||
"go/parser" | ||
"go/token" | ||
"log" | ||
"os" | ||
"path" | ||
"regexp" | ||
"strings" | ||
"text/template" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
type Rule struct { | ||
Name string | ||
Description string | ||
} | ||
|
||
const tmplStr = `--- | ||
title: {{.Rule.Name}} | ||
description: {{.Rule.Description}} | ||
--- | ||
{{.Content}} | ||
` | ||
|
||
var destDir string | ||
|
||
func main() { | ||
if len(os.Args) < 2 { | ||
panic("Please provide a destination directory") | ||
} | ||
destDir = os.Args[1] | ||
log.Printf("Destination directory: %s\n", destDir) | ||
if err := run(destDir); err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func run(destDir string) error { | ||
if err := os.MkdirAll(destDir, 0700); err != nil { | ||
return err | ||
} | ||
rules, err := listRules() | ||
if err != nil { | ||
return err | ||
} | ||
tmpl, err := template.New("rule").Parse(tmplStr) | ||
if err != nil { | ||
return err | ||
} | ||
for _, rule := range rules { | ||
if ok, err := genRuleDoc(rule, tmpl); err != nil { | ||
return errors.Wrapf(err, "Error generating docs for %s", rule.Name) | ||
} else if ok { | ||
log.Printf("Docs generated for %s\n", rule.Name) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func genRuleDoc(rule Rule, tmpl *template.Template) (bool, error) { | ||
mdfilename := fmt.Sprintf("docs/%s.md", rule.Name) | ||
content, err := os.ReadFile(mdfilename) | ||
if err != nil { | ||
return false, err | ||
} | ||
outputfile, err := os.Create(path.Join(destDir, fmt.Sprintf("%s.md", camelToKebab(rule.Name)))) | ||
if err != nil { | ||
return false, err | ||
} | ||
defer outputfile.Close() | ||
if err = tmpl.Execute(outputfile, struct { | ||
Rule Rule | ||
Content string | ||
}{ | ||
Rule: rule, | ||
Content: string(content), | ||
}); err != nil { | ||
return false, err | ||
} | ||
return true, nil | ||
} | ||
|
||
func listRules() ([]Rule, error) { | ||
fset := token.NewFileSet() | ||
node, err := parser.ParseFile(fset, "ruleset.go", nil, parser.ParseComments) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var rules []Rule | ||
ast.Inspect(node, func(n ast.Node) bool { | ||
switch x := n.(type) { | ||
case *ast.GenDecl: | ||
for _, spec := range x.Specs { | ||
if vSpec, ok := spec.(*ast.ValueSpec); ok { | ||
rule := Rule{} | ||
if cl, ok := vSpec.Values[0].(*ast.CompositeLit); ok { | ||
for _, elt := range cl.Elts { | ||
if kv, ok := elt.(*ast.KeyValueExpr); ok { | ||
switch kv.Key.(*ast.Ident).Name { | ||
case "Name": | ||
if basicLit, ok := kv.Value.(*ast.BasicLit); ok { | ||
rule.Name = strings.Trim(basicLit.Value, `"`) | ||
} | ||
case "Description": | ||
if basicLit, ok := kv.Value.(*ast.BasicLit); ok { | ||
rule.Description = strings.Trim(basicLit.Value, `"`) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
rules = append(rules, rule) | ||
} | ||
} | ||
} | ||
return true | ||
}) | ||
return rules, nil | ||
} | ||
|
||
func camelToKebab(s string) string { | ||
var re = regexp.MustCompile(`([a-z])([A-Z])`) | ||
return strings.ToLower(re.ReplaceAllString(s, `${1}-${2}`)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# syntax=docker/dockerfile:1 | ||
|
||
ARG GO_VERSION=1.21 | ||
ARG ALPINE_VERSION=3.20 | ||
|
||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest | ||
|
||
FROM golatest AS docsgen | ||
WORKDIR /src | ||
ENV CGO_ENABLED=0 | ||
RUN --mount=target=. \ | ||
--mount=target=/root/.cache,type=cache \ | ||
--mount=target=/go/pkg/mod,type=cache \ | ||
go build -mod=vendor -o /out/docsgen ./frontend/dockerfile/linter/generate.go | ||
|
||
FROM alpine AS gen | ||
RUN apk add --no-cache rsync git | ||
WORKDIR /src | ||
COPY --from=docsgen /out/docsgen /usr/bin | ||
RUN --mount=target=/context \ | ||
--mount=target=.,type=tmpfs <<EOT | ||
set -ex | ||
rsync -a /context/. . | ||
cd frontend/dockerfile/linter | ||
docsgen ./dist | ||
mkdir /out | ||
cp -r dist/* /out | ||
EOT | ||
|
||
FROM scratch AS update | ||
COPY --from=gen /out / | ||
|
||
FROM gen AS validate | ||
RUN --mount=target=/context \ | ||
--mount=target=.,type=tmpfs <<EOT | ||
set -e | ||
rsync -a /context/. . | ||
git add -A | ||
rm -rf frontend/dockerfile/docs/rules/* | ||
cp -rf /out/* ./frontend/dockerfile/docs/rules/ | ||
if [ -n "$(git status --porcelain -- frontend/dockerfile/docs/rules/)" ]; then | ||
echo >&2 'ERROR: Dockerfile docs result differs. Please update with "make docs-dockerfile"' | ||
git status --porcelain -- frontend/dockerfile/docs/rules/ | ||
exit 1 | ||
fi | ||
EOT |