victorzhuk/skills

z-go-entrypoint

Thin Go main and testable run for func main, cmd/ packages, CLI startup, or service bootstrap.

查看源码

使用方法

  1. 复制安装命令,在自己的智能体环境安装前先检查源码包。
  2. 从原始文档核对所需工具、凭据和支持的宿主。
  3. 说明输入材料与期望交付物,并对照来源检查输出。
npx skills add https://github.com/victorzhuk/skills --skill z-go-entrypoint

开始前需了解

  • 安装不包含模型订阅、API 额度或访问文件的权限。
  • 仓库星数和安装量反映活跃情况,不等于独立质量或安全测试。
仓库原始内容

按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。

Go Entrypoint

main reads the process and exits. run does the work and returns an error. Every testable behavior lives in run.

Thin main

Five statements, no logic. It owns the only os.Exit in the call graph.

go
// cmd/app/main.go
package main

import (
    "context"
    "fmt"
    "io"
    "os"
)

func main() {
    ctx := context.Background()
    if err := run(ctx, os.Args[1:], os.Stdout, os.Stderr); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

Map other failures to distinct codes here (flag errors and context.Canceled → 2 and 130) — nothing below main can exit.

Testable run

go
// cmd/app/run.go
package main

import (
    "context"
    "errors"
    "flag"
    "fmt"
    "io"
    "net/http"
    "os"
    "os/signal"
    "syscall"

    "example.com/app/internal/app"
)

type config struct {
    addr    string
    jsonOut bool
}

func run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
    cfg, rest, err := parseArgs(args, stderr)
    if err != nil {
        return err
    }
    if len(rest) > 0 {
        return fmt.Errorf("unexpected arguments: %v", rest)
    }

    ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
    defer stop()

    a, err := app.New(app.Config{Addr: cfg.addr}, stdout, stderr)
    if err != nil {
        return fmt.Errorf("construct app: %w", err)
    }

    if err := a.Serve(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
        return fmt.Errorf("serve: %w", err)
    }
    return nil
}

Order is fixed: parse → cancel on signal → construct → run. Parsing before the signal context keeps a bad flag from installing a handler; defer stop() releases the channel registration so the binary can exit promptly.

HTTP server: timeouts + drain

Inside run, build the server with inbound timeouts and drain it on cancel:

go
srv := &http.Server{
    Addr:              cfg.addr,
    Handler:           handler,
    ReadHeaderTimeout: 5 * time.Second,
    ReadTimeout:       10 * time.Second,
    WriteTimeout:      15 * time.Second,
    IdleTimeout:       60 * time.Second,
    MaxHeaderBytes:    1 << 20,
}

errCh := make(chan error, 1)
go func() {
    if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
        errCh <- err
    }
}()

select {
case <-ctx.Done():
case err := <-errCh:
    return fmt.Errorf("serve: %w", err)
}

shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
    return fmt.Errorf("shutdown: %w", err)
}

Shutdown order is fixed: stop accepting → drain in-flight requests → close dependencies. The signal context from signal.NotifyContext above is the only trigger; no new dependencies.

Flags

flag.NewFlagSet with ContinueOnError and output bound to the injected stderr — the default writes to os.Stderr, which a test cannot capture.

go
func parseArgs(args []string, stderr io.Writer) (*config, []string, error) {
    cfg := &config{}
    fs := flag.NewFlagSet("app", flag.ContinueOnError)
    fs.SetOutput(stderr)
    fs.StringVar(&cfg.addr, "addr", ":8080", "listen address")
    fs.BoolVar(&cfg.jsonOut, "json", false, "emit JSON")
    if err := fs.Parse(args); err != nil {
        return nil, nil, err
    }
    return cfg, fs.Args(), nil // positional args validated here
}

flag.ExitOnError calls os.Exit(2) on a bad flag: it kills the test binary. --help writes usage to stderr and returns flag.ErrHelp — return it as-is and let main choose the exit code.

Construction

Build the app in run through a domain constructor (newApp, app.New), not by opening connections in main. The constructor takes explicit config and streams, returns an error instead of panicking, and registers nothing globally.

Signals

signal.NotifyContext derives from the ctx passed in, so a test deadline still applies and signal.Stop runs on return. Register os.Interrupt and syscall.SIGTERM — the second is what a container runtime sends on stop.

Output

Write to the injected stdout/stderr. Program output goes to stdout; diagnostics, usage, and the top-level error message go to stderr. Splitting them keeps app --json | jq clean.

Quick Reference

ElementRule
mainthin; passes args, streams, ctx; owns os.Exit
runrun(ctx, args []string, stdout, stderr io.Writer) error
Flagsflag.NewFlagSet(name, flag.ContinueOnError), SetOutput(stderr)
Positional argsvalidated after fs.Parse
Signalssignal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer stop()
ConstructionnewApp(cfg, stdout, stderr) (app, error) inside run
Streamsinjected io.Writer, never os.Stdout/os.Stderr below main
Errorswrap with %w; return up; main prints and exits
Server timeoutsReadHeaderTimeout/ReadTimeout/WriteTimeout/IdleTimeout + MaxHeaderBytes at construction
Shutdownsrv.Shutdown with a bounded context; stop accepting → drain → close deps
Testingcall run with []string and bytes.Buffer

Do not

  • os.Exit inside run or any package it calls — return an error instead.
  • Read os.Args, os.Stdout, or os.Stderr inside run; they are parameters.
  • Package-level flag.Parse — it reads the real process state and exits on error.
  • Build the app in main, or store it in a package-level var for later reuse.
  • Unit-test main() directly — it exits the test runner; test run instead.
  • Mutate os.Args in a test to drive behavior; pass args to run.
  • Treat TestMain(m *testing.M) as a main() test — it is a per-test-binary

setup hook that calls m.Run(), unrelated to entrypoint shape.

  • Register signals without defer stop() — the goroutine outlives the run.

Verify

sh
go build ./... && timeout 5m go test -timeout 2m ./cmd/...

Related skills

  • [[z-go-cobra-patterns]] — command trees built on top of this split
  • [[z-go-clean-arch-di]] — constructor placement and layer direction
  • [[z-go-context]] — cancellation and deadline propagation
  • [[z-go-errors]] — wrapping, sentinels, errors.Is on shutdown
  • [[z-go-testing]] — table tests, parallelism, goleak
  • [[z-go-observability]] — logging and metrics setup inside run
来自同一仓库

更多 Skills

全部 Skills
victorzhuk
社区

z-accessibility

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-algo-core

DS/algorithm selection judgment for production code, not competitive tricks — hash map for membership/dedup, slice+sort beats a tree until proven, heap for top-k, two-pointer/sliding-window for scans, BFS/DFS/topo-sort for dependency shapes. Triggers on Big-O, hash map vs tree. Does not cover profiling/tuning; see [[z-go-performance]].

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-bdd

BDD acceptance contracts for humans and agents across UI, API, and CLI products. Use for Gherkin, Given-When-Then, E2E acceptance, or scenario evidence. Define repeatable scenarios with explicit assertions and required outcomes. Test selection belongs to [[z-testing-strategy]]. Go harness wiring belongs to [[z-go-bdd]].

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-codegraph

Use an existing CodeGraph integration for call graphs, impact analysis, affected tests, or route discovery. Discover installed tools and index freshness; follow host LSP precedence and verify current source. Do not initialize indexes without user authorization.

安装量
1
GitHub Stars
0
最近更新
10月22日