license_test.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tailscaleroot
  4. import (
  5. "bytes"
  6. "fmt"
  7. "io"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "testing"
  12. "tailscale.com/util/set"
  13. )
  14. func normalizeLineEndings(b []byte) []byte {
  15. return bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n"))
  16. }
  17. // TestLicenseHeaders checks that all Go files in the tree
  18. // directory tree have a correct-looking Tailscale license header.
  19. func TestLicenseHeaders(t *testing.T) {
  20. want := normalizeLineEndings([]byte(strings.TrimLeft(`
  21. // Copyright (c) Tailscale Inc & contributors
  22. // SPDX-License-Identifier: BSD-3-Clause
  23. `, "\n")))
  24. exceptions := set.Of(
  25. // Subprocess test harness code
  26. "util/winutil/testdata/testrestartableprocesses/main.go",
  27. "util/winutil/subprocess_windows_test.go",
  28. // WireGuard copyright
  29. "cmd/tailscale/cli/authenticode_windows.go",
  30. "wgengine/router/osrouter/ifconfig_windows.go",
  31. // noiseexplorer.com copyright
  32. "control/controlbase/noiseexplorer_test.go",
  33. // Generated eBPF management code
  34. "derp/xdp/bpf_bpfeb.go",
  35. "derp/xdp/bpf_bpfel.go",
  36. // Generated kube deepcopy funcs file starts with a Go build tag + an empty line
  37. "k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go",
  38. )
  39. err := filepath.Walk(".", func(path string, fi os.FileInfo, err error) error {
  40. if err != nil {
  41. return fmt.Errorf("path %s: %v", path, err)
  42. }
  43. if exceptions.Contains(filepath.ToSlash(path)) {
  44. return nil
  45. }
  46. base := filepath.Base(path)
  47. switch base {
  48. case ".git", "node_modules", "tempfork":
  49. return filepath.SkipDir
  50. }
  51. switch base {
  52. case "zsyscall_windows.go":
  53. // Generated code.
  54. return nil
  55. }
  56. if strings.HasSuffix(base, ".config.ts") {
  57. return nil
  58. }
  59. if strings.HasSuffix(base, "_string.go") {
  60. // Generated file from go:generate stringer
  61. return nil
  62. }
  63. ext := filepath.Ext(base)
  64. switch ext {
  65. default:
  66. return nil
  67. case ".go", ".ts", ".tsx":
  68. }
  69. buf := make([]byte, 512)
  70. f, err := os.Open(path)
  71. if err != nil {
  72. return err
  73. }
  74. defer f.Close()
  75. if n, err := io.ReadAtLeast(f, buf, 512); err != nil && err != io.ErrUnexpectedEOF {
  76. return err
  77. } else {
  78. buf = buf[:n]
  79. }
  80. buf = normalizeLineEndings(buf)
  81. bufNoTrunc := buf
  82. if i := bytes.Index(buf, []byte("\npackage ")); i != -1 {
  83. buf = buf[:i]
  84. }
  85. if bytes.Contains(buf, want) {
  86. return nil
  87. }
  88. if bytes.Contains(bufNoTrunc, []byte("BSD-3-Clause\npackage ")) {
  89. t.Errorf("file %s has license header as a package doc; add a blank line before the package line", path)
  90. return nil
  91. }
  92. t.Errorf("file %s is missing Tailscale copyright header:\n\n%s", path, want)
  93. return nil
  94. })
  95. if err != nil {
  96. t.Fatalf("Walk: %v", err)
  97. }
  98. }