treewide: add gofmt linter

Change-Id: Ic0d8450b46790c4dc797b117634227be9a48de01
Reviewed-on: https://review.monogon.dev/c/monogon/+/3827
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/build/analysis/gofmt/BUILD.bazel b/build/analysis/gofmt/BUILD.bazel
new file mode 100644
index 0000000..2c5835b
--- /dev/null
+++ b/build/analysis/gofmt/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "gofmt",
+    srcs = ["gofmt.go"],
+    importpath = "source.monogon.dev/build/analysis/gofmt",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//build/analysis/lib",
+        "@com_github_golangci_gofmt//gofmt",
+        "@org_golang_x_tools//go/analysis",
+    ],
+)
diff --git a/build/analysis/gofmt/gofmt.go b/build/analysis/gofmt/gofmt.go
new file mode 100644
index 0000000..817ce05
--- /dev/null
+++ b/build/analysis/gofmt/gofmt.go
@@ -0,0 +1,55 @@
+package gofmt
+
+import (
+	"bytes"
+	"os"
+
+	"github.com/golangci/gofmt/gofmt"
+	"golang.org/x/tools/go/analysis"
+
+	alib "source.monogon.dev/build/analysis/lib"
+)
+
+var Analyzer = &analysis.Analyzer{
+	Name: "gofmt",
+	Doc:  "checks if files have been run through `gofmt -s`",
+	Run: func(pass *analysis.Pass) (any, error) {
+		for _, f := range pass.Files {
+			if alib.IsGeneratedFile(f) {
+				continue
+			}
+
+			fileName := pass.Fset.PositionFor(f.Pos(), true).Filename
+			src, err := os.ReadFile(fileName)
+			if err != nil {
+				return nil, err
+			}
+
+			res, err := gofmt.Source(fileName, src, gofmt.Options{NeedSimplify: true})
+			if err != nil {
+				return nil, err
+			}
+
+			if bytes.Equal(src, res) {
+				continue
+			}
+
+			pass.Report(analysis.Diagnostic{
+				Pos:     f.Pos(),
+				Message: "not formatted with gofmt -s",
+				SuggestedFixes: []analysis.SuggestedFix{
+					{
+						TextEdits: []analysis.TextEdit{
+							{
+								Pos:     f.FileStart,
+								End:     f.FileEnd,
+								NewText: res,
+							},
+						},
+					},
+				},
+			})
+		}
+		return nil, nil
+	},
+}