mirror of
https://github.com/tailscale/tailscale.git
synced 2026-04-12 02:18:00 -04:00
Add misc/install-git-hooks.go and misc/git_hook/ to the OSS repo, adapted from the corp repo. The primary motivation is Change-Id generation in commit messages, which provides a persistent identifier for a change across cherry-picks between branches. The installer uses "git rev-parse --git-common-dir" instead of go-git to find the hooks directory, avoiding a new direct dependency while still supporting worktrees. Hooks included: - commit-msg: adds Change-Id trailer - pre-commit: blocks NOCOMMIT / DO NOT SUBMIT markers - pre-push: blocks local-directory replace directives in go.mod - post-checkout: warns when the hook binary is outdated Also update docs/commit-messages.md to reflect that Change-Id is no longer optional in the OSS repo. Updates tailscale/corp#39860 Change-Id: I09066b889118840c0ec6995cc03a9cf464740ffa Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
83 lines
1.7 KiB
Go
83 lines
1.7 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build ignore
|
|
|
|
// The install-git-hooks program installs git hooks.
|
|
//
|
|
// It installs a Go binary at .git/hooks/ts-git-hook and a pre-hook
|
|
// forwarding shell wrapper to .git/hooks/NAME.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
var hooks = []string{
|
|
"pre-push",
|
|
"pre-commit",
|
|
"commit-msg",
|
|
"post-checkout",
|
|
}
|
|
|
|
func fatalf(format string, a ...any) {
|
|
log.SetFlags(0)
|
|
log.Fatalf("install-git-hooks: "+format, a...)
|
|
}
|
|
|
|
func main() {
|
|
out, err := exec.Command("git", "rev-parse", "--git-common-dir").CombinedOutput()
|
|
if err != nil {
|
|
fatalf("finding git dir: %v, %s", err, out)
|
|
}
|
|
gitDir := strings.TrimSpace(string(out))
|
|
|
|
hookDir := filepath.Join(gitDir, "hooks")
|
|
if fi, err := os.Stat(hookDir); err != nil {
|
|
fatalf("checking hooks dir: %v", err)
|
|
} else if !fi.IsDir() {
|
|
fatalf("%s is not a directory", hookDir)
|
|
}
|
|
|
|
buildOut, err := exec.Command(goBin(), "build",
|
|
"-o", filepath.Join(hookDir, "ts-git-hook"+exe()),
|
|
"./misc/git_hook").CombinedOutput()
|
|
if err != nil {
|
|
log.Fatalf("go build git-hook: %v, %s", err, buildOut)
|
|
}
|
|
|
|
for _, hook := range hooks {
|
|
content := fmt.Sprintf(hookScript, hook)
|
|
file := filepath.Join(hookDir, hook)
|
|
// Install the hook. If it already exists, overwrite it, in case there's
|
|
// been changes.
|
|
if err := os.WriteFile(file, []byte(content), 0755); err != nil {
|
|
fatalf("%v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
const hookScript = `#!/usr/bin/env bash
|
|
exec "$(dirname ${BASH_SOURCE[0]})/ts-git-hook" %s "$@"
|
|
`
|
|
|
|
func goBin() string {
|
|
if p, err := exec.LookPath("go"); err == nil {
|
|
return p
|
|
}
|
|
return "go"
|
|
}
|
|
|
|
func exe() string {
|
|
if runtime.GOOS == "windows" {
|
|
return ".exe"
|
|
}
|
|
return ""
|
|
}
|