Ignore every program a local go build leaves, and refuse a main package whose program is not ignored (issue 1577)
Twice in the registry milestone a module's program, built in its folder, was committed with git add -A and stayed in the branch's history. The ignore list was kept by hand and covered 56 of 229 programs. checks/gitignored now lists every main package's program (in its directory and at its Go module's root) and fails on one no ignore file names; the root .gitignore names each, anchored to its path. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
+229
-42
@@ -1,49 +1,236 @@
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
# Go tool bundles built in place (go build in a module's cmd/<name>-tools) are build output.
|
||||
modules/pacman/pacman-tools
|
||||
modules/slack/cmd/slack-tools/slack-tools
|
||||
modules/jetbrains-toolbox/cmd/toolbox-tools/toolbox-tools
|
||||
modules/messenger/cmd/messenger/messenger
|
||||
modules/mesh-watcher/cmd/mesh-watcher/mesh-watcher
|
||||
modules/mesh-conditions/mesh-conditions
|
||||
modules/minio/object-store
|
||||
modules/claude-code/cmd/claude-code/claude-code
|
||||
modules/dnsmasq/cmd/dnsmasq-tools/dnsmasq-tools
|
||||
# ...and the same built from the module's root (go build ./cmd/<name>), which names it after the module.
|
||||
modules/messenger/messenger
|
||||
modules/claude-code/claude-code
|
||||
modules/mesh-watcher/mesh-watcher
|
||||
modules/dnsmasq/dnsmasq
|
||||
modules/mesh-issues/cmd/mesh-issues/mesh-issues
|
||||
modules/mesh-issues/mesh-issues
|
||||
modules/mounts/cmd/mounts/mounts
|
||||
modules/mesh-knowledge/cmd/mesh-knowledge/mesh-knowledge
|
||||
modules/mesh-knowledge/mesh-knowledge
|
||||
modules/mesh-reads/mesh-reads
|
||||
modules/mesh-reads/cmd/mesh-reads/mesh-reads
|
||||
modules/mesh-files/mesh-files
|
||||
modules/mesh-files/cmd/mesh-files/mesh-files
|
||||
|
||||
# The provisioner client built in place (issue 671).
|
||||
modules/mesh-provisioner-client/cmd/mesh-provisioner-client/mesh-provisioner-client
|
||||
modules/brother/cmd/brother/brother
|
||||
modules/mesh-build-go/cmd/mesh-build-go/mesh-build-go
|
||||
modules/mesh-config/mesh-config
|
||||
modules/mesh-build-image/cmd/mesh-build-image/mesh-build-image
|
||||
*.test
|
||||
|
||||
# binaries a local go build leaves in a module root
|
||||
/checks/paths/paths
|
||||
# Every program a local `go build` leaves, in its main package's directory and at its Go module's root, each anchored to
|
||||
# its one path so it never hides source (issue 1577: twice in one milestone a module's program was committed). The
|
||||
# checks/gitignored test refuses a main package whose program no ignore file names: add its line here.
|
||||
/checks/agenthome/agenthome
|
||||
/checks/config/config
|
||||
/checks/deps/deps
|
||||
/checks/environment/environment
|
||||
/checks/imagebuild/imagebuild
|
||||
/checks/imageplace/imageplace
|
||||
/checks/locks/locks
|
||||
/checks/mailstore/mailstore
|
||||
/checks/noplacement/noplacement
|
||||
/checks/owned/owned
|
||||
/checks/particulars/particulars
|
||||
/checks/paths/paths
|
||||
/checks/predefined/predefined
|
||||
/checks/readable/readable
|
||||
/checks/removedpackages/removedpackages
|
||||
/checks/secrets/secrets
|
||||
/checks/sshdir/sshdir
|
||||
/checks/templates/templates
|
||||
/checks/toolchains/toolchains
|
||||
/checks/words/words
|
||||
/modules/adwaita/adwaita-tools
|
||||
/modules/adwaita/cmd/adwaita-tools/adwaita-tools
|
||||
/modules/artifact-store-tools/artifact-store-tools
|
||||
/modules/artifact-store-tools/cmd/artifact-store-tools/artifact-store-tools
|
||||
/modules/asus-zephyrus-g14/cmd/zephyrus/zephyrus
|
||||
/modules/asus-zephyrus-g14/zephyrus
|
||||
/modules/avahi/avahi-tools
|
||||
/modules/avahi/cmd/avahi-tools/avahi-tools
|
||||
/modules/blueman/blueman-tools
|
||||
/modules/blueman/cmd/blueman-tools/blueman-tools
|
||||
/modules/bluetooth/bluetooth-tools
|
||||
/modules/bluetooth/cmd/bluetooth-tools/bluetooth-tools
|
||||
/modules/brother/brother
|
||||
/modules/brother/cmd/brother/brother
|
||||
/modules/claude-code/claude-code
|
||||
/modules/claude-code/cmd/claude-code/claude-code
|
||||
/modules/claude-licence-manager/claude-licence-manager
|
||||
/modules/claude-licence-manager/cmd/claude-licence-manager/claude-licence-manager
|
||||
/modules/clipmenu/clipmenu-tools
|
||||
/modules/clipmenu/cmd/clipmenu-tools/clipmenu-tools
|
||||
/modules/cups/cmd/cups-tools/cups-tools
|
||||
/modules/cups/cups-tools
|
||||
/modules/dbus/cmd/dbus-tools/dbus-tools
|
||||
/modules/dbus/dbus-tools
|
||||
/modules/desk-channel/cmd/desk-channel/desk-channel
|
||||
/modules/desk-channel/desk-channel
|
||||
/modules/disk-load/cmd/disk-load/disk-load
|
||||
/modules/disk-load/disk-load
|
||||
/modules/distribution/cmd/distribution-tools/distribution-tools
|
||||
/modules/distribution/cmd/distribution/distribution
|
||||
/modules/distribution/distribution
|
||||
/modules/distribution/distribution-tools
|
||||
/modules/dmenu/cmd/dmenu-tools/dmenu-tools
|
||||
/modules/dmenu/dmenu-tools
|
||||
/modules/dnsmasq/cmd/dnsmasq-tools/dnsmasq-tools
|
||||
/modules/dnsmasq/dnsmasq-tools
|
||||
/modules/docker-compose/cmd/docker-compose-tools/docker-compose-tools
|
||||
/modules/docker-compose/docker-compose-tools
|
||||
/modules/docker/cmd/docker-tools/docker-tools
|
||||
/modules/docker/docker-tools
|
||||
/modules/dunst/cmd/dunst-tools/dunst-tools
|
||||
/modules/dunst/dunst-tools
|
||||
/modules/fail2ban/cmd/fail2ban-tools/fail2ban-tools
|
||||
/modules/fail2ban/fail2ban-tools
|
||||
/modules/feh/cmd/feh-tools/feh-tools
|
||||
/modules/feh/feh-tools
|
||||
/modules/flatpak/cmd/flatpak-tools/flatpak-tools
|
||||
/modules/flatpak/flatpak-tools
|
||||
/modules/fonts/cmd/fonts-tools/fonts-tools
|
||||
/modules/fonts/fonts-tools
|
||||
/modules/forticlient/cmd/forticlient-tools/forticlient-tools
|
||||
/modules/forticlient/forticlient-tools
|
||||
/modules/gitea/agent-teams
|
||||
/modules/gitea/agent-token
|
||||
/modules/gitea/cmd/agent-teams/agent-teams
|
||||
/modules/gitea/cmd/agent-token/agent-token
|
||||
/modules/gitea/cmd/forge-account-provider/forge-account-provider
|
||||
/modules/gitea/cmd/forge/forge
|
||||
/modules/gitea/cmd/package-registry/package-registry
|
||||
/modules/gitea/cmd/repositories/repositories
|
||||
/modules/gitea/forge
|
||||
/modules/gitea/forge-account-provider
|
||||
/modules/gitea/package-registry
|
||||
/modules/gitea/repositories
|
||||
/modules/gnome-keyring/cmd/gnome-keyring-tools/gnome-keyring-tools
|
||||
/modules/gnome-keyring/gnome-keyring-tools
|
||||
/modules/hostname/cmd/hostname-tools/hostname-tools
|
||||
/modules/hostname/hostname-tools
|
||||
/modules/i3/cmd/i3-tools/i3-tools
|
||||
/modules/i3/i3-tools
|
||||
/modules/i3status-rust/cmd/i3status-rust-tools/i3status-rust-tools
|
||||
/modules/i3status-rust/i3status-rust-tools
|
||||
/modules/kernel/cmd/kernel-tools/kernel-tools
|
||||
/modules/kernel/kernel-tools
|
||||
/modules/keycloak/cmd/keycloak-provider/keycloak-provider
|
||||
/modules/keycloak/keycloak-provider
|
||||
/modules/lemurs/cmd/lemurs-tools/lemurs-tools
|
||||
/modules/lemurs/lemurs-tools
|
||||
/modules/localization/cmd/localization-tools/localization-tools
|
||||
/modules/localization/localization-tools
|
||||
/modules/logrotate/cmd/logrotate-tools/logrotate-tools
|
||||
/modules/logrotate/logrotate-tools
|
||||
/modules/mailu/automx
|
||||
/modules/mailu/cmd/automx/automx
|
||||
/modules/memory-pressure/cmd/memory-pressure/memory-pressure
|
||||
/modules/memory-pressure/memory-pressure
|
||||
/modules/mesh-build-go/buildstep/cmd/mesh-pack/mesh-pack
|
||||
/modules/mesh-build-go/buildstep/cmd/seatgen/seatgen
|
||||
/modules/mesh-build-go/buildstep/mesh-pack
|
||||
/modules/mesh-build-go/cmd/mesh-build-go/mesh-build-go
|
||||
/modules/mesh-build-go/mesh-build-go
|
||||
/modules/mesh-build-image/cmd/mesh-build-image/mesh-build-image
|
||||
/modules/mesh-build-image/mesh-build-image
|
||||
/modules/mesh-build-node/cmd/mesh-build-node/mesh-build-node
|
||||
/modules/mesh-build-node/mesh-build-node
|
||||
/modules/mesh-cli/cmd/mesh-cli/mesh-cli
|
||||
/modules/mesh-cli/mesh-cli
|
||||
/modules/mesh-conditions/cmd/mesh-conditions/mesh-conditions
|
||||
/modules/mesh-conditions/mesh-conditions
|
||||
/modules/mesh-config/cmd/mesh-config/mesh-config
|
||||
/modules/mesh-config/mesh-config
|
||||
/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/mesh-delivery-gate
|
||||
/modules/mesh-delivery-gate/mesh-delivery-gate
|
||||
modules/mesh-config/cmd/mesh-config/mesh-config
|
||||
|
||||
# Built by their modules' build step (issue 1577), never committed.
|
||||
modules/forticlient/cmd/forticlient-tools/forticlient-tools
|
||||
modules/networkmanager/uplink-tools
|
||||
checks/words/words
|
||||
modules/mesh-cli/mesh-cli
|
||||
modules/mesh-cli/cmd/mesh-cli/mesh-cli
|
||||
/modules/mesh-files/cmd/mesh-files/mesh-files
|
||||
/modules/mesh-files/mesh-files
|
||||
/modules/mesh-hand-acts/cmd/mesh-hand-acts/mesh-hand-acts
|
||||
/modules/mesh-hand-acts/mesh-hand-acts
|
||||
/modules/mesh-healers/cmd/mesh-healers/mesh-healers
|
||||
/modules/mesh-healers/mesh-healers
|
||||
/modules/mesh-issues/cmd/mesh-issues/mesh-issues
|
||||
/modules/mesh-issues/mesh-issues
|
||||
/modules/mesh-knowledge/cmd/mesh-knowledge/mesh-knowledge
|
||||
/modules/mesh-knowledge/mesh-knowledge
|
||||
/modules/mesh-mail-notifier/cmd/mesh-mail-notifier/mesh-mail-notifier
|
||||
/modules/mesh-mail-notifier/mesh-mail-notifier
|
||||
/modules/mesh-mail-reader/cmd/mesh-mail-reader/mesh-mail-reader
|
||||
/modules/mesh-mail-reader/mesh-mail-reader
|
||||
/modules/mesh-mail-web/cmd/mesh-mail-web/mesh-mail-web
|
||||
/modules/mesh-mail-web/mesh-mail-web
|
||||
/modules/mesh-mail/cmd/mesh-mail/mesh-mail
|
||||
/modules/mesh-mail/mesh-mail
|
||||
/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/mesh-provisioner-client
|
||||
/modules/mesh-provisioner-client/mesh-provisioner-client
|
||||
/modules/mesh-provisioner/cmd/mesh-provisioner/mesh-provisioner
|
||||
/modules/mesh-provisioner/mesh-provisioner
|
||||
/modules/mesh-reads/cmd/mesh-reads/mesh-reads
|
||||
/modules/mesh-reads/mesh-reads
|
||||
/modules/mesh-self-check/cmd/mesh-self-check/mesh-self-check
|
||||
/modules/mesh-self-check/mesh-self-check
|
||||
/modules/mesh-vault/cmd/mesh-vault/mesh-vault
|
||||
/modules/mesh-vault/mesh-vault
|
||||
/modules/mesh-watcher/cmd/mesh-watcher/mesh-watcher
|
||||
/modules/mesh-watcher/mesh-watcher
|
||||
/modules/messenger/cmd/messenger/messenger
|
||||
/modules/messenger/messenger
|
||||
/modules/minio/cmd/object-store/object-store
|
||||
/modules/minio/object-store
|
||||
/modules/mounts/cmd/mounts/mounts
|
||||
/modules/mounts/mounts
|
||||
/modules/nats/cmd/mesh-nats-snapshot/mesh-nats-snapshot
|
||||
/modules/nats/cmd/nats-tools/nats-tools
|
||||
/modules/nats/mesh-nats-snapshot
|
||||
/modules/nats/nats-tools
|
||||
/modules/netcheck/cmd/netcheck/netcheck
|
||||
/modules/netcheck/netcheck
|
||||
/modules/networkmanager/cmd/uplink-tools/uplink-tools
|
||||
/modules/networkmanager/uplink-tools
|
||||
/modules/nextcloud-client/cmd/nextcloud-client-tools/nextcloud-client-tools
|
||||
/modules/nextcloud-client/nextcloud-client-tools
|
||||
/modules/nfs-server/cmd/nfs-server/nfs-server
|
||||
/modules/nfs-server/nfs-server
|
||||
/modules/nm-applet/cmd/nm-applet-tools/nm-applet-tools
|
||||
/modules/nm-applet/nm-applet-tools
|
||||
/modules/only-office/cmd/only-office/only-office
|
||||
/modules/only-office/only-office
|
||||
/modules/openrazer/cmd/openrazer-tools/openrazer-tools
|
||||
/modules/openrazer/openrazer-tools
|
||||
/modules/pacman/cmd/pacman-tools/pacman-tools
|
||||
/modules/pacman/pacman-tools
|
||||
/modules/picom/cmd/picom-tools/picom-tools
|
||||
/modules/picom/picom-tools
|
||||
/modules/polychromatic/cmd/polychromatic-tools/polychromatic-tools
|
||||
/modules/polychromatic/polychromatic-tools
|
||||
/modules/postgres/cmd/postgres-provider/postgres-provider
|
||||
/modules/postgres/postgres-provider
|
||||
/modules/power/cmd/power/power
|
||||
/modules/power/power
|
||||
/modules/records/cmd/records/records
|
||||
/modules/records/records
|
||||
/modules/restic/cmd/restic-backups/restic-backups
|
||||
/modules/restic/restic-backups
|
||||
/modules/rofi/cmd/rofi-tools/rofi-tools
|
||||
/modules/rofi/rofi-tools
|
||||
/modules/route-adapter/cmd/route-provider/route-provider
|
||||
/modules/route-adapter/route-provider
|
||||
/modules/screen-lock/cmd/screen-lock-tools/screen-lock-tools
|
||||
/modules/screen-lock/screen-lock-tools
|
||||
/modules/sensors/cmd/sensors/sensors
|
||||
/modules/sensors/sensors
|
||||
/modules/slack/cmd/slack-tools/slack-tools
|
||||
/modules/slack/slack-tools
|
||||
/modules/snapd/cmd/snapd-tools/snapd-tools
|
||||
/modules/snapd/snapd-tools
|
||||
/modules/ssh-client/cmd/ssh-client-tools/ssh-client-tools
|
||||
/modules/ssh-client/ssh-client-tools
|
||||
/modules/sudo/cmd/sudo-tools/sudo-tools
|
||||
/modules/sudo/sudo-tools
|
||||
/modules/systemd-networkd/cmd/uplink-tools/uplink-tools
|
||||
/modules/systemd-networkd/uplink-tools
|
||||
/modules/systemd-resolved/cmd/resolver-tools/resolver-tools
|
||||
/modules/systemd-resolved/resolver-tools
|
||||
/modules/systemd/cmd/systemd-tools/systemd-tools
|
||||
/modules/systemd/systemd-tools
|
||||
/modules/telegram/cmd/telegram/telegram
|
||||
/modules/telegram/telegram
|
||||
/modules/time-sync/cmd/time-sync-tools/time-sync-tools
|
||||
/modules/time-sync/time-sync-tools
|
||||
/modules/triggerhappy/cmd/triggerhappy-tools/triggerhappy-tools
|
||||
/modules/triggerhappy/triggerhappy-tools
|
||||
/modules/xclip/cmd/xclip-tools/xclip-tools
|
||||
/modules/xclip/xclip-tools
|
||||
/modules/xdg/cmd/xdg-tools/xdg-tools
|
||||
/modules/xdg/xdg-tools
|
||||
/modules/xorg/cmd/xorg/xorg
|
||||
/modules/xorg/xorg
|
||||
/modules/xterm/cmd/xterm-tools/xterm-tools
|
||||
/modules/xterm/xterm-tools
|
||||
/modules/zfs/cmd/zfs-tools/zfs-tools
|
||||
/modules/zfs/zfs-tools
|
||||
|
||||
@@ -122,3 +122,171 @@ func Find(root string, dirs []string) ([]Hidden, error) {
|
||||
}
|
||||
return hidden, nil
|
||||
}
|
||||
|
||||
// Programs are the paths `go build` writes a compiled program to, for every main package of every Go module under
|
||||
// root (issue 1577, met twice in one milestone: a module's program committed with `git add -A`): `go build` in the
|
||||
// package's directory writes it there, and `go build ./<dir>` from the module's root writes it at the root, each named
|
||||
// after the package's import path. Relative to root, sorted; directories named node_modules, testdata or vendor, and
|
||||
// .git, are left out, and so is a path that is a directory of the tree.
|
||||
func Programs(root string) ([]string, error) {
|
||||
var mods []string
|
||||
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() && skipped(d.Name()) && p != root {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !d.IsDir() && d.Name() == "go.mod" {
|
||||
mods = append(mods, filepath.Dir(p))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, m := range mods {
|
||||
modPath, err := modulePath(filepath.Join(m, "go.mod"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = filepath.WalkDir(m, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if p != m {
|
||||
if skipped(d.Name()) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(p, "go.mod")); err == nil {
|
||||
return filepath.SkipDir // a module of its own, walked on its own
|
||||
}
|
||||
}
|
||||
isMain, err := mainPackage(p)
|
||||
if err != nil || !isMain {
|
||||
return err
|
||||
}
|
||||
name := filepath.Base(p)
|
||||
if p == m {
|
||||
name = pathBase(modPath)
|
||||
}
|
||||
for _, dir := range []string{p, m} {
|
||||
out := filepath.Join(dir, name)
|
||||
if st, err := os.Stat(out); err == nil && st.IsDir() {
|
||||
continue // go build refuses to write over a directory, so no program lands there
|
||||
}
|
||||
rel, _ := filepath.Rel(root, out)
|
||||
seen[filepath.ToSlash(rel)] = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
out := make([]string, 0, len(seen))
|
||||
for p := range seen {
|
||||
out = append(out, p)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Unignored names each of programs (paths from root) that root's ignore files do not ignore: a program a local build
|
||||
// leaves there that `git add -A` would commit.
|
||||
func Unignored(root string, programs []string) ([]string, error) {
|
||||
gitDir, err := os.MkdirTemp("", "gitignored-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(gitDir)
|
||||
if out, err := exec.Command("git", "init", "-q", "--bare", gitDir).CombinedOutput(); err != nil {
|
||||
return nil, fmt.Errorf("git init: %v: %s", err, out)
|
||||
}
|
||||
abs, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command("git", "--git-dir="+gitDir, "--work-tree="+abs, "-c", "core.excludesFile=/dev/null",
|
||||
"check-ignore", "--no-index", "-v", "-n", "-z", "--stdin")
|
||||
cmd.Dir = abs
|
||||
cmd.Stdin = strings.NewReader(strings.Join(programs, "\x00") + "\x00")
|
||||
out, err := cmd.Output()
|
||||
if ee, ok := err.(*exec.ExitError); !(err == nil || ok && ee.ExitCode() == 1) {
|
||||
return nil, fmt.Errorf("git check-ignore: %w", err)
|
||||
}
|
||||
// -v -n -z answers source, line, pattern and path for every path asked; the source is empty for one no pattern
|
||||
// matches, and a pattern starting with ! keeps the path.
|
||||
f := strings.Split(strings.TrimRight(string(out), "\x00"), "\x00")
|
||||
var open []string
|
||||
for i := 0; i+3 < len(f); i += 4 {
|
||||
if f[i+2] == "" || strings.HasPrefix(f[i+2], "!") {
|
||||
open = append(open, f[i+3])
|
||||
}
|
||||
}
|
||||
return open, nil
|
||||
}
|
||||
|
||||
func skipped(name string) bool {
|
||||
switch name {
|
||||
case ".git", "node_modules", "testdata", "vendor":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// modulePath reads the module line of a go.mod.
|
||||
func modulePath(goMod string) (string, error) {
|
||||
b, err := os.ReadFile(goMod)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, l := range strings.Split(string(b), "\n") {
|
||||
if f := strings.Fields(l); len(f) >= 2 && f[0] == "module" {
|
||||
return strings.Trim(f[1], `"`), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("%s names no module", goMod)
|
||||
}
|
||||
|
||||
// pathBase is the name `go build` gives a program at a module's root: its path's last element, a major version
|
||||
// suffix (/v2) left out.
|
||||
func pathBase(modPath string) string {
|
||||
parts := strings.Split(modPath, "/")
|
||||
last := parts[len(parts)-1]
|
||||
if len(parts) > 1 && len(last) > 1 && last[0] == 'v' && strings.Trim(last[1:], "0123456789") == "" {
|
||||
last = parts[len(parts)-2]
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
// mainPackage is whether dir holds a Go file, not a test, of package main.
|
||||
func mainPackage(dir string) (bool, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, e := range entries {
|
||||
n := e.Name()
|
||||
if e.IsDir() || filepath.Ext(n) != ".go" || strings.HasSuffix(n, "_test.go") {
|
||||
continue
|
||||
}
|
||||
b, err := os.ReadFile(filepath.Join(dir, n))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, l := range strings.Split(string(b), "\n") {
|
||||
if f := strings.Fields(l); len(f) >= 2 && f[0] == "package" {
|
||||
if f[1] == "main" {
|
||||
return true, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -78,3 +78,81 @@ func TestNoIgnorePatternHidesAModulesSource(t *testing.T) {
|
||||
t.Errorf("%s is hidden by %s: a module's source is never ignored; anchor the pattern to the binary (/<name>, /cmd/<name>/<name>)", h.Path, h.Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// The kind of issue 1577, met twice in one milestone: a module's program `go build` left in its folder was committed.
|
||||
// Every program a main package builds, in its own directory and at its module's root, is found, and one no ignore file
|
||||
// names is open; a directory of the same name is no program's place.
|
||||
func TestEveryProgramABuildLeavesIsIgnored(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"modules/m/go.mod": "module example.com/m/v2\n",
|
||||
"modules/m/main.go": "// a program\npackage main\n",
|
||||
"modules/m/cmd/m-tools/main.go": "package main\n",
|
||||
"modules/m/cmd/m-tools/main_test.go": "package main\n",
|
||||
"modules/m/internal/x/x.go": "package x\n",
|
||||
"modules/m/internal/x/x_test.go": "package main\n",
|
||||
"modules/m/node_modules/y/y.go": "package main\n",
|
||||
"modules/m/tool/go.mod": "module tool\n",
|
||||
"modules/m/tool/cmd/tool/main.go": "package main\n",
|
||||
"modules/m/tool/tool/source.go": "package tool\n",
|
||||
"checks/c/go.mod": "module example.com/checks/c\n",
|
||||
"checks/c/main.go": "package main\n",
|
||||
}
|
||||
for name, c := range map[string]struct {
|
||||
ignore string
|
||||
open []string
|
||||
}{
|
||||
"nothing ignored": {"", []string{"checks/c/c", "modules/m/cmd/m-tools/m-tools", "modules/m/m", "modules/m/m-tools",
|
||||
"modules/m/tool/cmd/tool/tool"}},
|
||||
"each anchored": {"/checks/c/c\n/modules/m/cmd/m-tools/m-tools\n/modules/m/m\n/modules/m/m-tools\n/modules/m/tool/cmd/tool/tool\n", nil},
|
||||
"one kept by a negation": {"/checks/c/c\n/modules/m/*\n!/modules/m/m\n/modules/m/cmd/m-tools/m-tools\n/modules/m/tool/cmd/tool/tool\n",
|
||||
[]string{"modules/m/m"}},
|
||||
} {
|
||||
root := t.TempDir()
|
||||
for f, b := range files {
|
||||
p := filepath.Join(root, f)
|
||||
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(p, []byte(b), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, ".gitignore"), []byte(c.ignore), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
programs, err := Programs(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := "checks/c/c,modules/m/cmd/m-tools/m-tools,modules/m/m,modules/m/m-tools,modules/m/tool/cmd/tool/tool"
|
||||
if got := strings.Join(programs, ","); got != want {
|
||||
t.Fatalf("%s: programs %s, want %s", name, got, want)
|
||||
}
|
||||
open, err := Unignored(root, programs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Join(open, ",") != strings.Join(c.open, ",") {
|
||||
t.Errorf("%s: open %v, want %v", name, open, c.open)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The catalogue's own tree: every program a local build leaves is ignored, so `git add -A` never takes one.
|
||||
func TestNoProgramABuildLeavesIsCommitted(t *testing.T) {
|
||||
root := filepath.Join("..", "..")
|
||||
programs, err := Programs(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(programs) == 0 {
|
||||
t.Fatal("no main package found: the check reads nothing")
|
||||
}
|
||||
open, err := Unignored(root, programs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, p := range open {
|
||||
t.Errorf("%s is the program a local go build leaves, and no ignore file names it: add /%s to the catalogue's .gitignore", p, p)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user