diff --git a/.golangci.yml b/.golangci.yml index 1b151e6..514a43c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,10 +4,13 @@ run: tests: true linters: - default: none + default: standard enable: + - errcheck - govet - ineffassign + - staticcheck + - unused settings: dupl: threshold: 100 diff --git a/internal/sse/raw_stream_token_replay_test.go b/internal/sse/raw_stream_token_replay_test.go index 3ba0c29..ddf0d7c 100644 --- a/internal/sse/raw_stream_token_replay_test.go +++ b/internal/sse/raw_stream_token_replay_test.go @@ -3,6 +3,7 @@ package sse import ( "bufio" "encoding/json" + "errors" "os" "path/filepath" "strconv" @@ -32,7 +33,10 @@ func TestRawStreamSamplesTokenReplay(t *testing.T) { if err != nil { t.Fatalf("read sample: %v", err) } - parsedTokens, expectedTokens := replayAndCollectTokens(string(raw)) + parsedTokens, expectedTokens, err := replayAndCollectTokens(string(raw)) + if err != nil { + t.Fatalf("replay token collection failed: %v", err) + } if expectedTokens <= 0 { t.Fatalf("expected positive token usage from raw stream, got %d", expectedTokens) } @@ -47,9 +51,10 @@ func TestRawStreamSamplesTokenReplay(t *testing.T) { } } -func replayAndCollectTokens(raw string) (parsedTokens int, expectedTokens int) { +func replayAndCollectTokens(raw string) (parsedTokens int, expectedTokens int, err error) { currentType := "thinking" scanner := bufio.NewScanner(strings.NewReader(raw)) + scanner.Buffer(make([]byte, 0, 64*1024), 2*1024*1024) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if !strings.HasPrefix(line, "data:") { @@ -72,7 +77,13 @@ func replayAndCollectTokens(raw string) (parsedTokens int, expectedTokens int) { parsedTokens = res.OutputTokens } } - return parsedTokens, expectedTokens + if scanErr := scanner.Err(); scanErr != nil { + if errors.Is(scanErr, bufio.ErrTooLong) { + return 0, 0, errors.New("raw stream line exceeds 2MiB scanner limit") + } + return 0, 0, scanErr + } + return parsedTokens, expectedTokens, nil } func rawAccumulatedTokenUsage(v any) int { diff --git a/scripts/lint.sh b/scripts/lint.sh index af07c27..bb9c27a 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -9,9 +9,30 @@ BOOTSTRAP_VERSION="${GOLANGCI_LINT_VERSION:-v2.11.4}" BOOTSTRAP_BIN="${ROOT_DIR}/.tmp/golangci-lint-${BOOTSTRAP_VERSION}" bootstrap_golangci_lint() { - local version_no_v archive_url tmp_dir + local version_no_v os arch artifact archive_url tmp_dir version_no_v="${BOOTSTRAP_VERSION#v}" - archive_url="https://github.com/golangci/golangci-lint/releases/download/${BOOTSTRAP_VERSION}/golangci-lint-${version_no_v}-linux-amd64.tar.gz" + os="$(uname -s | tr '[:upper:]' '[:lower:]')" + arch="$(uname -m | tr '[:upper:]' '[:lower:]')" + + case "$os" in + linux|darwin|windows) ;; + *) + echo "unsupported OS for bootstrap: ${os}" >&2 + return 1 + ;; + esac + + case "$arch" in + x86_64|amd64) arch="amd64" ;; + aarch64|arm64) arch="arm64" ;; + *) + echo "unsupported architecture for bootstrap: ${arch}" >&2 + return 1 + ;; + esac + + artifact="${os}-${arch}" + archive_url="https://github.com/golangci/golangci-lint/releases/download/${BOOTSTRAP_VERSION}/golangci-lint-${version_no_v}-${artifact}.tar.gz" mkdir -p "${ROOT_DIR}/.tmp" tmp_dir="$(mktemp -d)" @@ -19,7 +40,7 @@ bootstrap_golangci_lint() { curl -sSfL "${archive_url}" -o "${tmp_dir}/golangci-lint.tar.gz" tar -xzf "${tmp_dir}/golangci-lint.tar.gz" -C "${tmp_dir}" - cp "${tmp_dir}/golangci-lint-${version_no_v}-linux-amd64/golangci-lint" "${BOOTSTRAP_BIN}" + cp "${tmp_dir}/golangci-lint-${version_no_v}-${artifact}/golangci-lint" "${BOOTSTRAP_BIN}" chmod +x "${BOOTSTRAP_BIN}" echo "bootstrapped golangci-lint ${BOOTSTRAP_VERSION} to ${BOOTSTRAP_BIN}" >&2 @@ -34,6 +55,22 @@ run_lint() { fi } +is_compatibility_error() { + case "$1" in + *"unknown command \"fmt\""*|\ + *"unknown command \"run\""*|\ + *"unknown flag"*|\ + *"no such flag"*|\ + *"unsupported version of the configuration"*|\ + *"can't load config"*) + return 0 + ;; + *) + return 1 + ;; + esac +} + # v2 separates formatters from linters; enforce both in one entrypoint. if lint_output="$(run_lint "$LINT_BIN" 2>&1)"; then [[ -n "$lint_output" ]] && echo "$lint_output" @@ -46,9 +83,20 @@ if [[ -n "${GOLANGCI_LINT_BIN:-}" ]]; then exit 1 fi -echo "default golangci-lint is incompatible; bootstrapping ${BOOTSTRAP_VERSION}..." >&2 +if ! is_compatibility_error "$lint_output"; then + echo "$lint_output" >&2 + exit 1 +fi + +echo "default golangci-lint appears incompatible; bootstrapping ${BOOTSTRAP_VERSION}..." >&2 if [[ ! -x "${BOOTSTRAP_BIN}" ]]; then bootstrap_golangci_lint fi -run_lint "${BOOTSTRAP_BIN}" +if lint_output="$(run_lint "${BOOTSTRAP_BIN}" 2>&1)"; then + [[ -n "$lint_output" ]] && echo "$lint_output" + exit 0 +fi + +echo "$lint_output" >&2 +exit 1