From 0588c85b8fee718ffed0091975f36be869063e45 Mon Sep 17 00:00:00 2001 From: Jabberwocky238 <7176656@qq.com> Date: Thu, 30 Jul 2026 01:07:37 -0400 Subject: [PATCH] add binary build workflow and hook policy --- .github/workflows/deploy.yml | 55 ++++++++++++++++++++++++++ Dockerfile | 1 + README.md | 23 +++++++++++ common/hooks.go | 77 ------------------------------------ common/hooks_test.go | 47 ---------------------- hook.sh | 17 ++++++-- 6 files changed, 93 insertions(+), 127 deletions(-) create mode 100644 .github/workflows/deploy.yml delete mode 100644 common/hooks.go delete mode 100644 common/hooks_test.go diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..281460e --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,55 @@ +name: Build binaries + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build ${{ matrix.goos }}-${{ matrix.goarch }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + suffix: "" + - goos: darwin + goarch: amd64 + suffix: "" + - goos: windows + goarch: amd64 + suffix: .exe + + steps: + - name: Check out source + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Test + run: go test ./... + + - name: Build + env: + CGO_ENABLED: "0" + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + run: go build -trimpath -ldflags="-s -w" -o "dist/simplegit-${GOOS}-${GOARCH}${{ matrix.suffix }}" ./cmd + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: simplegit-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/simplegit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.suffix }} + if-no-files-found: error + retention-days: 14 diff --git a/Dockerfile b/Dockerfile index a54f1d2..94a4f73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update \ WORKDIR /app COPY --from=build /out/simplegit /app/simplegit +COPY simplegit/hook.sh /app/hook.sh EXPOSE 8080 2222 ENTRYPOINT ["/app/simplegit"] # Identity/JWT are verified in-process; no -auth-url. The Ed25519 signing key diff --git a/README.md b/README.md index a3ea16c..1d2ff56 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,29 @@ hook.sh [Git hook 原始参数...] `-hook-script=/path/to/production-hook.sh` 替换。simplegit 不解释或转发 hook 事件,具体效果完全由该脚本负责。 +仓库自带的 `hook.sh` 只向 `/hooks.log` 追加 hook 类型、仓库、参数和 stdin, +不访问任何外部服务,因此 standalone simplegit 不依赖 CI。 + +simpleci 提供集成适配器 `pkgs/simpleci/simplegit-hook.sh`。联合部署让 simplegit +显式使用该脚本,并配置: + +```sh +export SIMPLECI_URL=http://simpleci:8095 +export SIMPLECI_HOOK_TOKEN=replace-with-a-shared-secret +export SIMPLECI_WORKFLOW=.github/workflows/deploy.yml # 可选 +simplegit daemon -hook-script /path/to/simplegit-hook.sh +``` + +tag push 和分支删除不会触发构建;simpleci 不可用时通知最多等待 5 秒,且不影响 +push 的成功结果。simpleci 使用相同 secret 启动: + +```sh +simpleci -hook-token replace-with-a-shared-secret +``` + +`SIMPLECI_HOOK_TOKEN` 和 `-hook-token` 都为空时不启用鉴权,适合仅在受信任的 +本机或内部网络开发;生产环境应从同一个 Secret 分别注入两端。 + --- ## 目录结构 diff --git a/common/hooks.go b/common/hooks.go deleted file mode 100644 index a759175..0000000 --- a/common/hooks.go +++ /dev/null @@ -1,77 +0,0 @@ -package common - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" -) - -var gitHookNames = []string{ - "pre-receive", "update", "post-receive", "post-update", "proc-receive", - "push-to-checkout", "reference-transaction", "pre-applypatch", - "post-applypatch", "pre-commit", "pre-merge-commit", "prepare-commit-msg", - "commit-msg", "post-commit", "pre-rebase", "post-checkout", "post-merge", - "pre-push", "pre-auto-gc", "post-rewrite", "sendemail-validate", - "fsmonitor-watchman", "p4-pre-submit", "p4-post-changelist", - "p4-prepare-changelist", "p4-changelist", -} - -const hookWrapper = `#!/bin/sh -# Generated by simplegit. The operator controls hook behavior with -hook-script. -test -n "$SIMPLEGIT_HOOK_SCRIPT" || exit 0 -repo_dir=$(pwd -P) -repo=${repo_dir#"$SIMPLEGIT_REPO_ROOT"/} -repo=${repo%%.git} -exec "$SIMPLEGIT_HOOK_SCRIPT" %s "$repo" "$@" -` - -// EnsureHookTemplate creates the git-init template used for new repositories. -// All hook entrypoints delegate to one environment-specific external script. -func EnsureHookTemplate(root string) (string, error) { - dir := filepath.Join(root, "template") - hooksDir := filepath.Join(dir, "hooks") - if err := os.MkdirAll(hooksDir, 0o755); err != nil { - return "", fmt.Errorf("create hook template: %w", err) - } - for _, name := range gitHookNames { - path := filepath.Join(hooksDir, name) - if err := os.WriteFile(path, []byte(fmt.Sprintf(hookWrapper, name)), 0o755); err != nil { - return "", fmt.Errorf("write hook %s: %w", name, err) - } - } - if err := refreshRepositoryHooks(filepath.Join(root, "repos"), hooksDir); err != nil { - return "", err - } - return dir, nil -} - -// refreshRepositoryHooks updates repositories created with an older template. -func refreshRepositoryHooks(reposRoot, templateHooks string) error { - if _, err := os.Stat(reposRoot); os.IsNotExist(err) { - return nil - } - return filepath.WalkDir(reposRoot, func(path string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - if !entry.IsDir() || !strings.HasSuffix(entry.Name(), ".git") { - return nil - } - dst := filepath.Join(path, "hooks") - if err := os.MkdirAll(dst, 0o755); err != nil { - return err - } - for _, name := range gitHookNames { - body, err := os.ReadFile(filepath.Join(templateHooks, name)) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(dst, name), body, 0o755); err != nil { - return err - } - } - return filepath.SkipDir - }) -} diff --git a/common/hooks_test.go b/common/hooks_test.go deleted file mode 100644 index e16728d..0000000 --- a/common/hooks_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package common - -import ( - "os" - "path/filepath" - "strings" - "testing" -) - -func TestEnsureHookTemplatePassesTypeAndRepo(t *testing.T) { - root := t.TempDir() - dir, err := EnsureHookTemplate(root) - if err != nil { - t.Fatal(err) - } - body, err := os.ReadFile(filepath.Join(dir, "hooks", "post-receive")) - if err != nil { - t.Fatal(err) - } - text := string(body) - for _, want := range []string{`post-receive "$repo" "$@"`, `repo=${repo_dir#"$SIMPLEGIT_REPO_ROOT"/}`, `repo=${repo%.git}`} { - if !strings.Contains(text, want) { - t.Errorf("generated hook missing %q:\n%s", want, text) - } - } -} - -func TestEnsureHookTemplateRefreshesExistingRepos(t *testing.T) { - root := t.TempDir() - hook := filepath.Join(root, "repos", "alice", "demo.git", "hooks", "post-receive") - if err := os.MkdirAll(filepath.Dir(hook), 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(hook, []byte("old"), 0o755); err != nil { - t.Fatal(err) - } - if _, err := EnsureHookTemplate(root); err != nil { - t.Fatal(err) - } - body, err := os.ReadFile(hook) - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(body), `post-receive "$repo"`) { - t.Fatalf("existing hook was not refreshed: %s", body) - } -} diff --git a/hook.sh b/hook.sh index 70f220b..8bf604b 100755 --- a/hook.sh +++ b/hook.sh @@ -1,5 +1,16 @@ #!/bin/sh -# Default simplegit hook policy. Deployments can replace it with -hook-script. -# $1 is the Git hook type, $2 is the owner/name repository, remaining arguments -# are the original Git hook arguments. Original stdin and GIT_* env are intact. +# Default standalone policy: append the hook invocation and its stdin to a log. +# Integrations can supply another script with simplegit daemon -hook-script. + +log_file=${SIMPLEGIT_HOOK_LOG:-${SIMPLEGIT_REPO_ROOT%/repos}/hooks.log} +{ + printf '%s\t%s\t%s' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" "$2" + shift 2 + for arg in "$@"; do + printf '\t%s' "$arg" + done + printf '\n' + sed 's/^/stdin\t/' +} >>"$log_file" 2>/dev/null || true + exit 0