mirror of
https://github.com/yarlson/lnk.git
synced 2025-09-01 18:02:34 +02:00
feat(cmd): add 'list' command to display managed files
Implements a new 'list' command that shows all files and directories managed by lnk, improving visibility and user experience. fixes #4
This commit is contained in:
@@ -52,6 +52,9 @@ lnk init -r git@github.com:user/dotfiles.git
|
||||
# Add files/directories
|
||||
lnk add ~/.vimrc ~/.config/nvim ~/.gitconfig
|
||||
|
||||
# List managed files
|
||||
lnk list
|
||||
|
||||
# Check status
|
||||
lnk status
|
||||
|
||||
@@ -99,6 +102,7 @@ lnk pull # auto-creates symlinks
|
||||
|
||||
```bash
|
||||
vim ~/.vimrc # edit normally
|
||||
lnk list # see what's managed
|
||||
lnk status # check what changed
|
||||
lnk push "new plugins" # commit & push
|
||||
```
|
||||
@@ -108,6 +112,7 @@ lnk push "new plugins" # commit & push
|
||||
- `lnk init [-r remote]` - Create repo
|
||||
- `lnk add <files>` - Move files to repo, create symlinks
|
||||
- `lnk rm <files>` - Move files back, remove symlinks
|
||||
- `lnk list` - List files managed by lnk
|
||||
- `lnk status` - Git status + sync info
|
||||
- `lnk push [msg]` - Stage all, commit, push
|
||||
- `lnk pull` - Pull + restore missing symlinks
|
||||
|
43
cmd/list.go
Normal file
43
cmd/list.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/yarlson/lnk/internal/core"
|
||||
)
|
||||
|
||||
func newListCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "📋 List files managed by lnk",
|
||||
Long: "Display all files and directories currently managed by lnk.",
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
lnk := core.NewLnk()
|
||||
managedItems, err := lnk.List()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list managed items: %w", err)
|
||||
}
|
||||
|
||||
if len(managedItems) == 0 {
|
||||
printf(cmd, "📋 \033[1mNo files currently managed by lnk\033[0m\n")
|
||||
printf(cmd, " 💡 Use \033[1mlnk add <file>\033[0m to start managing files\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
printf(cmd, "📋 \033[1mFiles managed by lnk\033[0m (\033[36m%d item", len(managedItems))
|
||||
if len(managedItems) > 1 {
|
||||
printf(cmd, "s")
|
||||
}
|
||||
printf(cmd, "\033[0m):\n\n")
|
||||
|
||||
for _, item := range managedItems {
|
||||
printf(cmd, " 🔗 \033[36m%s\033[0m\n", item)
|
||||
}
|
||||
|
||||
printf(cmd, "\n💡 Use \033[1mlnk status\033[0m to check sync status\n")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
@@ -38,6 +38,7 @@ That's it.
|
||||
rootCmd.AddCommand(newInitCmd())
|
||||
rootCmd.AddCommand(newAddCmd())
|
||||
rootCmd.AddCommand(newRemoveCmd())
|
||||
rootCmd.AddCommand(newListCmd())
|
||||
rootCmd.AddCommand(newStatusCmd())
|
||||
rootCmd.AddCommand(newPushCmd())
|
||||
rootCmd.AddCommand(newPullCmd())
|
||||
|
@@ -163,6 +163,60 @@ func (suite *CLITestSuite) TestStatusCommand() {
|
||||
suite.Contains(err.Error(), "no remote configured")
|
||||
}
|
||||
|
||||
func (suite *CLITestSuite) TestListCommand() {
|
||||
// Test list without init - should fail
|
||||
err := suite.runCommand("list")
|
||||
suite.Error(err)
|
||||
suite.Contains(err.Error(), "Lnk repository not initialized")
|
||||
|
||||
// Initialize first
|
||||
err = suite.runCommand("init")
|
||||
suite.Require().NoError(err)
|
||||
suite.stdout.Reset()
|
||||
|
||||
// Test list with no managed files
|
||||
err = suite.runCommand("list")
|
||||
suite.NoError(err)
|
||||
output := suite.stdout.String()
|
||||
suite.Contains(output, "No files currently managed by lnk")
|
||||
suite.Contains(output, "lnk add <file>")
|
||||
suite.stdout.Reset()
|
||||
|
||||
// Add a file
|
||||
testFile := filepath.Join(suite.tempDir, ".bashrc")
|
||||
err = os.WriteFile(testFile, []byte("export PATH=/usr/local/bin:$PATH"), 0644)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.runCommand("add", testFile)
|
||||
suite.Require().NoError(err)
|
||||
suite.stdout.Reset()
|
||||
|
||||
// Test list with one managed file
|
||||
err = suite.runCommand("list")
|
||||
suite.NoError(err)
|
||||
output = suite.stdout.String()
|
||||
suite.Contains(output, "Files managed by lnk")
|
||||
suite.Contains(output, "1 item")
|
||||
suite.Contains(output, ".bashrc")
|
||||
suite.stdout.Reset()
|
||||
|
||||
// Add another file
|
||||
testFile2 := filepath.Join(suite.tempDir, ".vimrc")
|
||||
err = os.WriteFile(testFile2, []byte("set number"), 0644)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.runCommand("add", testFile2)
|
||||
suite.Require().NoError(err)
|
||||
suite.stdout.Reset()
|
||||
|
||||
// Test list with multiple managed files
|
||||
err = suite.runCommand("list")
|
||||
suite.NoError(err)
|
||||
output = suite.stdout.String()
|
||||
suite.Contains(output, "Files managed by lnk")
|
||||
suite.Contains(output, "2 items")
|
||||
suite.Contains(output, ".bashrc")
|
||||
suite.Contains(output, ".vimrc")
|
||||
}
|
||||
|
||||
func (suite *CLITestSuite) TestErrorHandling() {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -207,6 +261,12 @@ func (suite *CLITestSuite) TestErrorHandling() {
|
||||
wantErr: false,
|
||||
outContains: "Moves a file to the lnk repository",
|
||||
},
|
||||
{
|
||||
name: "list help",
|
||||
args: []string{"list", "--help"},
|
||||
wantErr: false,
|
||||
outContains: "Display all files and directories",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@@ -428,6 +428,22 @@ func (l *Lnk) Pull() ([]string, error) {
|
||||
return restored, nil
|
||||
}
|
||||
|
||||
// List returns the list of files and directories currently managed by lnk
|
||||
func (l *Lnk) List() ([]string, error) {
|
||||
// Check if repository is initialized
|
||||
if !l.git.IsGitRepository() {
|
||||
return nil, fmt.Errorf("❌ Lnk repository not initialized\n 💡 Run \033[1mlnk init\033[0m first")
|
||||
}
|
||||
|
||||
// Get managed items from .lnk file
|
||||
managedItems, err := l.getManagedItems()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get managed items: %w", err)
|
||||
}
|
||||
|
||||
return managedItems, nil
|
||||
}
|
||||
|
||||
// RestoreSymlinks finds all managed items from .lnk file and ensures they have proper symlinks
|
||||
func (l *Lnk) RestoreSymlinks() ([]string, error) {
|
||||
var restored []string
|
||||
|
@@ -516,6 +516,76 @@ func (suite *CoreTestSuite) TestStatusDetectsDirtyRepo() {
|
||||
suite.True(status.Dirty, "Repository should be dirty after editing managed file")
|
||||
}
|
||||
|
||||
// Test list functionality
|
||||
func (suite *CoreTestSuite) TestListManagedItems() {
|
||||
// Test list without init - should fail
|
||||
_, err := suite.lnk.List()
|
||||
suite.Error(err)
|
||||
suite.Contains(err.Error(), "Lnk repository not initialized")
|
||||
|
||||
// Initialize repository
|
||||
err = suite.lnk.Init()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Test list with no managed files
|
||||
items, err := suite.lnk.List()
|
||||
suite.Require().NoError(err)
|
||||
suite.Empty(items)
|
||||
|
||||
// Add a file
|
||||
testFile := filepath.Join(suite.tempDir, ".bashrc")
|
||||
content := "export PATH=$PATH:/usr/local/bin"
|
||||
err = os.WriteFile(testFile, []byte(content), 0644)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = suite.lnk.Add(testFile)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Test list with one managed file
|
||||
items, err = suite.lnk.List()
|
||||
suite.Require().NoError(err)
|
||||
suite.Len(items, 1)
|
||||
suite.Contains(items[0], ".bashrc")
|
||||
|
||||
// Add a directory
|
||||
testDir := filepath.Join(suite.tempDir, ".config")
|
||||
err = os.MkdirAll(testDir, 0755)
|
||||
suite.Require().NoError(err)
|
||||
configFile := filepath.Join(testDir, "app.conf")
|
||||
err = os.WriteFile(configFile, []byte("setting=value"), 0644)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = suite.lnk.Add(testDir)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Test list with multiple managed items
|
||||
items, err = suite.lnk.List()
|
||||
suite.Require().NoError(err)
|
||||
suite.Len(items, 2)
|
||||
|
||||
// Check that both items are present
|
||||
found := make(map[string]bool)
|
||||
for _, item := range items {
|
||||
if strings.Contains(item, ".bashrc") {
|
||||
found[".bashrc"] = true
|
||||
}
|
||||
if strings.Contains(item, ".config") {
|
||||
found[".config"] = true
|
||||
}
|
||||
}
|
||||
suite.True(found[".bashrc"], "Should contain .bashrc")
|
||||
suite.True(found[".config"], "Should contain .config")
|
||||
|
||||
// Remove one item and verify list is updated
|
||||
err = suite.lnk.Remove(testFile)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
items, err = suite.lnk.List()
|
||||
suite.Require().NoError(err)
|
||||
suite.Len(items, 1)
|
||||
suite.Contains(items[0], ".config")
|
||||
}
|
||||
|
||||
func TestCoreSuite(t *testing.T) {
|
||||
suite.Run(t, new(CoreTestSuite))
|
||||
}
|
||||
|
Reference in New Issue
Block a user