feat: add versioning

This commit is contained in:
2026-07-30 11:56:27 +02:00
parent e045bd39b2
commit 033b0bb5be
6 changed files with 74 additions and 1 deletions

View File

@@ -218,3 +218,26 @@ When behavior changes:
If a product decision would materially affect persistence compatibility, If a product decision would materially affect persistence compatibility,
GitHub writes, AI data exposure, destructive behavior, or default keybindings, GitHub writes, AI data exposure, destructive behavior, or default keybindings,
ask the repository owner rather than guessing. ask the repository owner rather than guessing.
## Semantic versioning
The application version lives in `version.go` and must use Semantic Versioning
in `MAJOR.MINOR.PATCH` form.
- Keep the major version at `0` until the repository owner explicitly requests
a `1.0.0` or later release.
- Increment `MINOR` and reset `PATCH` to zero for a completed new
backward-compatible feature. During `0.x`, also use a minor increment for an
intentional breaking change and document that break.
- Increment `PATCH` for a completed backward-compatible bug fix, refinement,
or other non-feature change.
- Apply one version increment per complete logical change, not per user prompt.
Follow-up questions and refinements that finish the same feature share that
feature's single version increment.
- Before incrementing, determine the logical change boundary from the
conversation and current work. When the boundary is unclear and Jujutsu is
available, inspect the current change and its parent read-only; an empty
described parent may identify the feature being developed. Never mutate
Jujutsu history merely to determine a version.
- If the current logical change already contains the appropriate version
increment, do not increment it again for another prompt in that same change.

View File

@@ -134,6 +134,7 @@ Adjust polling or inspect all command-line options:
```sh ```sh
diple --poll 15s diple --poll 15s
diple --version
diple --help diple --help
``` ```

17
cli.go
View File

