diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 000000000..454b8427c --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +../.claude/skills \ No newline at end of file diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 000000000..dda3f58d8 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1 @@ +*.local.json diff --git a/.claude/skills/commit-message/SKILL.md b/.claude/skills/commit-message/SKILL.md new file mode 100644 index 000000000..67aac4146 --- /dev/null +++ b/.claude/skills/commit-message/SKILL.md @@ -0,0 +1,328 @@ +--- +name: commit-message +description: This skill should be used when the user asks to "create a commit", "generate commit message", "commit changes", "make a commit", mentions "conventional commits", or discusses commit message formatting. Provides guided workflow for creating properly formatted commit messages with line length validation and required trailers. +version: 0.2.0 +--- + +# Conventional Commit Message Creation + +Create properly formatted conventional commit messages following project standards with line length validation and required trailers. + +## Purpose + +Generate commit messages that: + +- Follow conventional commits format (`type(scope): description`) +- Use component names or GitHub issue numbers as scope +- Respect line length limits (50 for subject, 72 for body) +- Include required trailers (Signed-off-by, Assisted-by) +- Follow the validation rules below + +## Quick Workflow + +1. **Analyze changes**: Run git status and git diff to understand modifications +2. **Determine scope**: Use component name from changed files, or GitHub issue number if available +3. **Generate message**: Create conventional commit message with proper formatting +4. **Add trailers**: Include Signed-off-by and Assisted-by trailers +5. **Confirm with user**: Display message and wait for approval before committing + +**CRITICAL**: Never commit without explicit user confirmation. + +## Conventional Commit Format + +### Structure + +```text +(): + +[optional body] + +Signed-off-by: +Assisted-by: (via Cursor) +``` + +### Type Selection + +Choose the appropriate commit type based on changes: + +| Type | Description | Example | +| ------ | ------------- | --------- | +| `feat` | New features | `feat(pipeline): add start --output flag` | +| `fix` | Bug fixes | `fix(taskrun): resolve log streaming race` | +| `docs` | Documentation | `docs(README): update installation steps` | +| `refactor` | Code refactoring | `refactor(actions): simplify CRUD helpers` | +| `test` | Test changes | `test(pipelinerun): add describe unit tests` | +| `chore` | Maintenance | `chore(deps): update go dependencies` | +| `build` | Build system | `build(Makefile): add vendor target` | +| `ci` | CI/CD changes | `ci(github): add golangci-lint action` | +| `perf` | Performance | `perf(log): optimize pod log streaming` | +| `style` | Code style | `style(format): run goimports formatter` | +| `revert` | Revert commit | `revert: undo breaking CLI flag change` | + +For complete type reference, see `references/commit-types.md`. + +### Scope Rules + +#### Priority 1: Component from changed files + +Analyze staged files to identify the primary component: + +```bash +git diff --cached --name-only +``` + +| File pattern | Scope | Example commit | +| ------------ | ----- | -------------- | +| `pkg/cmd/pipeline/*` | `pipeline` | `feat(pipeline): add start --dry-run flag` | +| `pkg/cmd/task/*` | `task` | `fix(task): resolve list filtering` | +| `pkg/cmd/pipelinerun/*` | `pipelinerun` | `feat(pipelinerun): add cancel subcommand` | +| `pkg/cmd/taskrun/*` | `taskrun` | `fix(taskrun): correct log streaming` | +| `pkg/actions/*` | `actions` | `refactor(actions): simplify CRUD` | +| `pkg/formatted/*` | `formatted` | `feat(formatted): add JSON output` | +| `pkg/log/*` | `log` | `fix(log): handle container restart` | +| `pkg/pods/*` | `pods` | `fix(pods): resolve container lookup` | +| `pkg/cli/*` | `cli` | `refactor(cli): extract client setup` | +| `pkg/flags/*` | `flags` | `feat(flags): add --output flag` | +| `pkg/plugins/*` | `plugins` | `feat(plugins): add discovery logic` | +| `pkg/version/*` | `version` | `fix(version): correct server detection` | +| `docs/*` | `docs` or filename | `docs(README): update steps` | +| `test/*` | component being tested | `test(pipeline): add E2E tests` | +| `cmd/*` | command name | `feat(tkn): add new subcommand` | +| Root files | filename | `chore(Makefile): add target` | +| `AGENTS.md`, `CLAUDE.md` | `docs` | `docs(AGENTS.md): update conventions` | + +#### Priority 2: GitHub issue number (optional) + +If the work is tracked in a GitHub issue and the user provides one, it can be used as the scope: + +```text +# Branch: fix-123-log-race +# Scope: #123 or component name +# Result: fix(log): resolve streaming race condition + +Fixes #123 +``` + +Add `Fixes #NNN` or `Closes #NNN` in the commit body (not the scope) — this is the standard GitHub convention for auto-closing issues. + +#### Priority 3: Ask user + +If changed files span multiple components or scope is unclear, ask the user which component is the primary focus. + +## Line Length Requirements + +### Subject Line + +- **Target**: 50 characters maximum +- **Hard limit**: 72 characters +- **Format**: `type(scope): description` counts toward limit +- **Tips**: Use present tense, no period at end + +```text +# Good (40 chars) +feat(pipeline): add start --dry-run + +# Too long - exceeds 72 char hard limit +feat(pipeline): add comprehensive dry-run support with preview output for pipeline start command +``` + +### Body + +- **Wrap at 72 characters per line** +- **Blank line** required between subject and body +- **Content**: Explain why, not what (code shows what) +- **Format**: Wrap manually or use heredoc in git commit + +```text +feat(pipeline): add start --dry-run + +Add dry-run flag to pipeline start command that previews the +PipelineRun YAML without creating it. Useful for validating +parameters and workspace bindings before submission. + +Signed-off-by: Developer Name +Assisted-by: Claude Sonnet 4.5 (via Cursor) +``` + +## Required Trailers + +### Signed-off-by + +**Always include**: `Signed-off-by: ` + +This certifies the Developer Certificate of Origin (DCO) — required by tektoncd upstream. + +**Detection priority order**: + +1. Environment variables: `$GIT_AUTHOR_NAME` and `$GIT_AUTHOR_EMAIL` +2. Git config: `git config user.name` and `git config user.email` +3. If neither configured, ask user to provide details + +```bash +echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" +git config user.name +git config user.email +``` + +If neither is configured, ask the user to provide their name and email. + +### Assisted-by + +**Always include**: `Assisted-by: (via Cursor)` + +**Format examples**: + +```text +Assisted-by: Claude Sonnet 4.5 (via Cursor) +Assisted-by: Claude Opus 4.5 (via Cursor) +``` + +Use the actual model name (Claude Sonnet 4.5, Claude Opus 4.5, etc.). + +## User Confirmation Requirement + +**CRITICAL RULE**: Always ask for user confirmation before executing `git commit`. + +### Confirmation Workflow + +1. **Generate** the commit message following all rules above +2. **Display** the complete message to the user with separator +3. **Ask**: "Should I commit with this message? (y/n)" +4. **Wait** for user response +5. **Commit** only if user confirms (yes/y/affirmative) + +### Example Interaction + +```text +Generated commit message: +--- +feat(pipeline): add JSON output support + +Add --output=json flag to pipeline list and describe commands. +Uses the existing formatted package JSON helpers. + +Signed-off-by: Developer Name +Assisted-by: Claude Sonnet 4.5 (via Cursor) +--- + +Should I commit with this message? (y/n) +``` + +Wait for user response before proceeding. + +## Commit Execution + +Use heredoc format for proper multi-line handling: + +```bash +git commit -m "$(cat <<'EOF' +feat(pipeline): add JSON output support + +Add --output=json flag to pipeline list and describe commands. + +Signed-off-by: Developer Name +Assisted-by: Claude Sonnet 4.5 (via Cursor) +EOF +)" +``` + +**Never use**: + +- `--no-verify` (skips pre-commit hooks) +- `--no-gpg-sign` (skips signing) +- `--amend` (unless explicitly requested and safe) + +## Complete Examples + +### Feature with component scope + +```text +feat(pipeline): add start --dry-run + +Add dry-run flag to pipeline start command that previews the +PipelineRun YAML without creating it on the cluster. + +Signed-off-by: Jane Developer +Assisted-by: Claude Sonnet 4.5 (via Cursor) +``` + +### Bug fix closing a GitHub issue + +```text +fix(log): resolve concurrent streaming panic + +Prevent nil pointer dereference when multiple containers stream +logs simultaneously during a TaskRun. + +Fixes #789 + +Signed-off-by: John Developer +Assisted-by: Claude Sonnet 4.5 (via Cursor) +``` + +### Documentation update + +```text +docs(AGENTS.md): update architecture conventions + +Add command tree structure and clarify golden-file testing +rules for contributors. + +Signed-off-by: Jane Developer +Assisted-by: Claude Sonnet 4.5 (via Cursor) +``` + +### Breaking change + +```text +feat(cli)!: remove deprecated --namespace flag + +Remove the deprecated short-form --namespace flag from all +commands. Use -n instead. Deprecated since v0.30. + +BREAKING CHANGE: Removed --namespace long flag from all +commands. Use -n or --ns instead. + +Signed-off-by: John Developer +Assisted-by: Claude Sonnet 4.5 (via Cursor) +``` + +## Validation Rules + +Every commit message must pass these checks: + +1. **Subject line format**: Must match `type(scope): description` (conventional commits) +2. **Subject line length**: Target 50 chars, hard limit 72 chars +3. **No trailing punctuation**: Subject must not end with `.` `!` `?` `,` `:` `;` (exception: `!` for breaking changes like `feat!:`) +4. **No trailing whitespace**: On any line +5. **Blank line after subject**: Required before body text +6. **Body line length**: Wrap at 72 characters per line +7. **Valid commit type**: Must be one of: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `build`, `ci`, `perf`, `revert` +8. **Signed-off-by trailer**: Required — `Signed-off-by: Name ` +9. **Assisted-by trailer**: Required when AI assists — `Assisted-by: Model (via Tool)` +10. **Trailer spacing**: Blank line before trailers, no blank lines between them, no trailing blank lines + +## Auto-Detection Summary + +When generating commit messages: + +1. Run `git status` (without -uall flag) +2. Run `git diff` for staged and unstaged changes +3. Identify primary component from staged file paths +4. If scope unclear, ask user +5. If user mentions a GitHub issue number, add `Fixes #NNN` to body +6. Analyze staged files to determine commit type +7. Generate appropriate scope and description +8. Detect author info from environment variables or git config +9. Ensure subject line is ≤50 characters (max 72) +10. Wrap body text at 72 characters per line +11. Add required trailers (Signed-off-by and Assisted-by) +12. Format according to conventional commits standard +13. **Display message and ask for user confirmation** +14. Only commit after receiving confirmation + +## Additional Resources + +For detailed information: + +- **`references/commit-types.md`** - Complete commit type reference with descriptions diff --git a/.claude/skills/commit-message/references/commit-types.md b/.claude/skills/commit-message/references/commit-types.md new file mode 100644 index 000000000..1a0f1bf5c --- /dev/null +++ b/.claude/skills/commit-message/references/commit-types.md @@ -0,0 +1,269 @@ +# Commit Types Reference + +Complete reference for conventional commit types used in the Tekton CLI project. + +## Standard Types + +### feat - New Features + +Use for new features or functionality added to the codebase. + +**Examples**: + +- `feat(pipeline): add start --dry-run flag` +- `feat(task): add describe --output=json` +- `feat(plugins): add plugin discovery mechanism` +- `feat(flags): expose new --output format` + +**When to use**: + +- Adding new capabilities +- Introducing new commands or sub-commands +- Adding new CLI flags or output formats +- Adding new configuration options + +### fix - Bug Fixes + +Use for bug fixes that resolve incorrect behavior. + +**Examples**: + +- `fix(log): resolve streaming race condition` +- `fix(taskrun): correct describe output format` +- `fix(actions): handle missing resource gracefully` +- `fix(cli): prevent nil pointer on missing kubeconfig` + +**When to use**: + +- Fixing crashes or errors +- Resolving incorrect behavior +- Patching security vulnerabilities +- Correcting logic errors + +### docs - Documentation + +Use for documentation-only changes. + +**Examples**: + +- `docs(README): update installation steps` +- `docs(cmd): add pipeline start examples` +- `docs(AGENTS.md): update architecture conventions` +- `docs(contributing): add code review guidelines` + +**When to use**: + +- Updating README files +- Adding or improving code comments +- Updating CLI reference documentation +- Improving developer guides + +### refactor - Code Refactoring + +Use for code changes that neither fix bugs nor add features, but improve code structure. + +**Examples**: + +- `refactor(actions): extract common CRUD logic` +- `refactor(formatted): simplify table renderer` +- `refactor(cli): consolidate client setup` +- `refactor(cmd): reorganize package structure` + +**When to use**: + +- Improving code readability +- Simplifying complex logic +- Reorganizing code structure +- Extracting common functionality + +### test - Testing + +Use for adding or updating tests. + +**Examples**: + +- `test(pipeline): add describe unit tests` +- `test(taskrun): improve golden file coverage` +- `test(log): add streaming edge case tests` +- `test(e2e): add pipeline start E2E test` + +**When to use**: + +- Adding new test cases +- Improving test coverage +- Fixing flaky tests +- Adding integration or E2E tests + +### chore - Maintenance Tasks + +Use for routine maintenance tasks and tooling. + +**Examples**: + +- `chore(deps): update go dependencies` +- `chore(vendor): run go mod vendor` +- `chore(tools): update pre-commit hooks` +- `chore(golden): regenerate golden files` + +**When to use**: + +- Dependency updates +- Tooling configuration +- Repository maintenance +- Build script updates (minor) + +### build - Build System + +Use for changes to build system or external dependencies. + +**Examples**: + +- `build(Makefile): add vendor target` +- `build(goreleaser): update release config` +- `build(docker): update container base image` +- `build(cross): add arm64 build support` + +**When to use**: + +- Makefile changes +- GoReleaser configuration +- Docker/container build changes +- Build configuration updates + +### ci - CI/CD Changes + +Use for changes to CI/CD configuration and scripts. + +**Examples**: + +- `ci(github): add golangci-lint action` +- `ci(actions): update test workflow` +- `ci(tekton): update CI pipeline` +- `ci(pre-commit): add new validation hooks` + +**When to use**: + +- GitHub Actions updates +- Tekton CI pipeline changes +- Pre-commit hook changes +- CI pipeline modifications + +### perf - Performance Improvements + +Use for changes that improve performance. + +**Examples**: + +- `perf(log): optimize pod log streaming` +- `perf(actions): reduce API call count` +- `perf(list): implement pagination` +- `perf(cli): lazy-load Tekton clients` + +**When to use**: + +- Optimization work +- Reducing latency +- Improving throughput +- Memory usage improvements + +### style - Code Style + +Use for formatting and style changes that don't affect code behavior. + +**Examples**: + +- `style(format): run goimports formatter` +- `style(lint): fix golangci-lint warnings` +- `style(markdown): fix markdownlint issues` +- `style(naming): apply consistent naming` + +**When to use**: + +- Running code formatters +- Fixing linter warnings (style-only) +- Applying consistent naming +- Whitespace/indentation fixes + +### revert - Revert Previous Commit + +Use for reverting previous commits. + +**Examples**: + +- `revert: undo breaking CLI flag change` +- `revert(pipeline): revert start refactoring` +- `revert: "feat(task): add new flag"` + +**When to use**: + +- Undoing problematic changes +- Rolling back breaking changes +- Reverting commits that caused issues + +**Format**: Include reference to original commit in body + +## Breaking Changes + +For any commit type, add `!` after type/scope to indicate breaking change: + +**Examples**: + +- `feat(cli)!: change default output format` +- `fix(flags)!: remove deprecated --namespace flag` +- `refactor(actions)!: restructure client interface` + +**Body should include**: + +```markdown +BREAKING CHANGE: +``` + +## Type Selection Guide + +### Decision Tree + +1. **Does it add new functionality?** → `feat` +2. **Does it fix a bug?** → `fix` +3. **Is it documentation only?** → `docs` +4. **Does it change code structure without behavior change?** → `refactor` +5. **Is it test-related?** → `test` +6. **Is it dependency/maintenance?** → `chore` +7. **Is it build system related?** → `build` +8. **Is it CI/CD related?** → `ci` +9. **Does it improve performance?** → `perf` +10. **Is it formatting/style only?** → `style` +11. **Does it revert a previous commit?** → `revert` + +### Common Mistakes + +**Wrong**: `chore(cli): add new subcommand` +**Right**: `feat(cli): add new subcommand` +*Reason: Adding a subcommand is a feature, not maintenance* + +**Wrong**: `feat(tests): add test coverage` +**Right**: `test(pipeline): add test coverage` +*Reason: Tests have their own type* + +**Wrong**: `fix(docs): update README` +**Right**: `docs(README): update installation steps` +*Reason: Documentation has its own type* + +**Wrong**: `chore(ci): update GitHub Actions` +**Right**: `ci(github): update test workflow` +*Reason: CI changes have their own type* + +## Multiple Changes in One Commit + +When a commit includes multiple types of changes, choose the most significant: + +**Example**: Adding feature + tests + +- **Choose**: `feat(pipeline): add start --dry-run flag` +- **Body mentions**: "Includes unit tests" + +**Example**: Bug fix + refactoring + +- **Choose**: `fix(log): resolve streaming race condition` +- **Body mentions**: "Refactored log reader for clarity" + +**Guideline**: If changes are too diverse, consider splitting into multiple commits. diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md new file mode 100644 index 000000000..2d85420fd --- /dev/null +++ b/.claude/skills/release-notes/SKILL.md @@ -0,0 +1,221 @@ +--- +name: release-notes +description: This skill should be used when the user asks to "create release note", "generate release notes", "release notes", "release changelog", "update GitHub release", or wants to generate categorized release notes between tags. Gathers PR/commit data via gh CLI, categorizes changes, and outputs formatted release notes. The user can optionally specify a release version (e.g. "create release note v0.45.0") to bypass auto-detection. +version: 0.1.0 +--- + +# Release Notes Generation + +Generate categorized release notes for Tekton CLI releases by gathering PR/commit data between two git tags and producing formatted markdown output. + +## Purpose + +Skill that: + +- Auto-detects current and previous tags +- Gathers PR and commit data via `gh` CLI +- Uses intelligent categorization +- Outputs release notes matching the project's established format +- Optionally updates the GitHub release + +## Workflow + +### Step 1: Pull latest tags + +```bash +git pull origin --tags +``` + +Ensure all tags are available locally before detection. + +### Step 2: Detect repository info + +```bash +gh repo view --json owner,name +``` + +Extract `owner` and `repo` name for API calls. + +### Step 3: Detect tags + +**If the user specified a version** (e.g. "create release note v0.45.0"), use that directly as the current tag — skip auto-detection. Validate that the tag exists locally: + +```bash +git tag --list 'v0.45.0' +``` + +If the tag doesn't exist locally, error and ask the user to verify. + +**Otherwise, auto-detect current tag:** + +```bash +git tag --points-at HEAD +``` + +Filter for `v*` prefixed tags. If no tag points at HEAD, list recent tags and ask the user which tag to use: + +```bash +git tag --list 'v*' --sort=-version:refname | head -10 +``` + +**Previous tag:** + +```bash +git tag --list 'v*' --sort=-version:refname +``` + +Find the entry immediately after the current tag in the version-sorted list. + +**CRITICAL**: Confirm both tags with the user before proceeding. Display: + +```text +Current tag: v0.45.0 +Previous tag: v0.44.1 + +Proceed with these tags? (y/n) +``` + +### Step 4: Verify prerequisites + +Check `gh` authentication: + +```bash +gh auth status +``` + +If not authenticated, instruct the user to run `gh auth login`. + +**Validate that both tags exist on GitHub:** + +```bash +gh api repos/{owner}/{repo}/git/ref/tags/{current_tag} +gh api repos/{owner}/{repo}/git/ref/tags/{previous_tag} +``` + +If either tag does not exist on GitHub, **stop and report the error** — do not proceed. + +**Check GitHub release status for the current tag:** + +```bash +gh release view {current_tag} --json isDraft,isPrerelease,tagName +``` + +- If **no release exists** for the tag: warn the user, but allow proceeding (notes can be generated for preview, but cannot be uploaded). +- If the release is a **draft** (`isDraft: true`): proceed normally — this is the expected state for generating release notes. +- If the release is **published** (not draft, not prerelease): **ask for explicit confirmation** before proceeding, since updating will override an already-published release. + +### Step 5: Gather PR/commit data + +Use `gh api` to collect data between the two tags: + +**Compare commits:** + +```bash +gh api repos/{owner}/{repo}/compare/{previous_tag}...{current_tag} --jq '.commits[].sha' +``` + +**For each commit, find associated PRs:** + +```bash +gh api repos/{owner}/{repo}/commits/{sha}/pulls +``` + +**Deduplicate by PR number.** For each PR/commit, extract: + +- PR number, title, body, author, URL, labels +For commits not associated with any PR, include them as standalone commit entries with their SHA, message, author, and URL. + +**IMPORTANT**: This step involves many API calls. Process commits in reasonable batches and report progress to the user. + +### Step 6: Categorize changes + +Using the gathered PR/commit data, categorize each entry into exactly these sections (skip empty ones): + +- `## ✨ Major changes and Features` +- `## 🐛 Bug Fixes` +- `## 📚 Documentation Updates` +- `## ⚙️ Chores` + +Use the entry format specified in `references/release-notes-format.md`. + +**Categorization guidelines:** + +- New capabilities, enhancements, significant behavior changes → Features +- Bug fixes, error corrections, regression fixes → Bug Fixes +- Documentation-only changes (docs/, README, comments) → Documentation Updates +- Dependencies, CI/CD, refactoring, formatting, test-only changes → Chores +**Internal vs user-facing detection:** + +Changes that match ANY of these patterns are internal and belong in Chores, NOT Features — regardless of `feat:` prefix: + +- CI/CD pipeline changes (`.tekton/`, `.github/workflows/`, Makefile targets) +- Release infrastructure (release scripts, goreleaser config) +- Test infrastructure (test helpers, e2e framework, test configuration) +- Build system changes (Dockerfile, build scripts) +- Developer tooling (linter config, pre-commit hooks, code generation) +- Internal refactoring that doesn't change user-visible behavior + +Only classify as Features when the change is **visible to end users**: new CLI commands, new flags, new output formats, new plugin capabilities, new resource support. + +### Step 7: Assemble complete release notes + +Combine all sections in this order: + +1. **Header** (see `references/release-notes-format.md` for template) +2. **Categorized sections** from Step 6 +3. **Installation section** (see `references/release-notes-format.md` for template) +4. **GitHub auto-generated changelog**: + +```bash +gh api repos/{owner}/{repo}/releases/generate-notes -f tag_name="{current_tag}" -f previous_tag_name="{previous_tag}" +``` + +Extract the `body` field from the response. + +### Step 8: Output, save, and optional GitHub release update + +1. **Always write** the complete release notes to `/tmp/release-notes-{current_tag}.md` and tell the user the file path. +2. **Display** the complete release notes to the user. +3. **Ask** if they want to update the GitHub release: + +```text +Release notes saved to /tmp/release-notes-{current_tag}.md + +Would you like to update the GitHub release for {current_tag} with these notes? (y/n) +``` + +4. If yes, update via: + +```bash +gh release edit {current_tag} --notes-file /tmp/release-notes-{current_tag}.md +``` + +If the release has a TODO placeholder (matching pattern `TODO: XXXXX.*?see older releases for some example`), replace only that placeholder in the existing release body rather than overwriting the entire body. + +### Step 9: Slack announcement (optional) + +After Step 8, ask the user if they want a Slack announcement message. + +If yes: + +1. Generate a friendly summary with a few emojis (not excessive), listing the top 5-7 most important features and/or bug fixes. Max 7 items total. +2. Save to `/tmp/release-notes-slack-{current_tag}.txt` and tell the user the file path so they can copy-paste. +3. Append the GitHub release URL at the end of the message. + +## Error Handling + +| Scenario | Action | +| --- | --- | +| No tag at HEAD | List recent tags, ask user to pick one | +| User-specified tag doesn't exist locally | Error and ask user to verify the tag name | +| Tag doesn't exist on GitHub | Stop and report error — do not proceed | +| `gh` not authenticated | Instruct user to run `gh auth login` | +| No previous tag found | Ask user to provide one manually | +| No GitHub release for tag | Warn but allow generating notes for preview | +| Release is a draft | Proceed normally (expected state) | +| Release is already published | Ask for explicit confirmation before overriding | +| API rate limiting | Report the error, suggest waiting or using a different token | + +## User Confirmation Requirements + +**CRITICAL**: Always confirm tags before gathering data. Always confirm before updating a GitHub release. Never update a release without explicit user approval. diff --git a/.claude/skills/release-notes/references/release-notes-format.md b/.claude/skills/release-notes/references/release-notes-format.md new file mode 100644 index 000000000..95fc6ad17 --- /dev/null +++ b/.claude/skills/release-notes/references/release-notes-format.md @@ -0,0 +1,115 @@ +# Release Notes Format Reference + +## Entry Format + +Each release note entry MUST follow this exact format. The Link line MUST be indented with two spaces so it renders as a nested sub-bullet: + +```markdown +* **Bold title:** One-sentence description of the change. + * Link: +``` + +### Rules + +- The first bullet MUST start with `*` (no indent) with a bold title followed by a colon and a description. +- The Link line MUST start with `* Link:` (two-space indent) with the PR or commit URL. +- Do NOT add a Contributors section. + +## Header Template + +```markdown +# Tekton CLI {tag} + +Tekton CLI {tag} has been released 🎉 +``` + +## Section Headers + +Use exactly these section headers (skip empty ones): + +```markdown +## ✨ Major changes and Features +## 🐛 Bug Fixes +## 📚 Documentation Updates +## ⚙️ Chores +``` + +## Installation Section Template + +```markdown +## Installation + +Install `tkn` using one of the following methods: + +### Homebrew (macOS/Linux) + +\`\`\`shell +brew install tektoncd-cli +\`\`\` + +### Release binary + +Download the binary for your platform from the [GitHub release page](https://github.com/tektoncd/cli/releases/tag/{tag}). + +### From source + +\`\`\`shell +go install github.com/tektoncd/cli/cmd/tkn@{tag} +\`\`\` + +### Documentation + +https://github.com/tektoncd/cli/tree/{tag}/docs +``` + +## Complete Example + +```markdown +# Tekton CLI v0.45.0 + +Tekton CLI v0.45.0 has been released 🎉 + +## ✨ Major changes and Features + +* **Add pipeline start --dry-run flag:** Preview PipelineRun YAML without creating it on the cluster. + * Link: https://github.com/tektoncd/cli/pull/1234 +* **Support JSON output for task describe:** Add --output=json flag to task describe command. + * Link: https://github.com/tektoncd/cli/pull/1230 + +## 🐛 Bug Fixes + +* **Fix log streaming race condition:** Corrected concurrent container log streaming that caused nil pointer panics. + * Link: https://github.com/tektoncd/cli/pull/1220 + +## ⚙️ Chores + +* **Bump go.opentelemetry.io/otel from 1.28.0 to 1.29.0:** Updated OpenTelemetry dependency to latest version. + * Link: https://github.com/tektoncd/cli/pull/1215 + +## Installation + +Install `tkn` using one of the following methods: + +### Homebrew (macOS/Linux) + +\`\`\`shell +brew install tektoncd-cli +\`\`\` + +### Release binary + +Download the binary for your platform from the [GitHub release page](https://github.com/tektoncd/cli/releases/tag/v0.45.0). + +### From source + +\`\`\`shell +go install github.com/tektoncd/cli/cmd/tkn@v0.45.0 +\`\`\` + +### Documentation + +https://github.com/tektoncd/cli/tree/v0.45.0/docs + +## What's Changed + +``` diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..c923ba7b8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,134 @@ +# Tekton CLI (`tkn`) + +Command-line client for [Tekton Pipelines](https://tekton.dev). Provides +sub-commands to create, list, describe, start, cancel, delete, and log +Tekton resources (Tasks, Pipelines, PipelineRuns, etc.). + +--- + +## Build & Test Commands + +```bash +# Build +go build ./cmd/tkn +make bin/tkn + +# Cross-platform binaries +make cross + +# Test — runs without a cluster +make test +make test-unit +make test-unit-race + +# Lint — must pass before every PR +make lint # golangci-lint + goimports + yamllint +make lint-go # Go only, all packages +make lint-go PKG=./pkg/pods/... # single package (fast) + +# Code formatting +make fmt # gofmt +make goimports # goimports + +# Update golden files (test snapshots) +make update-golden + +# Regenerate docs and formatting +make generated # update-golden + docs + fmt + +# Dependency update — required after go.mod changes +go mod vendor +``` + +E2E tests require a live cluster with Tekton Pipelines installed. +See [DEVELOPMENT.md](./DEVELOPMENT.md) for cluster setup. + +--- + +## Key Conventions + +1. **Cobra command tree.** Every user-facing command lives under `pkg/cmd/`. + The root command is wired in `cmd/tkn/main.go`. Each resource type + (pipeline, task, pipelinerun, …) has its own sub-package with standard + verbs: `create`, `delete`, `describe`, `list`, `start`, `logs`. + +2. **`pkg/` holds the shared logic.** Packages like `pkg/actions/`, + `pkg/formatted/`, `pkg/log/`, and `pkg/pods/` do the real work (API calls, + output formatting, log streaming). The command files in `pkg/cmd/` are mostly wiring, and should + only handle flag parsing and call into these packages — don't put core + logic directly in command handlers. + +3. **Golden-file tests.** Many commands use golden files for expected output. + After changing command output, run `make update-golden` and commit the + updated `.golden` files. + +4. **Vendored dependencies.** This project vendors (`go mod vendor`). + Always commit `vendor/` changes when updating `go.mod`. + +5. **`-mod=vendor` everywhere.** Builds and lints use `-mod=vendor`. + Do not remove this flag. + +6. **Version injection via ldflags.** The client version is injected at build + time through `LDFLAGS`. See the `Makefile` for `VERSIONLDFLAG`. + +--- + +## Architecture + +``` +cmd/ + tkn/ # main entry point + docs/ # doc generation binary + +pkg/ + cmd/ # Cobra command implementations (one sub-package per resource) + pipeline/ # tkn pipeline create|delete|describe|list|start|logs + task/ # tkn task create|delete|describe|list|start|logs + pipelinerun/ + taskrun/ + ... + actions/ # generic Tekton resource CRUD via dynamic client + cli/ # shared CLI params, streams, clients + flags/ # common flag definitions + formatted/ # output formatters (table, YAML, JSON) + log/ # log streaming for PipelineRun/TaskRun + pods/ # logic for finding the right container in a pod, pod/container log helpers + params/ # parameter parsing and merging + plugins/ # tkn plugin discovery + version/ # client/server version reporting + +docs/ # auto-generated CLI reference (markdown + man pages) +test/ # E2E test framework and scripts +hack/ # helper scripts (golden-file updates, etc.) +third_party/ # vendored non-Go assets +``` + +**Key patterns:** + +- Each command sub-package typically has: `.go` (command definition), + `_test.go` (unit tests), and `testdata/` with `.golden` files. +- `pkg/cli/Clients` bundles the Tekton, Kubernetes, and dynamic clients. + Commands receive this via their `Params` interface. + +--- + +## PR Conventions + +- Pull requests must follow the repository PR template defined in `.github/pull_request_template.md`. +- `make lint` must pass with zero issues. +- `make test` must pass with zero failures. +- Run `make generated` and commit generated/golden files when command output changes. +- Commit messages follow [Tekton community standards](https://github.com/tektoncd/community/blob/master/standards.md#commit-messages). + +--- + +## Skills + +For complex workflows, use these repo-local skills: + +- **Commit messages**: Conventional commits with component scopes, line length + validation, DCO Signed-off-by, and Assisted-by trailers. Trigger: "create + commit", "commit changes", "generate commit message" +- **Release notes**: Gather PRs between tags, categorize, output formatted + markdown, optionally update GitHub release. Trigger: "create release note", + "generate release notes", "release changelog" diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..c31706425 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md diff --git a/Makefile b/Makefile index e39f736a4..31d7044d5 100644 --- a/Makefile +++ b/Makefile @@ -102,8 +102,8 @@ $(BIN)/golangci-lint: ; $(info $(M) getting golangci-lint $(GOLANGCI_VERSION)) GOTOOLCHAIN=go$$(grep '^go ' go.mod | awk '{print $$2}') GOBIN=$(BIN) $(GO) install -mod=mod github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_VERSION) .PHONY: lint-go -lint-go: | $(GOLANGCILINT) ; $(info $(M) running golangci-lint…) @ ## Run golangci-lint - $Q $(GOLANGCILINT) run --modules-download-mode=vendor --max-issues-per-linter=0 --max-same-issues=0 --timeout 10m +lint-go: | $(GOLANGCILINT) ; $(info $(M) running golangci-lint…) @ ## Run golangci-lint (PKG=./pkg/pods/... for single package) + $Q $(GOLANGCILINT) run --modules-download-mode=vendor --max-issues-per-linter=0 --max-same-issues=0 --timeout 10m $(PKG) GOIMPORTS = $(BIN)/goimports $(GOIMPORTS): ; $(info $(M) getting goimports ) @@ -125,8 +125,8 @@ test-unit-race: ARGS=-race test-unit-verbose-and-race: ARGS=-v -race $(TEST_UNIT_TARGETS): test-unit .PHONY: $(TEST_UNIT_TARGETS) test-unit -test-unit: ; $(info $(M) running unit tests…) ## Run unit tests - $(GO) test -timeout $(TIMEOUT_UNIT) $(ARGS) $(shell go list ./... | grep -v third_party/) +test-unit: ; $(info $(M) running unit tests…) ## Run unit tests (PKG=./pkg/pods/... for single package) + $(GO) test -timeout $(TIMEOUT_UNIT) $(ARGS) $(PKGS) .PHONY: update-golden update-golden: ./vendor ; $(info $(M) Running unit tests to update golden files…) ## run unit tests (updating golden files)