feat(v0.5.3): SessionName deterministic tmux session naming
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SessionName returns a stable tmux session name for the given workspace.
|
||||
// Format: ctask-<category>-<slug>-<hash6>.
|
||||
//
|
||||
// category and slug are sanitized to [A-Za-z0-9_-]; characters outside that
|
||||
// set become '_'; runs of '_' collapse; both are lowercased. slug is
|
||||
// truncated to 30 characters maximum (after sanitization). hash6 is the
|
||||
// first six lowercase-hex characters of sha256(canonical absolute path).
|
||||
//
|
||||
// On Windows the path is lowercased before hashing to match
|
||||
// config.searchRootKey conventions for case-insensitive filesystems.
|
||||
func SessionName(category, slug, absWsPath string) string {
|
||||
cat := sanitizeNameComponent(category)
|
||||
sl := sanitizeNameComponent(slug)
|
||||
if len(sl) > 30 {
|
||||
sl = sl[:30]
|
||||
sl = strings.TrimRight(sl, "_")
|
||||
}
|
||||
clean := filepath.Clean(absWsPath)
|
||||
if runtime.GOOS == "windows" {
|
||||
clean = strings.ToLower(clean)
|
||||
}
|
||||
sum := sha256.Sum256([]byte(clean))
|
||||
hash := hex.EncodeToString(sum[:])[:6]
|
||||
return "ctask-" + cat + "-" + sl + "-" + hash
|
||||
}
|
||||
|
||||
// sanitizeNameComponent replaces every char outside [A-Za-z0-9_-] with '_',
|
||||
// collapses runs of '_', trims leading/trailing '_' and '-', and lowercases.
|
||||
func sanitizeNameComponent(s string) string {
|
||||
if s == "" {
|
||||
return "_"
|
||||
}
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
b.WriteRune(r)
|
||||
case r >= 'A' && r <= 'Z':
|
||||
b.WriteRune(r + ('a' - 'A'))
|
||||
case r >= '0' && r <= '9':
|
||||
b.WriteRune(r)
|
||||
case r == '_' || r == '-':
|
||||
b.WriteRune(r)
|
||||
default:
|
||||
b.WriteByte('_')
|
||||
}
|
||||
}
|
||||
out := b.String()
|
||||
for strings.Contains(out, "__") {
|
||||
out = strings.ReplaceAll(out, "__", "_")
|
||||
}
|
||||
out = strings.Trim(out, "_-")
|
||||
if out == "" {
|
||||
return "_"
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user