@@ -8,6 +8,17 @@ import (
var completionShells = []string{"bash", "zsh", "fish"} var completionShells = []string{"bash", "zsh", "fish"}
func handleVersionCommand(args []string, output io.Writer) (bool, error) {
if len(args) == 0 || args[0] != "--version" {
return false, nil
}
if len(args) != 1 {
return true, fmt.Errorf("usage: diple --version")
}
_, err := fmt.Fprintf(output, "diple %s\n", dipleVersion)
return true, err
}
func handleCompletionCommand(args []string, output io.Writer) (bool, error) { func handleCompletionCommand(args []string, output io.Writer) (bool, error) {
if len(args) == 0 || args[0] != "completion" { if len(args) == 0 || args[0] != "completion" {
return false, nil return false, nil
@@ -55,6 +66,7 @@ func writeCLIHelp(output io.Writer, defaults Config, configPath string) {
Usage: Usage:
diple [options] diple [options]
diple --version
diple completion <bash|zsh|fish> diple completion <bash|zsh|fish>
diple help diple help
@@ -92,6 +104,7 @@ Local state:
Other: Other:
-h, --help Show this help and exit. -h, --help Show this help and exit.
--version Show the application version and exit.
Boolean options accept explicit values, for example --cache=false. Boolean options accept explicit values, for example --cache=false.
Command-line options override TOML settings. GH_REPO is used only when Command-line options override TOML settings. GH_REPO is used only when
@@ -158,7 +171,7 @@ _diple_completion() {
;; ;;
esac esac
local options="--repo --all --limit --poll --endpoint --theme --dashboard-mode --thread-list-width --fold-resolved --compact-reviews --path-scroll --path-scroll-interval --editor-mode --config --cache --cache-max-age --cache-dir --help -h" local options="--repo --all --limit --poll --endpoint --theme --dashboard-mode --thread-list-width --fold-resolved --compact-reviews --path-scroll --path-scroll-interval --editor-mode --config --cache --cache-max-age --cache-dir --version --help -h"
COMPREPLY=($(compgen -W "${options}" -- "${current}")) COMPREPLY=($(compgen -W "${options}" -- "${current}"))
} }
complete -F _diple_completion diple complete -F _diple_completion diple
@@ -202,6 +215,7 @@ _diple() {
'--cache=[enable local read cache]:boolean:(true false)' \ '--cache=[enable local read cache]:boolean:(true false)' \
'--cache-max-age[maximum offline cache age]:duration:' \ '--cache-max-age[maximum offline cache age]:duration:' \
'--cache-dir[local read-cache directory]:directory:_directories' \ '--cache-dir[local read-cache directory]:directory:_directories' \
'--version[show application version]' \
'(-h --help)'{-h,--help}'[show help]' '(-h --help)'{-h,--help}'[show help]'
} }
@@ -234,5 +248,6 @@ complete -c diple -l config -r -F -d 'TOML configuration file'
complete -c diple -l cache -d 'Enable local read cache' complete -c diple -l cache -d 'Enable local read cache'
complete -c diple -l cache-max-age -x -d 'Maximum offline cache age' complete -c diple -l cache-max-age -x -d 'Maximum offline cache age'
complete -c diple -l cache-dir -r -a '(__fish_complete_directories)' -d 'Local read-cache directory' complete -c diple -l cache-dir -r -a '(__fish_complete_directories)' -d 'Local read-cache directory'
complete -c diple -l version -d 'Show application version'
complete -c diple -s h -l help -d 'Show help' complete -c diple -s h -l help -d 'Show help'
` `

View File

@@ -2,10 +2,34 @@ package main
import ( import (
"bytes" "bytes"
"regexp"
"strings" "strings"
"testing" "testing"
) )
func TestVersionCommandPrintsSemanticVersion(t *testing.T) {
var output bytes.Buffer
handled, err := handleVersionCommand([]string{"--version"}, &output)
if err != nil {
t.Fatal(err)
}
if !handled || output.String() != "diple "+dipleVersion+"\n" {
t.Fatalf("version output = %q, handled=%t", output.String(), handled)
}
if !regexp.MustCompile(`^0\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$`).MatchString(dipleVersion) {
t.Fatalf("version %q is not a pre-1.0 semantic version", dipleVersion)
}
}
func TestVersionCommandRejectsAdditionalArguments(t *testing.T) {
handled, err := handleVersionCommand(
[]string{"--version", "--help"}, &bytes.Buffer{},
)
if !handled || err == nil || !strings.Contains(err.Error(), "usage: diple --version") {
t.Fatalf("handled=%t error=%v", handled, err)
}
}
func TestCompletionCommandGeneratesSupportedShells(t *testing.T) { func TestCompletionCommandGeneratesSupportedShells(t *testing.T) {
for _, shell := range completionShells { for _, shell := range completionShells {
t.Run(shell, func(t *testing.T) { t.Run(shell, func(t *testing.T) {
@@ -65,6 +89,7 @@ func TestCLIHelpIsGroupedAndActionable(t *testing.T) {
"Appearance and navigation:", "Appearance and navigation:",
"Local state:", "Local state:",
"diple completion <bash|zsh|fish>", "diple completion <bash|zsh|fish>",
"--version",
"--repo OWNER/REPOSITORY", "--repo OWNER/REPOSITORY",
"--cache=false", "--cache=false",
"gh auth login", "gh auth login",

View File

@@ -11,6 +11,12 @@ import (
) )
func main() { func main() {
if handled, err := handleVersionCommand(os.Args[1:], os.Stdout); handled {
if err != nil {
exitf("%v", err)
}
return
}
if handled, err := handleCompletionCommand(os.Args[1:], os.Stdout); handled { if handled, err := handleCompletionCommand(os.Args[1:], os.Stdout); handled {
if err != nil { if err != nil {
exitf("%v", err) exitf("%v", err)

3
version.go Normal file
View File

@@ -0,0 +1,3 @@
package main
const dipleVersion = "0.1.0"