From f047ff11e10dab85c2868c9f2523a94c132981ce Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 01:54:51 +0700 Subject: [PATCH 1/6] ci: improve release pipeline with taskfile and bun - Replace Node.js/npx with Bun/bunx for semantic-release - Use Taskfile tasks in CI/release workflows (ci:test, ci:lint, release, release:proxy-warmup) - Add PR CI workflow (.github/workflows/ci.yml) with test + lint - Tighten release rules: only feat/fix/perf/refactor trigger releases - Hide non-code sections (docs/test/ci/chore/style) from release notes - Add Go module proxy warm-up after release - Bump Go version to 1.26 - Remove master branch references - Simplify MAINTAINING.md (remove v1 maintenance docs) - Fix pre-existing lint issues (goimports, gofumpt, misspellings) --- .github/RELEASE_GUIDE.md | 153 +++++------------ .github/workflows/ci.yml | 51 ++++++ .github/workflows/release.yml | 42 +++-- .releaserc.json | 63 +++---- MAINTAINING.md | 162 +++++------------- Taskfile.yml | 36 ++++ argo/builder/builder.go | 4 +- argo/builder/builder_test.go | 3 +- argo/builder/option.go | 3 +- argo/builder/option_test.go | 3 +- argo/builder/otel.go | 3 +- argo/builder/otel_test.go | 3 +- argo/builder/template/container.go | 3 +- argo/builder/template/container_test.go | 3 +- argo/builder/template/http.go | 1 + argo/builder/template/script.go | 3 +- argo/client.go | 3 +- argo/operations.go | 3 +- argo/operations_integration_test.go | 7 +- argo/operations_test.go | 3 +- argo/option_test.go | 3 +- argo/patterns/cicd.go | 1 + argo/patterns/cicd_test.go | 3 +- argo/patterns/parallel.go | 1 + argo/patterns/parallel_test.go | 3 +- base32/base32.go | 1 + base32/base32_test.go | 6 +- concurrent/execution.go | 2 +- db/migrations.go | 3 +- db/otel_integration_test.go | 3 +- db/pool.go | 3 +- db/pool_test.go | 3 +- docker/config.go | 1 + docker/config_test.go | 5 +- docker/executor_test.go | 3 +- docker/helpers_test.go | 5 +- docker/integration_test.go | 7 +- docker/logs_test.go | 3 +- docker/otel.go | 3 +- docker/otel_test.go | 3 +- docker/wait_test.go | 3 +- examples/base32/example.go | 12 +- examples/grpc/cmd/server/main.go | 2 +- examples/retry/example.go | 2 - .../temporal/scheduler/basic_scheduler.go | 2 +- examples/temporal/worker/basic_worker.go | 2 +- go.mod | 2 +- grpc/config.go | 3 +- grpc/config_test.go | 3 +- grpc/otel_instrumentation.go | 3 +- grpc/otel_instrumentation_test.go | 3 +- logging/logging.go | 2 +- logging/logging_test.go | 2 +- otel/config.go | 3 +- otel/config_test.go | 1 - otel/helper_test.go | 3 +- otel/instrumentation.go | 2 +- otel/logging.go | 3 +- otel/logging_test.go | 11 +- rest/client.go | 1 + rest/client_test.go | 1 + rest/middleware.go | 1 + rest/otel_middleware.go | 3 +- rest/otel_middleware_test.go | 3 +- retry/retry.go | 15 +- retry/retry_test.go | 4 +- server/server.go | 7 +- ssh/tunnel.go | 7 +- temporal/client.go | 3 +- temporal/client_integration_test.go | 3 +- temporal/e2e_integration_test.go | 5 +- temporal/schedule.go | 3 +- temporal/schedule_integration_test.go | 3 +- temporal/testcontainer/example_test.go | 3 +- temporal/worker.go | 3 +- temporal/worker_integration_test.go | 3 +- temporal/workflow.go | 3 +- temporal/workflow_integration_test.go | 3 +- 78 files changed, 383 insertions(+), 369 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/RELEASE_GUIDE.md b/.github/RELEASE_GUIDE.md index bf373da..7fad49d 100644 --- a/.github/RELEASE_GUIDE.md +++ b/.github/RELEASE_GUIDE.md @@ -1,20 +1,17 @@ # Release Guide -This guide explains how to ensure PR details appear in release notes when using semantic-release with a protected main branch. +This guide explains how to ensure PR details appear in release notes when using semantic-release. -## Problem +## How It Works -When merging PRs, only the PR title appears in releases: -- ❌ Release shows: "add Argo Workflows client library" -- ✅ Want: Full PR description with features, testing details, examples +1. PRs are squash-merged to `main` with conventional commit titles +2. `semantic-release` analyzes commit messages and determines version bump +3. GitHub release is created with categorized notes +4. Go module proxy is warmed for immediate availability -## Root Cause +## Getting Good Release Notes -`semantic-release` generates release notes from **commit messages**, not PR data. When you squash-merge, by default only the PR title becomes the commit message. - -## Solution: Configure Squash Merge to Include PR Body - -### Option 1: Repository Settings (Recommended) +### Configure Squash Merge (Repository Setting) 1. Go to: `https://github.com/jasoet/pkg/settings` 2. Scroll to **"Pull Requests"** section @@ -22,136 +19,74 @@ When merging PRs, only the PR title appears in releases: 4. Select: **"Default to pull request title and description"** 5. Save changes -This automatically includes PR body in squash commits. - -### Option 2: Manual Edit When Merging - -When clicking "Squash and merge": -1. GitHub shows the commit message editor -2. Copy your PR body content -3. Paste it below the title in the commit message -4. Complete the merge - -**Example commit message:** -``` -feat(argo): add Argo Workflows client library (#8) - -## Summary +This automatically includes PR body in squash commits, which becomes the release note content. -Adds a production-ready Argo Workflows client library to the pkg repository. +### What Triggers a Release -## Features +| Commit Type | Release | Hidden from Notes | +|---|---|---| +| `feat` | Minor | No | +| `fix` | Patch | No | +| `perf` | Patch | No | +| `refactor` | Patch | No | +| `docs`, `test`, `ci`, `chore`, `style`, `build` | None | Yes | +| `BREAKING CHANGE` footer or `!` suffix | Major | No | -- **Multiple Connection Modes**: Kubernetes API, in-cluster, and Argo Server HTTP -- **Flexible Configuration**: Config structs and functional options pattern -- **OpenTelemetry Integration**: Built-in tracing and observability support -- **Production-Ready**: Proper error handling without fatal errors +### PR Title Format -## Testing +Use [Conventional Commits](https://www.conventionalcommits.org/): -All unit tests pass: -\`\`\`bash -$ go test ./argo -PASS -ok github.com/jasoet/pkg/v2/argo 1.174s -\`\`\` ``` - -### Option 3: Use gh CLI with --body - -```bash -# Merge PR with body included -gh pr merge 123 --squash --body +feat(argo): add Argo Workflows client library +fix(compress): handle empty input gracefully +perf(db): reduce query allocations +refactor(otel): simplify provider setup +feat!: remove deprecated Config struct ``` -This prompts you to edit the commit message including the PR body. - -## How semantic-release Uses This - -1. **Commit analyzer** reads the commit messages (now with PR body) -2. **Release notes generator** creates sections based on commit type -3. **GitHub plugin** publishes the detailed release notes +### PR Body Best Practices -## Verification - -After merging with PR body included: - -1. Check commit log: `git log --format=fuller` -2. Verify commit body contains PR details -3. Wait for release workflow to complete -4. Check release notes at `https://github.com/jasoet/pkg/releases` - -## Best Practices - -### For PR Authors -- Write detailed PR descriptions with: - - Summary section - - Feature lists - - Testing details - - Migration examples - - Breaking changes - -### For Reviewers/Mergers -- Always include PR body when squash-merging -- Use conventional commit format in PR title -- Review the commit message before confirming merge - -## Example PR Template +Write detailed PR descriptions — they become the commit body on squash merge: ```markdown ## Summary Brief description of changes and motivation ## Changes -- ✅ Added feature X -- ✅ Updated component Y -- ✅ Fixed issue Z +- Added feature X +- Updated component Y +- Fixed issue Z ## Testing How changes were tested ## Breaking Changes List any breaking changes (or "None") - -## Migration Guide -How users should update their code (if needed) ``` -## Why Not Use @semantic-release/changelog? +## Configuration -The `@semantic-release/changelog` plugin writes CHANGELOG.md and commits it back to the repo. This **fails with protected branches** because: -- Protected branches require PRs for all commits -- semantic-release runs after merge, can't create another PR -- Workflow fails: `refusing to allow a bot to create or update workflow` - -Instead, GitHub releases serve as your changelog - they're automatically created with full details when commits include PR bodies. +- **`.releaserc.json`** - semantic-release config (commit analyzer, release notes, GitHub plugin) +- **`.github/workflows/release.yml`** - Release pipeline (test + semantic-release + Go proxy warm-up) +- **`.github/workflows/ci.yml`** - PR pipeline (test + lint) ## Troubleshooting -### Release notes still missing details +### Release notes missing details -**Check:** Did the squash commit include PR body? +**Check:** Did the squash commit include the PR body? ```bash git show HEAD --format=fuller ``` +**Fix:** Verify repository squash merge settings (see above). -**Solution:** Verify repo settings or manually edit commit messages when merging. - -### semantic-release fails with "protected branch" - -**Check:** Do you have `@semantic-release/changelog` or `@semantic-release/git` in `.releaserc.json`? +### Unwanted releases from non-code changes -**Solution:** Remove these plugins - they try to commit to the repo, which fails with protected branches. +Only `feat`, `fix`, `perf`, and `refactor` commits trigger releases. Use `chore`, `ci`, `docs`, `test`, or `style` types for non-library changes. -## Current Configuration +### Go proxy not updated -Your `.releaserc.json` is correctly configured: -- ✅ Analyzes commits with conventional commits -- ✅ Generates detailed release notes -- ✅ Publishes to GitHub releases -- ✅ No plugins that commit back to repo -- ✅ Patch releases for: `fix`, `docs(README)`, `refactor`, `style`, `chore`, `test` -- ✅ Minor releases for: `feat` -- ✅ Major releases for: breaking changes - -The only requirement is **including PR bodies in squash commits**. +The release workflow warms the proxy automatically. If it still shows stale data: +```bash +GOPROXY=https://proxy.golang.org go list -m github.com/jasoet/pkg/v2@v2.x.x +``` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..54839a8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + pull_request: + branches: + - main + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.26' + check-latest: true + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + + - name: Run tests + run: task ci:test + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.26' + check-latest: true + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f23df18..7ec3703 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - master workflow_dispatch: jobs: @@ -20,16 +19,24 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.24.7' + go-version: '1.26' check-latest: true + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + - name: Run tests - run: go test -v ./... + run: task ci:test release: name: Release needs: test runs-on: ubuntu-latest + permissions: + contents: write + issues: write steps: - name: Checkout uses: actions/checkout@v4 @@ -37,15 +44,30 @@ jobs: fetch-depth: 0 persist-credentials: false - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Set up Go + uses: actions/setup-go@v5 with: - node-version: '20' + go-version: '1.26' + check-latest: true - - name: Install dependencies - run: npm install -g semantic-release @semantic-release/github conventional-changelog-conventionalcommits + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install semantic-release + run: bun install -g semantic-release @semantic-release/github conventional-changelog-conventionalcommits - name: Release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx semantic-release \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: task release + + - name: Warm Go module proxy + if: success() + run: | + sleep 5 + task release:proxy-warmup diff --git a/.releaserc.json b/.releaserc.json index bd660a0..4d857b0 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -1,7 +1,6 @@ { "branches": [ - "main", - "master" + "main" ], "plugins": [ [ @@ -9,25 +8,12 @@ { "preset": "conventionalcommits", "releaseRules": [ - { - "type": "docs", - "scope": "README", - "release": "patch" - }, { "type": "refactor", "release": "patch" }, { - "type": "style", - "release": "patch" - }, - { - "type": "chore", - "release": "patch" - }, - { - "type": "test", + "type": "perf", "release": "patch" } ] @@ -41,49 +27,68 @@ "types": [ { "type": "feat", - "section": "Features" + "section": "Features", + "hidden": false }, { "type": "fix", - "section": "Bug Fixes" + "section": "Bug Fixes", + "hidden": false }, { "type": "perf", - "section": "Performance Improvements" + "section": "Performance Improvements", + "hidden": false }, { - "type": "docs", - "section": "Documentation" + "type": "refactor", + "section": "Code Refactoring", + "hidden": false }, { - "type": "refactor", - "section": "Code Refactoring" + "type": "docs", + "section": "Documentation", + "hidden": true }, { "type": "test", - "section": "Tests" + "section": "Tests", + "hidden": true }, { "type": "build", - "section": "Build System" + "section": "Build System", + "hidden": true }, { "type": "ci", - "section": "Continuous Integration" + "section": "Continuous Integration", + "hidden": true }, { "type": "chore", - "section": "Chores" + "section": "Chores", + "hidden": true + }, + { + "type": "style", + "section": "Styles", + "hidden": true } ] + }, + "writerOpts": { + "headerPartial": "## [{{version}}]({{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}) ({{date}})\n\n**Full Changelog**: {{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}\n\n```\ngo get github.com/jasoet/pkg/v2@{{currentTag}}\n```" } } ], [ "@semantic-release/github", { - "assets": [] + "assets": [], + "successComment": false, + "failTitle": false } ] ] -} \ No newline at end of file +} diff --git a/MAINTAINING.md b/MAINTAINING.md index 2396047..b5f506c 100644 --- a/MAINTAINING.md +++ b/MAINTAINING.md @@ -1,121 +1,76 @@ # Maintaining Guide -This document explains how to maintain and release patches for both v1 and v2 versions of this library. +This document explains how to maintain and release this library. ## Branch Strategy -### `main` - v2 Development (Current) +### `main` - Active Development - **Module Path:** `github.com/jasoet/pkg/v2` -- **Latest Version:** v2.0.0 - **Purpose:** Active development for v2.x releases -- **Go Version:** 1.25.1+ +- **Go Version:** 1.26+ -### `release/v1` - v1 Maintenance -- **Module Path:** `github.com/jasoet/pkg` (no /v2 suffix) -- **Latest Version:** v1.5.0 -- **Purpose:** Bug fixes and security patches for v1.x -- **Go Version:** 1.25.1+ +## Releasing -## Releasing v1 Patches +Releases are fully automated via [semantic-release](https://github.com/semantic-release/semantic-release) on every push to `main`. -When you need to release a bug fix or security patch for v1.x users: +### What triggers a release -### 1. Switch to v1 branch -```bash -git checkout release/v1 -``` +| Commit Type | Release | Example | +|---|---|---| +| `feat` | Minor (v2.x.0) | `feat(server): add gRPC interceptor` | +| `fix` | Patch (v2.0.x) | `fix(compress): handle empty input` | +| `perf` | Patch | `perf(db): reduce query allocations` | +| `refactor` | Patch | `refactor(otel): simplify provider setup` | +| Breaking change | Major (vX.0.0) | `feat!: remove deprecated API` or footer `BREAKING CHANGE:` | -### 2. Apply fixes +### What does NOT trigger a release -**Option A: Cherry-pick from main** -```bash -# If the fix was already made on main -git cherry-pick -``` +`docs`, `test`, `ci`, `chore`, `style`, `build` commits are excluded. -**Option B: Direct fix** -```bash -# Make changes directly on release/v1 -# Edit files, test, commit -git add . -git commit -m "fix: description of the fix" -``` +### Workflow -### 3. Test thoroughly -```bash -# Run all tests with coverage -task test:all +1. Merge PR to `main` with conventional commit title +2. CI runs tests +3. semantic-release analyzes commits since last tag +4. If a release is warranted, it creates a GitHub release with notes +5. Go module proxy is warmed automatically -# Or run individually -task test # Unit tests -task test:integration # Integration tests (with testcontainers) -``` +## CI Pipelines -### 4. Tag and release -```bash -# Tag with appropriate version (e.g., v1.5.1, v1.5.2) -git tag v1.5.1 -git push origin release/v1 -git push origin v1.5.1 -``` - -### 5. Return to main -```bash -git checkout main -``` +- **`ci.yml`** - Runs on PRs: test (with race detector) + lint +- **`release.yml`** - Runs on push to `main`: test + semantic-release -## Releasing v2 Versions +## Conventional Commits -For v2 development on `main` branch: +All PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/): -```bash -# Ensure you're on main -git checkout main - -# Tag with v2.x.x version -git tag v2.0.0 -git push origin main -git push origin v2.0.0 ``` +(): -## Version Guidelines - -### v1.x.x (Maintenance Only) -- **Patch releases** (v1.5.1, v1.5.2): Bug fixes, security patches -- **NO new features** - v1 is in maintenance mode -- **NO breaking changes** - maintain backward compatibility -- **NO OpenTelemetry** - v1 does not have OTel support - -### v2.x.x (Active Development) -- **Major releases** (v2.0.0, v3.0.0): Breaking changes allowed -- **Minor releases** (v2.1.0, v2.2.0): New features, backward compatible -- **Patch releases** (v2.0.1, v2.0.2): Bug fixes, security patches +[optional body] -## Import Paths for Users - -### Using v1 (Maintenance Branch) -```go -import "github.com/jasoet/pkg/compress" -import "github.com/jasoet/pkg/server" +[optional footer(s)] ``` -```bash -go get github.com/jasoet/pkg@v1.5.0 -``` +### Best Practices for PR Authors +- Write detailed PR descriptions (they become release notes when squash-merged) +- Use conventional commit format in PR title +- Include scope when the change targets a specific package + +## Import Paths -### Using v2 (Current) ```go import "github.com/jasoet/pkg/v2/compress" import "github.com/jasoet/pkg/v2/server" ``` ```bash -go get github.com/jasoet/pkg/v2@v2.0.0 +go get github.com/jasoet/pkg/v2@latest ``` -## Testing Strategy +## Testing -Before any release (v1 or v2): +Before any release: 1. **Unit Tests:** `task test` 2. **Integration Tests:** `task test:integration` @@ -123,44 +78,5 @@ Before any release (v1 or v2): Or run everything: ```bash -task test:all # Runs all tests with coverage +task test:complete # Runs all tests with coverage ``` - -## Common Scenarios - -### Security Patch for v1 Users - -1. Identify the vulnerability -2. Fix on `release/v1` branch -3. Test thoroughly -4. Release as v1.5.x (patch version) -5. Optionally apply to main if relevant to v2 - -### Bug Fix Needed for Both v1 and v2 - -1. Fix on `main` first (for v2) -2. Cherry-pick to `release/v1` -3. Test on both branches -4. Release both versions: - - v1.5.x (patch) - - v2.0.x or v2.x.0 (depending on scope) - -### Migration from v1 to v2 - -Users upgrading from v1 to v2 need to: -1. Update import paths: add `/v2` suffix -2. Update `go.mod`: `go get github.com/jasoet/pkg/v2@latest` -3. Review CHANGELOG for breaking changes - -## Notes - -- **Both versions can coexist** - Projects can use v1 and v2 simultaneously if needed -- **v1 lifecycle** - Will be maintained for critical security fixes, but new features go to v2 -- **Semantic versioning** - Strictly followed for both v1 and v2 -- **Go compatibility** - Both versions currently support Go 1.25.1+ - -## Questions? - -If you have questions about releasing patches or version management, refer to: -- [Go Modules Version Management](https://go.dev/doc/modules/version-numbers) -- [Semantic Versioning 2.0](https://semver.org/) diff --git a/Taskfile.yml b/Taskfile.yml index 1740196..bba531d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -208,6 +208,42 @@ tasks: cmds: - gofumpt -l -w . + ci:test: + desc: Run unit tests for CI (no coverage HTML) + silent: true + cmds: + - go test -race -count=1 ./... -tags=!examples + + ci:lint: + desc: Run golangci-lint for CI + silent: true + cmds: + - golangci-lint run ./... + + ci:check: + desc: Run all CI checks (test + lint) + silent: true + cmds: + - task: ci:test + - task: ci:lint + + release: + desc: Run semantic-release (CI only) + silent: true + cmds: + - bunx semantic-release + + release:proxy-warmup: + desc: Warm Go module proxy with latest tag + silent: true + cmds: + - | + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + if [ -n "$LATEST_TAG" ]; then + echo "Warming Go proxy for github.com/jasoet/pkg/v2@${LATEST_TAG}" + GOPROXY=https://proxy.golang.org GO111MODULE=on go list -m "github.com/jasoet/pkg/v2@${LATEST_TAG}" || true + fi + clean: desc: Clean build artifacts silent: true diff --git a/argo/builder/builder.go b/argo/builder/builder.go index 3122450..9e9cd42 100644 --- a/argo/builder/builder.go +++ b/argo/builder/builder.go @@ -7,11 +7,12 @@ import ( "time" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/jasoet/pkg/v2/otel" ) // WorkflowBuilder provides a fluent API for constructing Argo Workflows. @@ -233,6 +234,7 @@ func (b *WorkflowBuilder) AddParallel(source WorkflowSourceV2) *WorkflowBuilder // cleanup := template.NewScript("cleanup", "bash", // template.WithScript("echo 'Cleaning up resources...'")) // builder.AddExitHandler(cleanup) +// // AddExitHandler adds an exit handler from a WorkflowSource. Exit handlers run // after the main workflow completes (regardless of success or failure). // diff --git a/argo/builder/builder_test.go b/argo/builder/builder_test.go index aa3fe28..7e16f5b 100644 --- a/argo/builder/builder_test.go +++ b/argo/builder/builder_test.go @@ -4,10 +4,11 @@ import ( "testing" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/argo/builder/template" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + + "github.com/jasoet/pkg/v2/argo/builder/template" ) func TestWorkflowBuilder_Build(t *testing.T) { diff --git a/argo/builder/option.go b/argo/builder/option.go index 2e69f5a..5353502 100644 --- a/argo/builder/option.go +++ b/argo/builder/option.go @@ -2,8 +2,9 @@ package builder import ( "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" corev1 "k8s.io/api/core/v1" + + "github.com/jasoet/pkg/v2/otel" ) // Option is a functional option for configuring WorkflowBuilder. diff --git a/argo/builder/option_test.go b/argo/builder/option_test.go index 274849a..7350df8 100644 --- a/argo/builder/option_test.go +++ b/argo/builder/option_test.go @@ -4,11 +4,12 @@ import ( "testing" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/jasoet/pkg/v2/otel" ) func TestWithOTelConfig(t *testing.T) { diff --git a/argo/builder/otel.go b/argo/builder/otel.go index 97aa2df..2fc70ad 100644 --- a/argo/builder/otel.go +++ b/argo/builder/otel.go @@ -3,10 +3,11 @@ package builder import ( "context" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" + + "github.com/jasoet/pkg/v2/otel" ) // otelInstrumentation holds OpenTelemetry instrumentation components for the workflow builder. diff --git a/argo/builder/otel_test.go b/argo/builder/otel_test.go index 0a81057..3d5bc9e 100644 --- a/argo/builder/otel_test.go +++ b/argo/builder/otel_test.go @@ -5,7 +5,6 @@ import ( "errors" "testing" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" @@ -13,6 +12,8 @@ import ( "go.opentelemetry.io/otel/sdk/metric/metricdata" noopt "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/jasoet/pkg/v2/otel" ) func TestNewOTelInstrumentation(t *testing.T) { diff --git a/argo/builder/template/container.go b/argo/builder/template/container.go index a81c43b..976b227 100644 --- a/argo/builder/template/container.go +++ b/argo/builder/template/container.go @@ -5,9 +5,10 @@ import ( "fmt" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/jasoet/pkg/v2/otel" ) // Container is a WorkflowSource that creates a container-based workflow step. diff --git a/argo/builder/template/container_test.go b/argo/builder/template/container_test.go index d73e37e..6eff4e0 100644 --- a/argo/builder/template/container_test.go +++ b/argo/builder/template/container_test.go @@ -6,11 +6,12 @@ import ( "testing" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/jasoet/pkg/v2/otel" ) func TestNewContainer(t *testing.T) { diff --git a/argo/builder/template/http.go b/argo/builder/template/http.go index 3093bbd..9efd722 100644 --- a/argo/builder/template/http.go +++ b/argo/builder/template/http.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/jasoet/pkg/v2/otel" ) diff --git a/argo/builder/template/script.go b/argo/builder/template/script.go index 0961abc..1a493f8 100644 --- a/argo/builder/template/script.go +++ b/argo/builder/template/script.go @@ -4,9 +4,10 @@ import ( "context" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/jasoet/pkg/v2/otel" ) // Script is a WorkflowSource that creates a script-based workflow step. diff --git a/argo/client.go b/argo/client.go index 515f363..6c354e3 100644 --- a/argo/client.go +++ b/argo/client.go @@ -6,10 +6,11 @@ import ( "os" "github.com/argoproj/argo-workflows/v3/pkg/apiclient" - "github.com/jasoet/pkg/v2/otel" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + + "github.com/jasoet/pkg/v2/otel" ) // NewClient creates a new Argo Workflows client from the given configuration. diff --git a/argo/operations.go b/argo/operations.go index da17774..64da625 100644 --- a/argo/operations.go +++ b/argo/operations.go @@ -8,10 +8,11 @@ import ( "github.com/argoproj/argo-workflows/v3/pkg/apiclient" "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflow" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/jasoet/pkg/v2/otel" ) // SubmitWorkflow submits a workflow to Argo with OpenTelemetry tracing. diff --git a/argo/operations_integration_test.go b/argo/operations_integration_test.go index efb1a5a..0134e09 100644 --- a/argo/operations_integration_test.go +++ b/argo/operations_integration_test.go @@ -8,13 +8,14 @@ import ( "time" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/argo/builder" - "github.com/jasoet/pkg/v2/argo/builder/template" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/jasoet/pkg/v2/argo/builder" + "github.com/jasoet/pkg/v2/argo/builder/template" + "github.com/jasoet/pkg/v2/otel" ) func TestIntegration_SubmitWorkflow(t *testing.T) { diff --git a/argo/operations_test.go b/argo/operations_test.go index ba25ea6..a79a30a 100644 --- a/argo/operations_test.go +++ b/argo/operations_test.go @@ -13,11 +13,12 @@ import ( "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflowarchive" "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflowtemplate" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/jasoet/pkg/v2/otel" ) // Mock workflow service client diff --git a/argo/option_test.go b/argo/option_test.go index deee2fe..730b125 100644 --- a/argo/option_test.go +++ b/argo/option_test.go @@ -3,9 +3,10 @@ package argo import ( "testing" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/otel" ) func TestWithKubeConfig(t *testing.T) { diff --git a/argo/patterns/cicd.go b/argo/patterns/cicd.go index 2011f78..d90d530 100644 --- a/argo/patterns/cicd.go +++ b/argo/patterns/cicd.go @@ -2,6 +2,7 @@ package patterns import ( "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/jasoet/pkg/v2/argo/builder" "github.com/jasoet/pkg/v2/argo/builder/template" ) diff --git a/argo/patterns/cicd_test.go b/argo/patterns/cicd_test.go index 1b6fceb..19eef7c 100644 --- a/argo/patterns/cicd_test.go +++ b/argo/patterns/cicd_test.go @@ -4,9 +4,10 @@ import ( "testing" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/argo/builder" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/argo/builder" ) func TestBuildTestDeploy(t *testing.T) { diff --git a/argo/patterns/parallel.go b/argo/patterns/parallel.go index da5ad28..e971df4 100644 --- a/argo/patterns/parallel.go +++ b/argo/patterns/parallel.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/jasoet/pkg/v2/argo/builder" "github.com/jasoet/pkg/v2/argo/builder/template" ) diff --git a/argo/patterns/parallel_test.go b/argo/patterns/parallel_test.go index ab9d036..f285ed8 100644 --- a/argo/patterns/parallel_test.go +++ b/argo/patterns/parallel_test.go @@ -4,9 +4,10 @@ import ( "testing" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/jasoet/pkg/v2/argo/builder" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/argo/builder" ) func TestFanOutFanIn(t *testing.T) { diff --git a/base32/base32.go b/base32/base32.go index edc483c..9ae57a0 100644 --- a/base32/base32.go +++ b/base32/base32.go @@ -184,6 +184,7 @@ func IsValidBase32Char(c rune) bool { // base32.NormalizeBase32("abc-def") // "ABCDEF" // base32.NormalizeBase32("1O 2I") // "1021" // base32.NormalizeBase32("hell0") // "HELL0" +// // normalizeReplacer performs single-pass replacement of confusable characters and separators. var normalizeReplacer = strings.NewReplacer( "-", "", diff --git a/base32/base32_test.go b/base32/base32_test.go index efa3fd6..98e2f3e 100644 --- a/base32/base32_test.go +++ b/base32/base32_test.go @@ -58,9 +58,9 @@ func TestDecodeBase32(t *testing.T) { {"case insensitive upper", "ABC", 10604, false}, {"case insensitive lower", "abc", 10604, false}, {"case insensitive mixed", "AbC", 10604, false}, - {"with I correction", "1I", 33, false}, // I→1 - {"with L correction", "1L", 33, false}, // L→1 - {"with O correction", "1O", 32, false}, // O→0 + {"with I correction", "1I", 33, false}, // I→1 + {"with L correction", "1L", 33, false}, // L→1 + {"with O correction", "1O", 32, false}, // O→0 {"empty string", "", 0, true}, {"invalid char U", "U", 0, true}, {"invalid char #", "#", 0, true}, diff --git a/concurrent/execution.go b/concurrent/execution.go index 4b4eb4f..426b70e 100644 --- a/concurrent/execution.go +++ b/concurrent/execution.go @@ -15,7 +15,7 @@ type Func[T any] func(ctx context.Context) (T, error) // ExecuteConcurrently executes multiple functions concurrently and collects their results. // // All functions receive a shared cancellable context. When any function returns an error -// or panics, the context is cancelled to signal other goroutines to stop. +// or panics, the context is canceled to signal other goroutines to stop. // // Returns a map of results indexed by the provided keys, and the first causal error // encountered (preferring real errors over context cancellation errors). diff --git a/db/migrations.go b/db/migrations.go index 47821be..cad397f 100644 --- a/db/migrations.go +++ b/db/migrations.go @@ -10,9 +10,10 @@ import ( "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" "github.com/golang-migrate/migrate/v4/source/iofs" - "github.com/jasoet/pkg/v2/logging" "github.com/rs/zerolog" "gorm.io/gorm" + + "github.com/jasoet/pkg/v2/logging" ) // RunPostgresMigrationsWithGorm applies pending UP migrations using a GORM connection. diff --git a/db/otel_integration_test.go b/db/otel_integration_test.go index 5e6d269..8a291cc 100644 --- a/db/otel_integration_test.go +++ b/db/otel_integration_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/google/uuid" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" @@ -20,6 +19,8 @@ import ( noopm "go.opentelemetry.io/otel/metric/noop" noopt "go.opentelemetry.io/otel/trace/noop" "gorm.io/gorm" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // TestPostgresPoolWithOTelTracing tests OTel tracing callbacks diff --git a/db/pool.go b/db/pool.go index a7524c3..68b3f68 100644 --- a/db/pool.go +++ b/db/pool.go @@ -8,7 +8,6 @@ import ( "fmt" "time" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/uptrace/opentelemetry-go-extra/otelgorm" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -18,6 +17,8 @@ import ( "gorm.io/driver/sqlserver" "gorm.io/gorm" "gorm.io/gorm/logger" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // DatabaseType identifies the database backend. diff --git a/db/pool_test.go b/db/pool_test.go index 750aeec..5db72bc 100644 --- a/db/pool_test.go +++ b/db/pool_test.go @@ -6,12 +6,13 @@ import ( "time" "github.com/go-playground/validator/v10" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" noopm "go.opentelemetry.io/otel/metric/noop" noopt "go.opentelemetry.io/otel/trace/noop" "gorm.io/gorm/logger" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) func TestDatabaseConfigValidation(t *testing.T) { diff --git a/docker/config.go b/docker/config.go index 8dcbe90..823ad5d 100644 --- a/docker/config.go +++ b/docker/config.go @@ -6,6 +6,7 @@ import ( "time" "github.com/docker/go-connections/nat" + "github.com/jasoet/pkg/v2/otel" ) diff --git a/docker/config_test.go b/docker/config_test.go index 167dc96..02fa747 100644 --- a/docker/config_test.go +++ b/docker/config_test.go @@ -5,10 +5,11 @@ import ( "time" "github.com/docker/go-connections/nat" - "github.com/jasoet/pkg/v2/docker" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/docker" + "github.com/jasoet/pkg/v2/otel" ) func TestConfigOptions_Image(t *testing.T) { diff --git a/docker/executor_test.go b/docker/executor_test.go index 010efd0..26d86f7 100644 --- a/docker/executor_test.go +++ b/docker/executor_test.go @@ -8,9 +8,10 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/docker" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/docker" ) func TestExecutor_FunctionalOptions_Nginx(t *testing.T) { diff --git a/docker/helpers_test.go b/docker/helpers_test.go index 53ff035..0302c48 100644 --- a/docker/helpers_test.go +++ b/docker/helpers_test.go @@ -5,12 +5,13 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/docker" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/trace" + + "github.com/jasoet/pkg/v2/docker" + "github.com/jasoet/pkg/v2/otel" ) func TestExecutor_ContainerID(t *testing.T) { diff --git a/docker/integration_test.go b/docker/integration_test.go index 01feb29..af30e88 100644 --- a/docker/integration_test.go +++ b/docker/integration_test.go @@ -7,12 +7,13 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/docker" - "github.com/jasoet/pkg/v2/otel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/trace" + + "github.com/jasoet/pkg/v2/docker" + "github.com/jasoet/pkg/v2/otel" ) // Integration test with OpenTelemetry @@ -181,7 +182,7 @@ func TestIntegration_VolumeMounts(t *testing.T) { // Docker and Podman. On macOS, Podman VM only shares /Users and // /var/folders by default — /tmp is not accessible from the VM. hostDir := t.TempDir() - err := os.WriteFile(filepath.Join(hostDir, "testfile"), []byte("hello"), 0644) + err := os.WriteFile(filepath.Join(hostDir, "testfile"), []byte("hello"), 0o644) require.NoError(t, err) exec, _ := docker.New( diff --git a/docker/logs_test.go b/docker/logs_test.go index 48b2620..8c6f4e1 100644 --- a/docker/logs_test.go +++ b/docker/logs_test.go @@ -6,9 +6,10 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/docker" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/docker" ) func TestLogOptions_WithStdout(t *testing.T) { diff --git a/docker/otel.go b/docker/otel.go index eded377..577a19a 100644 --- a/docker/otel.go +++ b/docker/otel.go @@ -3,11 +3,12 @@ package docker import ( "context" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" + + "github.com/jasoet/pkg/v2/otel" ) // otelInstrumentation holds OpenTelemetry instrumentation components. diff --git a/docker/otel_test.go b/docker/otel_test.go index c5175d2..85aefc8 100644 --- a/docker/otel_test.go +++ b/docker/otel_test.go @@ -5,7 +5,6 @@ import ( "errors" "testing" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdkmetric "go.opentelemetry.io/otel/sdk/metric" @@ -13,6 +12,8 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" + + "github.com/jasoet/pkg/v2/otel" ) func TestOTelInstrumentation_AddSpanAttributes(t *testing.T) { diff --git a/docker/wait_test.go b/docker/wait_test.go index 586494e..1a0e514 100644 --- a/docker/wait_test.go +++ b/docker/wait_test.go @@ -6,9 +6,10 @@ import ( "time" "github.com/docker/docker/client" - "github.com/jasoet/pkg/v2/docker" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/jasoet/pkg/v2/docker" ) func TestWaitStrategy_WaitForLog(t *testing.T) { diff --git a/examples/base32/example.go b/examples/base32/example.go index ce0dfe1..96e0a05 100644 --- a/examples/base32/example.go +++ b/examples/base32/example.go @@ -332,12 +332,12 @@ func checksumValidation() { fmt.Println("\nTesting various corruptions:") corruptions := map[string]string{ - "Change A→X": "XBCD1234" + base32.ExtractChecksum(validData), - "Change 1→2": "ABCD2234" + base32.ExtractChecksum(validData), - "Swap AB→BA": "BACD1234" + base32.ExtractChecksum(validData), - "Delete character": "ABCD123" + base32.ExtractChecksum(validData), - "Add character": "ABCD12345" + base32.ExtractChecksum(validData), - "Change checksum": testData + "00", + "Change A→X": "XBCD1234" + base32.ExtractChecksum(validData), + "Change 1→2": "ABCD2234" + base32.ExtractChecksum(validData), + "Swap AB→BA": "BACD1234" + base32.ExtractChecksum(validData), + "Delete character": "ABCD123" + base32.ExtractChecksum(validData), + "Add character": "ABCD12345" + base32.ExtractChecksum(validData), + "Change checksum": testData + "00", } for desc, corrupted := range corruptions { diff --git a/examples/grpc/cmd/server/main.go b/examples/grpc/cmd/server/main.go index fc17d55..e3415a2 100644 --- a/examples/grpc/cmd/server/main.go +++ b/examples/grpc/cmd/server/main.go @@ -9,9 +9,9 @@ import ( "github.com/labstack/echo/v4" "google.golang.org/grpc" - grpcserver "github.com/jasoet/pkg/v2/grpc" calculatorv1 "github.com/jasoet/pkg/v2/examples/grpc/gen/calculator/v1" "github.com/jasoet/pkg/v2/examples/grpc/internal/service" + grpcserver "github.com/jasoet/pkg/v2/grpc" ) func main() { diff --git a/examples/retry/example.go b/examples/retry/example.go index 71da62e..941b823 100644 --- a/examples/retry/example.go +++ b/examples/retry/example.go @@ -122,7 +122,6 @@ func example3PermanentErrors() { fmt.Printf(" Attempt %d\n", attempts) return validateInput("") // Invalid input }) - if err != nil { fmt.Printf(" ❌ Failed immediately (no retry): %v\n", err) fmt.Printf(" Total attempts: %d (expected: 1)\n", attempts) @@ -155,7 +154,6 @@ func example4ContextCancellation() { fmt.Printf(" Attempt %d\n", attempts) return errors.New("still failing") }) - if err != nil { fmt.Printf(" ❌ Cancelled: %v\n", err) fmt.Printf(" Stopped after %d attempts\n", attempts) diff --git a/examples/temporal/scheduler/basic_scheduler.go b/examples/temporal/scheduler/basic_scheduler.go index 5a34eb9..2552ca2 100644 --- a/examples/temporal/scheduler/basic_scheduler.go +++ b/examples/temporal/scheduler/basic_scheduler.go @@ -9,8 +9,8 @@ import ( "syscall" "time" - "github.com/jasoet/pkg/v2/temporal" "github.com/jasoet/pkg/v2/examples/temporal/workflows" + "github.com/jasoet/pkg/v2/temporal" "github.com/rs/zerolog/log" "go.temporal.io/sdk/client" ) diff --git a/examples/temporal/worker/basic_worker.go b/examples/temporal/worker/basic_worker.go index 7f71107..9081528 100644 --- a/examples/temporal/worker/basic_worker.go +++ b/examples/temporal/worker/basic_worker.go @@ -9,9 +9,9 @@ import ( "syscall" "time" - "github.com/jasoet/pkg/v2/temporal" "github.com/jasoet/pkg/v2/examples/temporal/activities" "github.com/jasoet/pkg/v2/examples/temporal/workflows" + "github.com/jasoet/pkg/v2/temporal" "github.com/rs/zerolog/log" "go.temporal.io/sdk/worker" ) diff --git a/go.mod b/go.mod index 11849e8..83d2392 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/jasoet/pkg/v2 -go 1.24.10 +go 1.26.0 require ( github.com/argoproj/argo-workflows/v3 v3.7.4 diff --git a/grpc/config.go b/grpc/config.go index a79af73..61a1b80 100644 --- a/grpc/config.go +++ b/grpc/config.go @@ -4,9 +4,10 @@ import ( "fmt" "time" - "github.com/jasoet/pkg/v2/otel" "github.com/labstack/echo/v4" "google.golang.org/grpc" + + "github.com/jasoet/pkg/v2/otel" ) // ServerMode defines the server operation mode diff --git a/grpc/config_test.go b/grpc/config_test.go index 81f5a57..b2c8d4c 100644 --- a/grpc/config_test.go +++ b/grpc/config_test.go @@ -4,7 +4,6 @@ import ( "testing" "time" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -12,6 +11,8 @@ import ( noopm "go.opentelemetry.io/otel/metric/noop" noopt "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) func TestNewConfigDefaults(t *testing.T) { diff --git a/grpc/otel_instrumentation.go b/grpc/otel_instrumentation.go index bf96d41..1c7ab28 100644 --- a/grpc/otel_instrumentation.go +++ b/grpc/otel_instrumentation.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/labstack/echo/v4" "go.opentelemetry.io/otel/attribute" otellog "go.opentelemetry.io/otel/log" @@ -15,6 +14,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // ============================================================================ diff --git a/grpc/otel_instrumentation_test.go b/grpc/otel_instrumentation_test.go index aba1afd..0d93117 100644 --- a/grpc/otel_instrumentation_test.go +++ b/grpc/otel_instrumentation_test.go @@ -7,7 +7,6 @@ import ( "net/http/httptest" "testing" - pkgotel "github.com/jasoet/pkg/v2/otel" "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,6 +16,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // ============================================================================ diff --git a/logging/logging.go b/logging/logging.go index 095d87f..0fed407 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -89,7 +89,7 @@ func InitializeWithFile(serviceName string, debug bool, output OutputDestination } var err error - file, err = os.OpenFile(fileConfig.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + file, err = os.OpenFile(fileConfig.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) if err != nil { return nil, fmt.Errorf("failed to open log file %s: %w", fileConfig.Path, err) } diff --git a/logging/logging_test.go b/logging/logging_test.go index 46b69bd..563f978 100644 --- a/logging/logging_test.go +++ b/logging/logging_test.go @@ -207,7 +207,7 @@ func TestInitializeWithFile(t *testing.T) { // Verify permissions are 0644 mode := info.Mode().Perm() - assert.Equal(t, os.FileMode(0644), mode) + assert.Equal(t, os.FileMode(0o644), mode) }) t.Run("multiple log levels to file", func(t *testing.T) { diff --git a/otel/config.go b/otel/config.go index cbede22..63b9454 100644 --- a/otel/config.go +++ b/otel/config.go @@ -4,13 +4,14 @@ import ( "context" "errors" - "github.com/jasoet/pkg/v2/logging" "go.opentelemetry.io/otel/log" noopl "go.opentelemetry.io/otel/log/noop" "go.opentelemetry.io/otel/metric" noopm "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/trace" noopt "go.opentelemetry.io/otel/trace/noop" + + "github.com/jasoet/pkg/v2/logging" ) type contextKey string diff --git a/otel/config_test.go b/otel/config_test.go index 29bba2e..3b3a589 100644 --- a/otel/config_test.go +++ b/otel/config_test.go @@ -346,7 +346,6 @@ func TestDisableMetrics(t *testing.T) { }) } - func TestIsTracingEnabled(t *testing.T) { t.Run("returns false when config is nil", func(t *testing.T) { var cfg *Config diff --git a/otel/helper_test.go b/otel/helper_test.go index 8f4f80a..ccdb650 100644 --- a/otel/helper_test.go +++ b/otel/helper_test.go @@ -5,8 +5,9 @@ import ( "errors" "testing" - "github.com/jasoet/pkg/v2/logging" "go.opentelemetry.io/otel/log/noop" + + "github.com/jasoet/pkg/v2/logging" ) func TestNewLogHelper(t *testing.T) { diff --git a/otel/instrumentation.go b/otel/instrumentation.go index 5a82980..3733dec 100644 --- a/otel/instrumentation.go +++ b/otel/instrumentation.go @@ -379,7 +379,7 @@ func (l *LayeredSpanHelper) StartHandler(ctx context.Context, component, operati // if err := s.repo.Update(lc.Context(), data); err != nil { // return lc.Error(err, "failed to update event") // } -// return lc.Success("Event cancelled") +// return lc.Success("Event canceled") // } func (l *LayeredSpanHelper) StartService(ctx context.Context, component, operation string, fields ...Field) *LayerContext { tracerName := "service." + component diff --git a/otel/logging.go b/otel/logging.go index 85b4677..8b9c67b 100644 --- a/otel/logging.go +++ b/otel/logging.go @@ -6,13 +6,14 @@ import ( "os" "time" - "github.com/jasoet/pkg/v2/logging" "github.com/rs/zerolog" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" "go.opentelemetry.io/otel/log" sdklog "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + + "github.com/jasoet/pkg/v2/logging" ) // LogLevel is an alias for logging.LogLevel for convenience. diff --git a/otel/logging_test.go b/otel/logging_test.go index 634556c..ddd7928 100644 --- a/otel/logging_test.go +++ b/otel/logging_test.go @@ -4,9 +4,10 @@ import ( "context" "testing" - "github.com/jasoet/pkg/v2/logging" "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/noop" + + "github.com/jasoet/pkg/v2/logging" ) // TestWithConsoleOutput tests the WithConsoleOutput option @@ -264,7 +265,6 @@ func TestNewLoggerProviderWithOptions_MultipleOptions(t *testing.T) { WithConsoleOutput(true), WithLogLevel(logging.LogLevelWarn), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -279,7 +279,6 @@ func TestNewLoggerProviderWithOptions_MultipleOptions(t *testing.T) { WithConsoleOutput(false), WithLogLevel(logging.LogLevelInfo), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -353,7 +352,6 @@ func TestSetupZerologConsole(t *testing.T) { WithLogLevel(tt.logLevel), WithConsoleOutput(true), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -372,7 +370,6 @@ func TestNewLoggerProviderWithOptions_Integration(t *testing.T) { "my-service", WithConsoleOutput(true), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -394,7 +391,6 @@ func TestNewLoggerProviderWithOptions_Integration(t *testing.T) { WithConsoleOutput(true), WithLogLevel(logging.LogLevelInfo), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -410,7 +406,6 @@ func TestNewLoggerProviderWithOptions_Integration(t *testing.T) { WithConsoleOutput(false), WithLogLevel(logging.LogLevelNone), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -469,7 +464,6 @@ func TestLoggerProviderOptions_Chaining(t *testing.T) { WithConsoleOutput(true), WithLogLevel(logging.LogLevelDebug), ) - if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -485,7 +479,6 @@ func TestLoggerProviderOptions_Chaining(t *testing.T) { WithLogLevel(logging.LogLevelDebug), WithLogLevel(logging.LogLevelError), // This should win ) - if err != nil { t.Fatalf("expected no error, got %v", err) } diff --git a/rest/client.go b/rest/client.go index 2309a6c..ba6aad4 100644 --- a/rest/client.go +++ b/rest/client.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-resty/resty/v2" + "github.com/jasoet/pkg/v2/otel" ) diff --git a/rest/client_test.go b/rest/client_test.go index a2932ea..6a0e3f1 100644 --- a/rest/client_test.go +++ b/rest/client_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/go-resty/resty/v2" + "github.com/jasoet/pkg/v2/concurrent" "github.com/jasoet/pkg/v2/otel" ) diff --git a/rest/middleware.go b/rest/middleware.go index 8568d5e..47c235b 100644 --- a/rest/middleware.go +++ b/rest/middleware.go @@ -5,6 +5,7 @@ import ( "time" "github.com/go-resty/resty/v2" + "github.com/jasoet/pkg/v2/otel" ) diff --git a/rest/otel_middleware.go b/rest/otel_middleware.go index 9517144..8f73d97 100644 --- a/rest/otel_middleware.go +++ b/rest/otel_middleware.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - pkgotel "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" otellog "go.opentelemetry.io/otel/log" @@ -12,6 +11,8 @@ import ( "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.27.0" "go.opentelemetry.io/otel/trace" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // ============================================================================ diff --git a/rest/otel_middleware_test.go b/rest/otel_middleware_test.go index 87904d7..89a4660 100644 --- a/rest/otel_middleware_test.go +++ b/rest/otel_middleware_test.go @@ -7,9 +7,10 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/metric/noop" noopt "go.opentelemetry.io/otel/trace/noop" + + "github.com/jasoet/pkg/v2/otel" ) // ============================================================================ diff --git a/retry/retry.go b/retry/retry.go index e935dd5..50150cc 100644 --- a/retry/retry.go +++ b/retry/retry.go @@ -6,10 +6,11 @@ import ( "time" "github.com/cenkalti/backoff/v4" - pkgotel "github.com/jasoet/pkg/v2/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" + + pkgotel "github.com/jasoet/pkg/v2/otel" ) // Operation is a function that may fail and should be retried. @@ -201,15 +202,15 @@ func Do(ctx context.Context, cfg Config, operation Operation) error { // Handle context cancellation if ctx.Err() != nil { if span != nil { - span.SetStatus(codes.Error, "Operation cancelled") + span.SetStatus(codes.Error, "Operation canceled") span.RecordError(ctx.Err()) } if logger != nil { - logger.Error(ctx.Err(), "Operation cancelled", + logger.Error(ctx.Err(), "Operation canceled", pkgotel.F("attempts", attempt), ) } - return fmt.Errorf("%s cancelled after %d attempts: %w", cfg.OperationName, attempt, ctx.Err()) + return fmt.Errorf("%s canceled after %d attempts: %w", cfg.OperationName, attempt, ctx.Err()) } // Failed after retries @@ -320,15 +321,15 @@ func DoWithNotify( if ctx.Err() != nil { if span != nil { - span.SetStatus(codes.Error, "Operation cancelled") + span.SetStatus(codes.Error, "Operation canceled") span.RecordError(ctx.Err()) } if logger != nil { - logger.Error(ctx.Err(), "Operation cancelled", + logger.Error(ctx.Err(), "Operation canceled", pkgotel.F("attempts", attempt), ) } - return fmt.Errorf("%s cancelled after %d attempts: %w", cfg.OperationName, attempt, ctx.Err()) + return fmt.Errorf("%s canceled after %d attempts: %w", cfg.OperationName, attempt, ctx.Err()) } if span != nil { diff --git a/retry/retry_test.go b/retry/retry_test.go index c46a83f..f94f723 100644 --- a/retry/retry_test.go +++ b/retry/retry_test.go @@ -116,7 +116,7 @@ func TestDo_ContextCancellation(t *testing.T) { err := Do(ctx, cfg, operation) assert.Error(t, err) assert.ErrorIs(t, err, context.Canceled) - assert.Contains(t, err.Error(), "cancelled") + assert.Contains(t, err.Error(), "canceled") // Should stop after cancellation assert.LessOrEqual(t, attempts, 3) } @@ -313,7 +313,7 @@ func TestDoWithNotify_ContextCancellation(t *testing.T) { err := DoWithNotify(ctx, cfg, operation, notifyFunc) assert.Error(t, err) assert.ErrorIs(t, err, context.Canceled) - assert.Contains(t, err.Error(), "cancelled") + assert.Contains(t, err.Error(), "canceled") assert.LessOrEqual(t, attempts, 3) } diff --git a/server/server.go b/server/server.go index 1c2c146..24f6330 100644 --- a/server/server.go +++ b/server/server.go @@ -13,8 +13,9 @@ import ( "syscall" "time" - "github.com/jasoet/pkg/v2/otel" "github.com/labstack/echo/v4" + + "github.com/jasoet/pkg/v2/otel" ) type ( @@ -22,7 +23,7 @@ type ( Operation func(e *echo.Echo) // Shutdown is called during graceful shutdown before the Echo instance is stopped. Shutdown func(e *echo.Echo) - // EchoConfigurer is called during setup to customise the Echo instance (add routes, middleware, etc.). + // EchoConfigurer is called during setup to customize the Echo instance (add routes, middleware, etc.). EchoConfigurer func(e *echo.Echo) ) @@ -71,7 +72,7 @@ func WithShutdownTimeout(d time.Duration) Option { return func(c *Config) { c.ShutdownTimeout = d } } -// WithEchoConfigurer sets a callback that customises the Echo instance. +// WithEchoConfigurer sets a callback that customizes the Echo instance. func WithEchoConfigurer(ec EchoConfigurer) Option { return func(c *Config) { c.EchoConfigurer = ec } } diff --git a/ssh/tunnel.go b/ssh/tunnel.go index 9df2595..a432edb 100644 --- a/ssh/tunnel.go +++ b/ssh/tunnel.go @@ -8,9 +8,10 @@ import ( "sync" "time" - "github.com/jasoet/pkg/v2/otel" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/knownhosts" + + "github.com/jasoet/pkg/v2/otel" ) // Config holds the configuration for an SSH tunnel @@ -195,12 +196,12 @@ func (t *Tunnel) forward(localConn net.Conn, remoteAddr string) { go func() { defer wg.Done() - _, _ = io.Copy(remoteConn, localConn) + _, _ = io.Copy(remoteConn, localConn) //nolint:errcheck }() go func() { defer wg.Done() - _, _ = io.Copy(localConn, remoteConn) + _, _ = io.Copy(localConn, remoteConn) //nolint:errcheck }() wg.Wait() diff --git a/temporal/client.go b/temporal/client.go index 216ac17..e9ac642 100644 --- a/temporal/client.go +++ b/temporal/client.go @@ -6,13 +6,14 @@ import ( "os" "time" - "github.com/jasoet/pkg/v2/otel" prom "github.com/prometheus/client_golang/prometheus" "github.com/rs/zerolog" "github.com/uber-go/tally/v4" "github.com/uber-go/tally/v4/prometheus" "go.temporal.io/sdk/client" sdktally "go.temporal.io/sdk/contrib/tally" + + "github.com/jasoet/pkg/v2/otel" ) func NewClientWithMetrics(config *Config, metricsEnabled bool) (client.Client, io.Closer, error) { diff --git a/temporal/client_integration_test.go b/temporal/client_integration_test.go index bf27d86..d6b14fb 100644 --- a/temporal/client_integration_test.go +++ b/temporal/client_integration_test.go @@ -7,11 +7,12 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/api/enums/v1" "go.temporal.io/sdk/client" + + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) func TestClientIntegration(t *testing.T) { diff --git a/temporal/e2e_integration_test.go b/temporal/e2e_integration_test.go index 2256357..e03f553 100644 --- a/temporal/e2e_integration_test.go +++ b/temporal/e2e_integration_test.go @@ -9,14 +9,15 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/logging" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/sdk/activity" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + + "github.com/jasoet/pkg/v2/logging" + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) // E2E Test workflows and activities diff --git a/temporal/schedule.go b/temporal/schedule.go index d60cf6f..d8d1bff 100644 --- a/temporal/schedule.go +++ b/temporal/schedule.go @@ -7,8 +7,9 @@ import ( "sync" "time" - "github.com/jasoet/pkg/v2/otel" "go.temporal.io/sdk/client" + + "github.com/jasoet/pkg/v2/otel" ) type WorkflowScheduleOptions struct { diff --git a/temporal/schedule_integration_test.go b/temporal/schedule_integration_test.go index ee87919..5fb428f 100644 --- a/temporal/schedule_integration_test.go +++ b/temporal/schedule_integration_test.go @@ -7,11 +7,12 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/sdk/client" "go.temporal.io/sdk/workflow" + + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) func TestScheduleManagerIntegration(t *testing.T) { diff --git a/temporal/testcontainer/example_test.go b/temporal/testcontainer/example_test.go index 1ba46ab..a68121a 100644 --- a/temporal/testcontainer/example_test.go +++ b/temporal/testcontainer/example_test.go @@ -9,9 +9,10 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "go.temporal.io/sdk/client" "go.temporal.io/sdk/workflow" + + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) // Example workflow for demonstration diff --git a/temporal/worker.go b/temporal/worker.go index 80f79a4..5ad4278 100644 --- a/temporal/worker.go +++ b/temporal/worker.go @@ -6,9 +6,10 @@ import ( "io" "sync" - "github.com/jasoet/pkg/v2/otel" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" + + "github.com/jasoet/pkg/v2/otel" ) type WorkerManager struct { diff --git a/temporal/worker_integration_test.go b/temporal/worker_integration_test.go index feca0bb..9d0b44c 100644 --- a/temporal/worker_integration_test.go +++ b/temporal/worker_integration_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/sdk/activity" @@ -17,6 +16,8 @@ import ( "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) // RetryPolicy alias for temporal retry policy diff --git a/temporal/workflow.go b/temporal/workflow.go index 312397e..c04dfac 100644 --- a/temporal/workflow.go +++ b/temporal/workflow.go @@ -8,11 +8,12 @@ import ( "sort" "time" - "github.com/jasoet/pkg/v2/otel" "go.temporal.io/api/common/v1" "go.temporal.io/api/enums/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" + + "github.com/jasoet/pkg/v2/otel" ) // WorkflowManager provides workflow query and management operations diff --git a/temporal/workflow_integration_test.go b/temporal/workflow_integration_test.go index 33d6a95..1961cfb 100644 --- a/temporal/workflow_integration_test.go +++ b/temporal/workflow_integration_test.go @@ -8,13 +8,14 @@ import ( "testing" "time" - "github.com/jasoet/pkg/v2/temporal/testcontainer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/api/enums/v1" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + + "github.com/jasoet/pkg/v2/temporal/testcontainer" ) // Test workflows for integration testing From e8c65b9ea1537a9d1ef6a83f432ebe117802e513 Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 02:01:18 +0700 Subject: [PATCH 2/6] fix(ci): install golangci-lint via go install for Go 1.26 compatibility The pre-built binary from golangci-lint-action was compiled with Go 1.24, which is incompatible with Go 1.26 target. Installing from source ensures it's compiled with the correct Go version. --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54839a8..d8d61fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,8 @@ jobs: with: version: 3.x - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: latest + - name: Install golangci-lint + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + + - name: Run lint + run: task ci:lint From 5c86064ccaef9ea5ef2c41323b2fcec002de91a0 Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 02:12:11 +0700 Subject: [PATCH 3/6] docs: update INSTRUCTION.md with Go 1.26 and new CI/release tasks --- INSTRUCTION.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/INSTRUCTION.md b/INSTRUCTION.md index 7769008..73db12e 100644 --- a/INSTRUCTION.md +++ b/INSTRUCTION.md @@ -8,7 +8,7 @@ Production-ready Go utility library (v2) with OpenTelemetry instrumentation. 15 packages: otel, config, logging, db, docker, server, grpc, rest, concurrent, temporal, ssh, compress, argo, retry, base32. **Module Path:** `github.com/jasoet/pkg/v2` -**Go Version:** 1.24+ (uses generics) +**Go Version:** 1.26+ (uses generics) **Test Coverage:** 85% ## Conventions @@ -58,6 +58,11 @@ Production-ready Go utility library (v2) with OpenTelemetry instrumentation. 15 | `task docker:check` | Verify Docker/Podman availability | | `task k8s:check` | Verify kubectl and cluster | | `task argo:check` | Verify Argo Workflows | +| `task ci:test` | Unit tests for CI (no coverage HTML) | +| `task ci:lint` | Lint for CI | +| `task ci:check` | CI test + lint | +| `task release` | Run semantic-release (CI only) | +| `task release:proxy-warmup` | Warm Go module proxy with latest tag | ## Testing Strategy From ee1d6a7711919e3d9f693497f95a7e2b20ce5a5f Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 02:13:45 +0700 Subject: [PATCH 4/6] docs: promote git authorship rule to dedicated section in INSTRUCTION.md --- INSTRUCTION.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/INSTRUCTION.md b/INSTRUCTION.md index 73db12e..4498154 100644 --- a/INSTRUCTION.md +++ b/INSTRUCTION.md @@ -11,12 +11,20 @@ Production-ready Go utility library (v2) with OpenTelemetry instrumentation. 15 **Go Version:** 1.26+ (uses generics) **Test Coverage:** 85% +## ABSOLUTE RULE — Git Authorship + +**NEVER add AI (Claude, Copilot, or any AI) as co-author, committer, or contributor in git commits.** +Only the user's registered email may appear in commits. This is company policy — commits with AI +authorship WILL BE REJECTED. Do not use `--author`, `Co-authored-by`, or any other mechanism to +attribute commits to AI. This applies to ALL commits, including those made by tools and subagents. + ## Conventions +- **Node.js**: Always use `bun`/`bunx` (never node, npm, npx). - **Commands**: Always use `task ` to run commands. Run `task --list` to discover available tasks. If a command is important or repeated but has no task, suggest adding it to `Taskfile.yml`. - **Brainstorming**: New topics or planning always start with brainstorming skill first. If unsure, ask the user. - **Superpowers**: Ensure superpowers skills are installed. Use TDD for implementation, systematic-debugging for bugs. -- **Commits**: Use Conventional Commits. Format: `(): `. Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`. Never add AI as co-author or committer. +- **Commits**: Use Conventional Commits. Format: `(): `. Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`. - **Branching**: New branch for each feature/fix (`feat/...`, `fix/...`). PR with squash merge. Use `gh` for PR status and CI checks. - **Containers**: Dual Docker/Podman support. This is a shared library — consumers use either runtime. - **Patterns**: Functional options for configuration. OTelConfig always injected via `With*()` options, never serialized (`yaml:"-" mapstructure:"-"`). Use `otel.Layers.Start*()` for instrumentation. From 3bcd529146131ac12aeb4b754aee1d2e1b05d474 Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 02:15:09 +0700 Subject: [PATCH 5/6] docs: add CLAUDE.md as critical rules safety net --- CLAUDE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3352642 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,20 @@ +# CLAUDE.md + +Read INSTRUCTION.md for full project context. + +## ABSOLUTE RULE — NO EXCEPTIONS + +**NEVER add AI (Claude, Copilot, or any AI) as co-author, committer, or contributor in git commits.** +Only the user's registered email may appear in commits. This is company policy — commits with AI +authorship WILL BE REJECTED. Do not use `--author`, `Co-authored-by`, or any other mechanism to +attribute commits to AI. This applies to ALL commits, including those made by tools and subagents. + +## Critical Rules (never forget) + +- Always use `task ` to run commands — never run raw commands directly. Run `task --list` to discover tasks. +- Node.js: always `bun`/`bunx` (never node, npm, npx). +- Containers: dual Docker/Podman support. This is a shared library — consumers use either runtime. +- Use brainstorming skill when user starts a new topic or plans something. +- Check and update INSTRUCTION.md and README.md when making significant changes. +- Conventional Commits: `(): `. +- Branch per change, squash merge. Use `gh` for PR and CI checks. From 0cc370a37a90c993ecea63fd7d25ab9fd3e8da14 Mon Sep 17 00:00:00 2001 From: Jasoet Martohartono Date: Fri, 6 Mar 2026 02:18:07 +0700 Subject: [PATCH 6/6] fix(ci): pass GITHUB_TOKEN to setup-task to avoid API rate limits --- .github/workflows/ci.yml | 2 ++ .github/workflows/release.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8d61fd..16bff79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ jobs: uses: arduino/setup-task@v2 with: version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Run tests run: task ci:test @@ -44,6 +45,7 @@ jobs: uses: arduino/setup-task@v2 with: version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install golangci-lint run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ec3703..35d6d7d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,7 @@ jobs: uses: arduino/setup-task@v2 with: version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Run tests run: task ci:test @@ -54,6 +55,7 @@ jobs: uses: arduino/setup-task@v2 with: version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Bun uses: oven-sh/setup-bun@v2