mirror of
https://github.com/ipfs/kubo.git
synced 2025-12-15 14:11:14 +08:00
feat(add): add support for naming pinned CIDs (#10877)
* feat(add): add support for naming pinned CID Signed-off-by: kapil <kapilsareen584@gmail.com> * fix(add): no double pinning and simplify pin-name - modify PinRoot to accept name parameter, eliminating double pinning - remove automatic filename fallback logic for cleaner behavior - only create named pins when explicitly requested via --pin-name=value - replace NoPinName constant with idiomatic empty string literals - Update help text and tests to reflect explicit-only behavior * docs: changelog * chore: lint * test: negative case for empty pin-name * chore: gofmt --------- Signed-off-by: kapil <kapilsareen584@gmail.com> Co-authored-by: Marcin Rataj <lidel@lidel.org>
This commit is contained in:
@@ -86,7 +86,7 @@ func addMigrationFiles(ctx context.Context, node *core.IpfsNode, paths []string,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipfsPath, err := ufs.Add(ctx, files.NewReaderStatFile(f, fi), options.Unixfs.Pin(pin))
|
ipfsPath, err := ufs.Add(ctx, files.NewReaderStatFile(f, fi), options.Unixfs.Pin(pin, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ const (
|
|||||||
// write-batch. The total size of the batch is limited by
|
// write-batch. The total size of the batch is limited by
|
||||||
// BatchMaxnodes and BatchMaxSize.
|
// BatchMaxnodes and BatchMaxSize.
|
||||||
DefaultBatchMaxSize = 100 << 20 // 20MiB
|
DefaultBatchMaxSize = 100 << 20 // 20MiB
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type AddEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
pinNameOptionName = "pin-name"
|
||||||
quietOptionName = "quiet"
|
quietOptionName = "quiet"
|
||||||
quieterOptionName = "quieter"
|
quieterOptionName = "quieter"
|
||||||
silentOptionName = "silent"
|
silentOptionName = "silent"
|
||||||
@@ -184,6 +185,7 @@ See 'dag export' and 'dag import' for more information.
|
|||||||
cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. (experimental)"),
|
cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. (experimental)"),
|
||||||
cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. (experimental)").WithDefault(32),
|
cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. (experimental)").WithDefault(32),
|
||||||
cmds.BoolOption(pinOptionName, "Pin locally to protect added files from garbage collection.").WithDefault(true),
|
cmds.BoolOption(pinOptionName, "Pin locally to protect added files from garbage collection.").WithDefault(true),
|
||||||
|
cmds.StringOption(pinNameOptionName, "Name to use for the pin. Requires explicit value (e.g., --pin-name=myname)."),
|
||||||
cmds.StringOption(toFilesOptionName, "Add reference to Files API (MFS) at the provided path."),
|
cmds.StringOption(toFilesOptionName, "Add reference to Files API (MFS) at the provided path."),
|
||||||
cmds.BoolOption(preserveModeOptionName, "Apply existing POSIX permissions to created UnixFS entries. Disables raw-leaves. (experimental)"),
|
cmds.BoolOption(preserveModeOptionName, "Apply existing POSIX permissions to created UnixFS entries. Disables raw-leaves. (experimental)"),
|
||||||
cmds.BoolOption(preserveMtimeOptionName, "Apply existing POSIX modification time to created UnixFS entries. Disables raw-leaves. (experimental)"),
|
cmds.BoolOption(preserveMtimeOptionName, "Apply existing POSIX modification time to created UnixFS entries. Disables raw-leaves. (experimental)"),
|
||||||
@@ -230,6 +232,7 @@ See 'dag export' and 'dag import' for more information.
|
|||||||
silent, _ := req.Options[silentOptionName].(bool)
|
silent, _ := req.Options[silentOptionName].(bool)
|
||||||
chunker, _ := req.Options[chunkerOptionName].(string)
|
chunker, _ := req.Options[chunkerOptionName].(string)
|
||||||
dopin, _ := req.Options[pinOptionName].(bool)
|
dopin, _ := req.Options[pinOptionName].(bool)
|
||||||
|
pinName, pinNameSet := req.Options[pinNameOptionName].(string)
|
||||||
rawblks, rbset := req.Options[rawLeavesOptionName].(bool)
|
rawblks, rbset := req.Options[rawLeavesOptionName].(bool)
|
||||||
maxFileLinks, maxFileLinksSet := req.Options[maxFileLinksOptionName].(int)
|
maxFileLinks, maxFileLinksSet := req.Options[maxFileLinksOptionName].(int)
|
||||||
maxDirectoryLinks, maxDirectoryLinksSet := req.Options[maxDirectoryLinksOptionName].(int)
|
maxDirectoryLinks, maxDirectoryLinksSet := req.Options[maxDirectoryLinksOptionName].(int)
|
||||||
@@ -260,6 +263,8 @@ See 'dag export' and 'dag import' for more information.
|
|||||||
cidVer = int(cfg.Import.CidVersion.WithDefault(config.DefaultCidVersion))
|
cidVer = int(cfg.Import.CidVersion.WithDefault(config.DefaultCidVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pin names are only used when explicitly provided via --pin-name=value
|
||||||
|
|
||||||
if !rbset && cfg.Import.UnixFSRawLeaves != config.Default {
|
if !rbset && cfg.Import.UnixFSRawLeaves != config.Default {
|
||||||
rbset = true
|
rbset = true
|
||||||
rawblks = cfg.Import.UnixFSRawLeaves.WithDefault(config.DefaultUnixFSRawLeaves)
|
rawblks = cfg.Import.UnixFSRawLeaves.WithDefault(config.DefaultUnixFSRawLeaves)
|
||||||
@@ -296,7 +301,9 @@ See 'dag export' and 'dag import' for more information.
|
|||||||
if onlyHash && toFilesSet {
|
if onlyHash && toFilesSet {
|
||||||
return fmt.Errorf("%s and %s options are not compatible", onlyHashOptionName, toFilesOptionName)
|
return fmt.Errorf("%s and %s options are not compatible", onlyHashOptionName, toFilesOptionName)
|
||||||
}
|
}
|
||||||
|
if !dopin && pinNameSet {
|
||||||
|
return fmt.Errorf("%s option requires %s to be set", pinNameOptionName, pinOptionName)
|
||||||
|
}
|
||||||
if wrap && toFilesSet {
|
if wrap && toFilesSet {
|
||||||
return fmt.Errorf("%s and %s options are not compatible", wrapOptionName, toFilesOptionName)
|
return fmt.Errorf("%s and %s options are not compatible", wrapOptionName, toFilesOptionName)
|
||||||
}
|
}
|
||||||
@@ -326,7 +333,7 @@ See 'dag export' and 'dag import' for more information.
|
|||||||
|
|
||||||
options.Unixfs.Chunker(chunker),
|
options.Unixfs.Chunker(chunker),
|
||||||
|
|
||||||
options.Unixfs.Pin(dopin),
|
options.Unixfs.Pin(dopin, pinName),
|
||||||
options.Unixfs.HashOnly(onlyHash),
|
options.Unixfs.HashOnly(onlyHash),
|
||||||
options.Unixfs.FsCache(fscache),
|
options.Unixfs.FsCache(fscache),
|
||||||
options.Unixfs.Nocopy(nocopy),
|
options.Unixfs.Nocopy(nocopy),
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestPathUnixFSHAMTPartial(t *testing.T) {
|
|||||||
dir[strconv.Itoa(i)] = files.NewBytesFile([]byte(strconv.Itoa(i)))
|
dir[strconv.Itoa(i)] = files.NewBytesFile([]byte(strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := a.Unixfs().Add(ctx, files.NewMapDirectory(dir), options.Unixfs.Pin(false))
|
r, err := a.Unixfs().Add(ctx, files.NewMapDirectory(dir), options.Unixfs.Pin(false, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options
|
|||||||
attribute.Bool("maxhamtfanoutset", settings.MaxHAMTFanoutSet),
|
attribute.Bool("maxhamtfanoutset", settings.MaxHAMTFanoutSet),
|
||||||
attribute.Int("layout", int(settings.Layout)),
|
attribute.Int("layout", int(settings.Layout)),
|
||||||
attribute.Bool("pin", settings.Pin),
|
attribute.Bool("pin", settings.Pin),
|
||||||
|
attribute.String("pin-name", settings.PinName),
|
||||||
attribute.Bool("onlyhash", settings.OnlyHash),
|
attribute.Bool("onlyhash", settings.OnlyHash),
|
||||||
attribute.Bool("fscache", settings.FsCache),
|
attribute.Bool("fscache", settings.FsCache),
|
||||||
attribute.Bool("nocopy", settings.NoCopy),
|
attribute.Bool("nocopy", settings.NoCopy),
|
||||||
@@ -136,6 +137,9 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options
|
|||||||
fileAdder.Progress = settings.Progress
|
fileAdder.Progress = settings.Progress
|
||||||
}
|
}
|
||||||
fileAdder.Pin = settings.Pin && !settings.OnlyHash
|
fileAdder.Pin = settings.Pin && !settings.OnlyHash
|
||||||
|
if settings.Pin {
|
||||||
|
fileAdder.PinName = settings.PinName
|
||||||
|
}
|
||||||
fileAdder.Silent = settings.Silent
|
fileAdder.Silent = settings.Silent
|
||||||
fileAdder.RawLeaves = settings.RawLeaves
|
fileAdder.RawLeaves = settings.RawLeaves
|
||||||
if settings.MaxFileLinksSet {
|
if settings.MaxFileLinksSet {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type UnixfsAddSettings struct {
|
|||||||
Layout Layout
|
Layout Layout
|
||||||
|
|
||||||
Pin bool
|
Pin bool
|
||||||
|
PinName string
|
||||||
OnlyHash bool
|
OnlyHash bool
|
||||||
FsCache bool
|
FsCache bool
|
||||||
NoCopy bool
|
NoCopy bool
|
||||||
@@ -83,6 +84,7 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix,
|
|||||||
Layout: BalancedLayout,
|
Layout: BalancedLayout,
|
||||||
|
|
||||||
Pin: false,
|
Pin: false,
|
||||||
|
PinName: "",
|
||||||
OnlyHash: false,
|
OnlyHash: false,
|
||||||
FsCache: false,
|
FsCache: false,
|
||||||
NoCopy: false,
|
NoCopy: false,
|
||||||
@@ -280,9 +282,12 @@ func (unixfsOpts) Layout(layout Layout) UnixfsAddOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pin tells the adder to pin the file root recursively after adding
|
// Pin tells the adder to pin the file root recursively after adding
|
||||||
func (unixfsOpts) Pin(pin bool) UnixfsAddOption {
|
func (unixfsOpts) Pin(pin bool, pinName string) UnixfsAddOption {
|
||||||
return func(settings *UnixfsAddSettings) error {
|
return func(settings *UnixfsAddSettings) error {
|
||||||
settings.Pin = pin
|
settings.Pin = pin
|
||||||
|
if pin {
|
||||||
|
settings.PinName = pinName
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -539,7 +539,7 @@ func (tp *TestSuite) TestAddPinned(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true))
|
_, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ type Adder struct {
|
|||||||
Out chan<- interface{}
|
Out chan<- interface{}
|
||||||
Progress bool
|
Progress bool
|
||||||
Pin bool
|
Pin bool
|
||||||
|
PinName string
|
||||||
Trickle bool
|
Trickle bool
|
||||||
RawLeaves bool
|
RawLeaves bool
|
||||||
MaxLinks int
|
MaxLinks int
|
||||||
@@ -182,9 +183,10 @@ func (adder *Adder) curRootNode() (ipld.Node, error) {
|
|||||||
return root, err
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively pins the root node of Adder and
|
// PinRoot recursively pins the root node of Adder with an optional name and
|
||||||
// writes the pin state to the backing datastore.
|
// writes the pin state to the backing datastore. If name is empty, the pin
|
||||||
func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error {
|
// will be created without a name.
|
||||||
|
func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node, name string) error {
|
||||||
ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "PinRoot")
|
ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "PinRoot")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
@@ -207,7 +209,7 @@ func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error {
|
|||||||
adder.tempRoot = rnk
|
adder.tempRoot = rnk
|
||||||
}
|
}
|
||||||
|
|
||||||
err = adder.pinning.PinWithMode(ctx, rnk, pin.Recursive, "")
|
err = adder.pinning.PinWithMode(ctx, rnk, pin.Recursive, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -369,7 +371,12 @@ func (adder *Adder) AddAllAndPin(ctx context.Context, file files.Node) (ipld.Nod
|
|||||||
if !adder.Pin {
|
if !adder.Pin {
|
||||||
return nd, nil
|
return nd, nil
|
||||||
}
|
}
|
||||||
return nd, adder.PinRoot(ctx, nd)
|
|
||||||
|
if err := adder.PinRoot(ctx, nd, adder.PinName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adder *Adder) addFileNode(ctx context.Context, path string, file files.Node, toplevel bool) error {
|
func (adder *Adder) addFileNode(ctx context.Context, path string, file files.Node, toplevel bool) error {
|
||||||
@@ -530,7 +537,7 @@ func (adder *Adder) maybePauseForGC(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = adder.PinRoot(ctx, rn)
|
err = adder.PinRoot(ctx, rn, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ This release was brought to you by the [Interplanetary Shipyard](https://ipship
|
|||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [🔦 Highlights](#-highlights)
|
- [🔦 Highlights](#-highlights)
|
||||||
- [Clear provide queue when reprovide strategy changes](#clear-provide-queue-when-reprovide-strategy-changes)
|
- [Clear provide queue when reprovide strategy changes](#clear-provide-queue-when-reprovide-strategy-changes)
|
||||||
|
- [Named pins in `ipfs add` command](#-named-pins-in-ipfs-add-command)
|
||||||
- [Removed unnecessary dependencies](#removed-unnecessary-dependencies)
|
- [Removed unnecessary dependencies](#removed-unnecessary-dependencies)
|
||||||
- [📦️ Important dependency updates](#-important-dependency-updates)
|
- [📦️ Important dependency updates](#-important-dependency-updates)
|
||||||
- [📝 Changelog](#-changelog)
|
- [📝 Changelog](#-changelog)
|
||||||
@@ -31,6 +32,18 @@ A new `ipfs provide clear` command also allows manual queue clearing for debuggi
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Upgrading to Kubo 0.37 will automatically clear any preexisting provide queue. The next time `Reprovider.Interval` hits, `Reprovider.Strategy` will be executed on a clean slate, ensuring consistent behavior with your current configuration.
|
> Upgrading to Kubo 0.37 will automatically clear any preexisting provide queue. The next time `Reprovider.Interval` hits, `Reprovider.Strategy` will be executed on a clean slate, ensuring consistent behavior with your current configuration.
|
||||||
|
|
||||||
|
#### 🧷 Named pins in `ipfs add` command
|
||||||
|
|
||||||
|
Added `--pin-name` flag to `ipfs add` for assigning names to pins.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ ipfs add --pin-name=testname cat.jpg
|
||||||
|
added bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi cat.jpg
|
||||||
|
|
||||||
|
$ ipfs pin ls --names
|
||||||
|
bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi recursive testname
|
||||||
|
```
|
||||||
|
|
||||||
#### Removed unnecessary dependencies
|
#### Removed unnecessary dependencies
|
||||||
|
|
||||||
Kubo has been cleaned up by removing unnecessary dependencies and packages:
|
Kubo has been cleaned up by removing unnecessary dependencies and packages:
|
||||||
|
|||||||
@@ -108,6 +108,44 @@ func TestAdd(t *testing.T) {
|
|||||||
require.Equal(t, shortStringCidV1NoRawLeaves, cidStr)
|
require.Equal(t, shortStringCidV1NoRawLeaves, cidStr)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("ipfs add --pin-name=foo", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
node := harness.NewT(t).NewNode().Init().StartDaemon()
|
||||||
|
defer node.StopDaemon()
|
||||||
|
|
||||||
|
pinName := "test-pin-name"
|
||||||
|
cidStr := node.IPFSAddStr(shortString, "--pin-name", pinName)
|
||||||
|
require.Equal(t, shortStringCidV0, cidStr)
|
||||||
|
|
||||||
|
pinList := node.IPFS("pin", "ls", "--names").Stdout.Trimmed()
|
||||||
|
require.Contains(t, pinList, shortStringCidV0)
|
||||||
|
require.Contains(t, pinList, pinName)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ipfs add --pin=false --pin-name=foo returns an error", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
node := harness.NewT(t).NewNode().Init().StartDaemon()
|
||||||
|
defer node.StopDaemon()
|
||||||
|
|
||||||
|
// Use RunIPFS to allow for errors without assertion
|
||||||
|
result := node.RunIPFS("add", "--pin=false", "--pin-name=foo")
|
||||||
|
require.Error(t, result.Err, "Expected an error due to incompatible --pin and --pin-name")
|
||||||
|
require.Contains(t, result.Stderr.String(), "pin-name option requires pin to be set")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ipfs add --pin-name without value should fail", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
node := harness.NewT(t).NewNode().Init().StartDaemon()
|
||||||
|
defer node.StopDaemon()
|
||||||
|
|
||||||
|
// When --pin-name is passed without any value, it should fail
|
||||||
|
result := node.RunIPFS("add", "--pin-name")
|
||||||
|
require.Error(t, result.Err, "Expected an error when --pin-name has no value")
|
||||||
|
require.Contains(t, result.Stderr.String(), "missing argument for option \"pin-name\"")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("produced unixfs max file links: command flag --max-file-links overrides configuration in Import.UnixFSFileMaxLinks", func(t *testing.T) {
|
t.Run("produced unixfs max file links: command flag --max-file-links overrides configuration in Import.UnixFSFileMaxLinks", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user