Skip to content

Commit 17687e2

Browse files
committed
feat: add --dereference-symlinks flag for recursive symlink resolution
add new --dereference-symlinks boolean flag that recursively resolves all symlinks to their target content during file collection. this works on symlinks inside directories, not just CLI arguments. the flag is wired through cli/parse.go to boxo's SerialFileOptions.DereferenceSymlinks. deprecate --dereference-args which only worked on symlinks passed directly as CLI arguments. the help text now indicates it is deprecated and directs users to use --dereference-symlinks instead. ref: ipfs/specs#499
1 parent 894a196 commit 17687e2

File tree

4 files changed

+37
-27
lines changed

4 files changed

+37
-27
lines changed

cli/parse.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ func stdinName(req *cmds.Request) string {
8484
return name
8585
}
8686

87+
func dereferenceSymlinks(req *cmds.Request) bool {
88+
deref, _ := req.Options[cmds.DerefSymlinks].(bool)
89+
return deref
90+
}
91+
8792
type parseState struct {
8893
cmdline []string
8994
i int
@@ -324,7 +329,7 @@ func parseArgs(req *cmds.Request, root *cmds.Command, stdin *os.File) error {
324329
if err != nil {
325330
return err
326331
}
327-
nf, err := appendFile(fpath, argDef, isRecursive(req), filter)
332+
nf, err := appendFile(fpath, argDef, isRecursive(req), filter, dereferenceSymlinks(req))
328333
if err != nil {
329334
return err
330335
}
@@ -542,7 +547,7 @@ func getArgDef(i int, argDefs []cmds.Argument) *cmds.Argument {
542547
const notRecursiveFmtStr = "'%s' is a directory, use the '-%s' flag to specify directories"
543548
const dirNotSupportedFmtStr = "invalid path '%s', argument '%s' does not support directories"
544549

545-
func appendFile(fpath string, argDef *cmds.Argument, recursive bool, filter *files.Filter) (files.Node, error) {
550+
func appendFile(fpath string, argDef *cmds.Argument, recursive bool, filter *files.Filter, dereferenceSymlinks bool) (files.Node, error) {
546551
stat, err := os.Lstat(fpath)
547552
if err != nil {
548553
return nil, err
@@ -566,7 +571,10 @@ func appendFile(fpath string, argDef *cmds.Argument, recursive bool, filter *fil
566571

567572
return files.NewReaderFile(file), nil
568573
}
569-
return files.NewSerialFileWithFilter(fpath, filter, stat)
574+
return files.NewSerialFileWithOptions(fpath, stat, files.SerialFileOptions{
575+
Filter: filter,
576+
DereferenceSymlinks: dereferenceSymlinks,
577+
})
570578
}
571579

572580
// Inform the user if a file is waiting on input

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
module github.com/ipfs/go-ipfs-cmds
22

3-
go 1.24.0
3+
go 1.24.6
44

55
require (
6-
github.com/ipfs/boxo v0.34.0
7-
github.com/ipfs/go-log/v2 v2.8.1
6+
github.com/ipfs/boxo v0.35.3-0.20260117004328-4ff72d072c00
7+
github.com/ipfs/go-log/v2 v2.9.0
88
github.com/rs/cors v1.11.1
99
github.com/texttheater/golang-levenshtein v1.0.1
1010
golang.org/x/term v0.37.0

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn8
22
github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
33
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5-
github.com/ipfs/boxo v0.34.0 h1:pMP9bAsTs4xVh8R0ZmxIWviV7kjDa60U24QrlGgHb1g=
6-
github.com/ipfs/boxo v0.34.0/go.mod h1:kzdH/ewDybtO3+M8MCVkpwnIIc/d2VISX95DFrY4vQA=
7-
github.com/ipfs/go-log/v2 v2.8.1 h1:Y/X36z7ASoLJaYIJAL4xITXgwf7RVeqb1+/25aq/Xk0=
8-
github.com/ipfs/go-log/v2 v2.8.1/go.mod h1:NyhTBcZmh2Y55eWVjOeKf8M7e4pnJYM3yDZNxQBWEEY=
5+
github.com/ipfs/boxo v0.35.3-0.20260117004328-4ff72d072c00 h1:e9p5CizXgzPlnxt1kzDyYNoKusO4cvDjNG33UqyVhwM=
6+
github.com/ipfs/boxo v0.35.3-0.20260117004328-4ff72d072c00/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0=
7+
github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk=
8+
github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
99
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
1010
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
1111
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1212
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1313
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
1414
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
15-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
16-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
15+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
16+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
1717
github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U=
1818
github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8=
1919
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=

opts.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,30 @@ package cmds
22

33
// Flag names
44
const (
5-
EncShort = "enc"
6-
EncLong = "encoding"
7-
RecShort = "r"
8-
RecLong = "recursive"
9-
ChanOpt = "stream-channels"
10-
TimeoutOpt = "timeout"
11-
OptShortHelp = "h"
12-
OptLongHelp = "help"
13-
DerefLong = "dereference-args"
14-
StdinName = "stdin-name"
15-
Hidden = "hidden"
16-
HiddenShort = "H"
17-
Ignore = "ignore"
18-
IgnoreRules = "ignore-rules-path"
5+
EncShort = "enc"
6+
EncLong = "encoding"
7+
RecShort = "r"
8+
RecLong = "recursive"
9+
ChanOpt = "stream-channels"
10+
TimeoutOpt = "timeout"
11+
OptShortHelp = "h"
12+
OptLongHelp = "help"
13+
DerefLong = "dereference-args"
14+
DerefSymlinks = "dereference-symlinks"
15+
StdinName = "stdin-name"
16+
Hidden = "hidden"
17+
HiddenShort = "H"
18+
Ignore = "ignore"
19+
IgnoreRules = "ignore-rules-path"
1920
)
2021

2122
// options that are used by this package
2223
var OptionEncodingType = StringOption(EncLong, EncShort, "The encoding type the output should be encoded with (json, xml, or text)").WithDefault("text")
2324
var OptionRecursivePath = BoolOption(RecLong, RecShort, "Add directory paths recursively")
2425
var OptionStreamChannels = BoolOption(ChanOpt, "Stream channel output")
2526
var OptionTimeout = StringOption(TimeoutOpt, "Set a global timeout on the command")
26-
var OptionDerefArgs = BoolOption(DerefLong, "Symlinks supplied in arguments are dereferenced")
27+
var OptionDerefArgs = BoolOption(DerefLong, "DEPRECATED: use --dereference-symlinks instead. Only dereferenced symlinks in CLI arguments, not inside directories.")
28+
var OptionDerefSymlinks = BoolOption(DerefSymlinks, "Recursively resolve all symlinks to their target content. Works on symlinks inside directories, not just CLI arguments.")
2729
var OptionStdinName = StringOption(StdinName, "Assign a name if the file source is stdin.")
2830
var OptionHidden = BoolOption(Hidden, HiddenShort, "Include files that are hidden. Only takes effect on recursive add.")
2931
var OptionIgnore = StringsOption(Ignore, "A rule (.gitignore-stype) defining which file(s) should be ignored (variadic, experimental)")

0 commit comments

Comments
 (0)