feat: support optional pin names (#10261)
Henrique Dias committed
Jan 4, 2024 at 14:25 UTC
a8a6bbe929b0a821d854a20e3f5ed68f6cfe601c
22 files changed
+200
-60
client/rpc/pin.go
+7
-1
@@ -26,6 +26,7 @@ type pinRefKeyList struct {
26
type pin struct {
27
path path.ImmutablePath
28
typ string
29
+ name string
30
err error
31
}
32
@@ -37,6 +38,10 @@ func (p pin) Path() path.ImmutablePath {
38
return p.path
39
}
40
41
+func (p pin) Name() string {
42
+ return p.name
43
+}
44
+
45
func (p pin) Type() string {
46
return p.typ
47
}
@@ -53,6 +58,7 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
58
59
type pinLsObject struct {
60
Cid string
61
+ Name string
62
Type string
63
}
64
@@ -102,7 +108,7 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan i
108
}
109
110
select {
105
- case ch <- pin{typ: out.Type, path: path.FromCid(c)}:
111
+ case ch <- pin{typ: out.Type, name: out.Name, path: path.FromCid(c)}:
112
case <-ctx.Done():
113
return
114
}
cmd/ipfs/kubo/init.go
+1
-1
@@ -252,7 +252,7 @@ func initializeIpnsKeyspace(repoRoot string) error {
252
253
// pin recursively because this might already be pinned
254
// and doing a direct pin would throw an error in that case
255
- err = nd.Pinning.Pin(ctx, emptyDir, true)
255
+ err = nd.Pinning.Pin(ctx, emptyDir, true, "")
256
if err != nil {
257
return err
258
}
core/commands/dag/import.go
+1
-1
@@ -152,7 +152,7 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
152
ret.PinErrorMsg = err.Error()
153
} else if nd, err := blockDecoder.DecodeNode(req.Context, block); err != nil {
154
ret.PinErrorMsg = err.Error()
155
- } else if err := node.Pinning.Pin(req.Context, nd, true); err != nil {
155
+ } else if err := node.Pinning.Pin(req.Context, nd, true, ""); err != nil {
156
ret.PinErrorMsg = err.Error()
157
} else if err := node.Pinning.Flush(req.Context); err != nil {
158
ret.PinErrorMsg = err.Error()
core/commands/pin/pin.go
+52
-14
@@ -57,6 +57,28 @@ var addPinCmd = &cmds.Command{
57
Helptext: cmds.HelpText{
58
Tagline: "Pin objects to local storage.",
59
ShortDescription: "Stores an IPFS object(s) from a given path locally to disk.",
60
+ LongDescription: `
61
+Create a pin for the given object, protecting resolved CID from being garbage
62
+collected.
63
+
64
+An optional name can be provided, and read back via 'ipfs pin ls --names'.
65
+
66
+Be mindful of defaults:
67
+
68
+Default pin type is 'recursive' (entire DAG).
69
+Pass -r=false to create a direct pin for a single block.
70
+Use 'pin ls -t recursive' to only list roots of recursively pinned DAGs
71
+(significantly faster when many big DAGs are pinned recursively)
72
+
73
+Default pin name is empty. Pass '--name' to 'pin add' to set one
74
+and use 'pin ls --names' to see it.
75
+Pin add is idempotent: pinning CID which is already pinned won't change
76
+the name, value passed with '--name' with the original pin is preserved.
77
+To rename pin, use 'pin rm' and 'pin add --name'.
78
+
79
+If daemon is running, any missing blocks will be retrieved from the network.
80
+It may take some time. Pass '--progress' to track the progress.
81
+`,
82
},
83
84
Arguments: []cmds.Argument{
@@ -64,6 +86,7 @@ var addPinCmd = &cmds.Command{
86
},
87
Options: []cmds.Option{
88
cmds.BoolOption(pinRecursiveOptionName, "r", "Recursively pin the object linked to by the specified object(s).").WithDefault(true),
89
+ cmds.StringOption(pinNameOptionName, "n", "An optional name for created pin(s)."),
90
cmds.BoolOption(pinProgressOptionName, "Show progress"),
91
},
92
Type: AddPinOutput{},
@@ -75,6 +98,7 @@ var addPinCmd = &cmds.Command{
98
99
// set recursive flag
100
recursive, _ := req.Options[pinRecursiveOptionName].(bool)
101
+ name, _ := req.Options[pinNameOptionName].(string)
102
showProgress, _ := req.Options[pinProgressOptionName].(bool)
103
104
if err := req.ParseBodyArgs(); err != nil {
@@ -87,7 +111,7 @@ var addPinCmd = &cmds.Command{
111
}
112
113
if !showProgress {
90
- added, err := pinAddMany(req.Context, api, enc, req.Arguments, recursive)
114
+ added, err := pinAddMany(req.Context, api, enc, req.Arguments, recursive, name)
115
if err != nil {
116
return err
117
}
@@ -105,7 +129,7 @@ var addPinCmd = &cmds.Command{
129
130
ch := make(chan pinResult, 1)
131
go func() {
108
- added, err := pinAddMany(ctx, api, enc, req.Arguments, recursive)
132
+ added, err := pinAddMany(ctx, api, enc, req.Arguments, recursive, name)
133
ch <- pinResult{pins: added, err: err}
134
}()
135
@@ -181,7 +205,7 @@ var addPinCmd = &cmds.Command{
205
},
206
}
207
184
-func pinAddMany(ctx context.Context, api coreiface.CoreAPI, enc cidenc.Encoder, paths []string, recursive bool) ([]string, error) {
208
+func pinAddMany(ctx context.Context, api coreiface.CoreAPI, enc cidenc.Encoder, paths []string, recursive bool, name string) ([]string, error) {
209
added := make([]string, len(paths))
210
for i, b := range paths {
211
p, err := cmdutils.PathOrCidPath(b)
@@ -194,7 +218,7 @@ func pinAddMany(ctx context.Context, api coreiface.CoreAPI, enc cidenc.Encoder,
218
return nil, err
219
}
220
197
- if err := api.Pin().Add(ctx, rp, options.Pin.Recursive(recursive)); err != nil {
221
+ if err := api.Pin().Add(ctx, rp, options.Pin.Recursive(recursive), options.Pin.Name(name)); err != nil {
222
return nil, err
223
}
224
added[i] = enc.Encode(rp.RootCid())
@@ -281,6 +305,7 @@ const (
305
pinTypeOptionName = "type"
306
pinQuietOptionName = "quiet"
307
pinStreamOptionName = "stream"
308
+ pinNamesOptionName = "names"
309
)
310
311
var listPinCmd = &cmds.Command{
@@ -294,6 +319,7 @@ respectively.
319
`,
320
LongDescription: `
321
Returns a list of objects that are pinned locally.
322
+
323
By default, all pinned objects are returned, but the '--type' flag or
324
arguments can restrict that to a specific pin type or to some specific objects
325
respectively.
@@ -302,10 +328,13 @@ Use --type=<type> to specify the type of pinned keys to list.
328
Valid values are:
329
* "direct": pin that specific object.
330
* "recursive": pin that specific object, and indirectly pin all its
305
- descendants
331
+ descendants
332
* "indirect": pinned indirectly by an ancestor (like a refcount)
333
* "all"
334
335
+By default, pin names are not included (returned as empty).
336
+Pass '--names' flag to return pin names (set with '--name' from 'pin add').
337
+
338
With arguments, the command fails if any of the arguments is not a pinned
339
object. And if --type=<type> is additionally used, the command will also fail
340
if any of the arguments is not of the specified type.
@@ -334,6 +363,7 @@ Example:
363
cmds.StringOption(pinTypeOptionName, "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\".").WithDefault("all"),
364
cmds.BoolOption(pinQuietOptionName, "q", "Write just hashes of objects."),
365
cmds.BoolOption(pinStreamOptionName, "s", "Enable streaming of pins as they are discovered."),
366
+ cmds.BoolOption(pinNamesOptionName, "n", "Enable displaying pin names (slower)."),
367
},
368
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
369
api, err := cmdenv.GetApi(env, req)
@@ -343,6 +373,7 @@ Example:
373
374
typeStr, _ := req.Options[pinTypeOptionName].(string)
375
stream, _ := req.Options[pinStreamOptionName].(bool)
376
+ displayNames, _ := req.Options[pinNamesOptionName].(bool)
377
378
switch typeStr {
379
case "all", "direct", "indirect", "recursive":
@@ -356,7 +387,7 @@ Example:
387
lgcList := map[string]PinLsType{}
388
if !stream {
389
emit = func(v PinLsOutputWrapper) error {
359
- lgcList[v.PinLsObject.Cid] = PinLsType{Type: v.PinLsObject.Type}
390
+ lgcList[v.PinLsObject.Cid] = PinLsType{Type: v.PinLsObject.Type, Name: v.PinLsObject.Name}
391
return nil
392
}
393
} else {
@@ -368,7 +399,7 @@ Example:
399
if len(req.Arguments) > 0 {
400
err = pinLsKeys(req, typeStr, api, emit)
401
} else {
371
- err = pinLsAll(req, typeStr, api, emit)
402
+ err = pinLsAll(req, typeStr, displayNames, api, emit)
403
}
404
if err != nil {
405
return err
@@ -402,8 +433,10 @@ Example:
433
if stream {
434
if quiet {
435
fmt.Fprintf(w, "%s\n", out.PinLsObject.Cid)
405
- } else {
436
+ } else if out.PinLsObject.Name == "" {
437
fmt.Fprintf(w, "%s %s\n", out.PinLsObject.Cid, out.PinLsObject.Type)
438
+ } else {
439
+ fmt.Fprintf(w, "%s %s %s\n", out.PinLsObject.Cid, out.PinLsObject.Type, out.PinLsObject.Name)
440
}
441
return nil
442
}
@@ -411,8 +444,10 @@ Example:
444
for k, v := range out.PinLsList.Keys {
445
if quiet {
446
fmt.Fprintf(w, "%s\n", k)
414
- } else {
447
+ } else if v.Name == "" {
448
fmt.Fprintf(w, "%s %s\n", k, v.Type)
449
+ } else {
450
+ fmt.Fprintf(w, "%s %s %s\n", k, v.Type, v.Name)
451
}
452
}
453
@@ -437,11 +472,13 @@ type PinLsList struct {
472
// PinLsType contains the type of a pin
473
type PinLsType struct {
474
Type string
475
+ Name string
476
}
477
478
// PinLsObject contains the description of a pin
479
type PinLsObject struct {
480
Cid string `json:",omitempty"`
481
+ Name string `json:",omitempty"`
482
Type string `json:",omitempty"`
483
}
484
@@ -502,7 +539,7 @@ func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fu
539
return nil
540
}
541
505
-func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit func(value PinLsOutputWrapper) error) error {
542
+func pinLsAll(req *cmds.Request, typeStr string, detailed bool, api coreiface.CoreAPI, emit func(value PinLsOutputWrapper) error) error {
543
enc, err := cmdenv.GetCidEncoder(req)
544
if err != nil {
545
return err
@@ -520,7 +557,7 @@ func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fun
557
panic("unhandled pin type")
558
}
559
523
- pins, err := api.Pin().Ls(req.Context, opt)
560
+ pins, err := api.Pin().Ls(req.Context, opt, options.Pin.Ls.Detailed(detailed))
561
if err != nil {
562
return err
563
}
@@ -532,6 +569,7 @@ func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fun
569
err = emit(PinLsOutputWrapper{
570
PinLsObject: PinLsObject{
571
Type: p.Type(),
572
+ Name: p.Name(),
573
Cid: enc.Encode(p.Path().RootCid()),
574
},
575
})
@@ -748,15 +786,15 @@ func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc ci
786
out := make(chan any)
787
go func() {
788
defer close(out)
751
- for p := range n.Pinning.RecursiveKeys(ctx) {
789
+ for p := range n.Pinning.RecursiveKeys(ctx, false) {
790
if p.Err != nil {
791
out <- PinVerifyRes{Err: p.Err.Error()}
792
return
793
}
756
- pinStatus := checkPin(p.C)
794
+ pinStatus := checkPin(p.Pin.Key)
795
if !pinStatus.Ok || opts.includeOk {
796
select {
759
- case out <- PinVerifyRes{Cid: enc.Encode(p.C), PinStatus: pinStatus}:
797
+ case out <- PinVerifyRes{Cid: enc.Encode(p.Pin.Key), PinStatus: pinStatus}:
798
case <-ctx.Done():
799
return
800
}
core/coreapi/block.go
+1
-1
@@ -60,7 +60,7 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc
60
}
61
62
if settings.Pin {
63
- if err = api.pinning.PinWithMode(ctx, b.Cid(), pin.Recursive); err != nil {
63
+ if err = api.pinning.PinWithMode(ctx, b.Cid(), pin.Recursive, ""); err != nil {
64
return nil, err
65
}
66
if err := api.pinning.Flush(ctx); err != nil {
core/coreapi/dag.go
+2
-2
@@ -30,7 +30,7 @@ func (adder *pinningAdder) Add(ctx context.Context, nd ipld.Node) error {
30
return err
31
}
32
33
- if err := adder.pinning.PinWithMode(ctx, nd.Cid(), pin.Recursive); err != nil {
33
+ if err := adder.pinning.PinWithMode(ctx, nd.Cid(), pin.Recursive, ""); err != nil {
34
return err
35
}
36
@@ -51,7 +51,7 @@ func (adder *pinningAdder) AddMany(ctx context.Context, nds []ipld.Node) error {
51
for _, nd := range nds {
52
c := nd.Cid()
53
if cids.Visit(c) {
54
- if err := adder.pinning.PinWithMode(ctx, c, pin.Recursive); err != nil {
54
+ if err := adder.pinning.PinWithMode(ctx, c, pin.Recursive, ""); err != nil {
55
return err
56
}
57
}
core/coreapi/object.go
+1
-1
@@ -133,7 +133,7 @@ func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Obj
133
}
134
135
if options.Pin {
136
- if err := api.pinning.PinWithMode(ctx, dagnode.Cid(), pin.Recursive); err != nil {
136
+ if err := api.pinning.PinWithMode(ctx, dagnode.Cid(), pin.Recursive, ""); err != nil {
137
return path.ImmutablePath{}, err
138
}
139
core/coreapi/pin.go
+23
-17
@@ -38,7 +38,7 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
38
39
defer api.blockstore.PinLock(ctx).Unlock(ctx)
40
41
- err = api.pinning.Pin(ctx, dagNode, settings.Recursive)
41
+ err = api.pinning.Pin(ctx, dagNode, settings.Recursive, settings.Name)
42
if err != nil {
43
return fmt.Errorf("pin: %s", err)
44
}
@@ -67,7 +67,7 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan c
67
return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
68
}
69
70
- return api.pinLsAll(ctx, settings.Type), nil
70
+ return api.pinLsAll(ctx, settings.Type, settings.Detailed), nil
71
}
72
73
func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) {
@@ -231,12 +231,12 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
231
out := make(chan coreiface.PinStatus)
232
go func() {
233
defer close(out)
234
- for p := range api.pinning.RecursiveKeys(ctx) {
234
+ for p := range api.pinning.RecursiveKeys(ctx, false) {
235
var res *pinStatus
236
if p.Err != nil {
237
res = &pinStatus{err: p.Err}
238
} else {
239
- res = checkPin(p.C)
239
+ res = checkPin(p.Pin.Key)
240
}
241
select {
242
case <-ctx.Done():
@@ -252,6 +252,7 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
252
type pinInfo struct {
253
pinType string
254
path path.ImmutablePath
255
+ name string
256
err error
257
}
258
@@ -263,6 +264,10 @@ func (p *pinInfo) Type() string {
264
return p.pinType
265
}
266
267
+func (p *pinInfo) Name() string {
268
+ return p.name
269
+}
270
+
271
func (p *pinInfo) Err() error {
272
return p.err
273
}
@@ -271,16 +276,17 @@ func (p *pinInfo) Err() error {
276
//
277
// The caller must keep reading results until the channel is closed to prevent
278
// leaking the goroutine that is fetching pins.
274
-func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreiface.Pin {
279
+func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string, detailed bool) <-chan coreiface.Pin {
280
out := make(chan coreiface.Pin, 1)
281
282
emittedSet := cid.NewSet()
283
279
- AddToResultKeys := func(c cid.Cid, typeStr string) error {
284
+ AddToResultKeys := func(c cid.Cid, name, typeStr string) error {
285
if emittedSet.Visit(c) {
286
select {
287
case out <- &pinInfo{
288
pinType: typeStr,
289
+ name: name,
290
path: path.FromCid(c),
291
}:
292
case <-ctx.Done():
@@ -296,25 +302,25 @@ func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreifac
302
var rkeys []cid.Cid
303
var err error
304
if typeStr == "recursive" || typeStr == "all" {
299
- for streamedCid := range api.pinning.RecursiveKeys(ctx) {
305
+ for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
306
if streamedCid.Err != nil {
307
out <- &pinInfo{err: streamedCid.Err}
308
return
309
}
304
- if err = AddToResultKeys(streamedCid.C, "recursive"); err != nil {
310
+ if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "recursive"); err != nil {
311
out <- &pinInfo{err: err}
312
return
313
}
308
- rkeys = append(rkeys, streamedCid.C)
314
+ rkeys = append(rkeys, streamedCid.Pin.Key)
315
}
316
}
317
if typeStr == "direct" || typeStr == "all" {
312
- for streamedCid := range api.pinning.DirectKeys(ctx) {
318
+ for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
319
if streamedCid.Err != nil {
320
out <- &pinInfo{err: streamedCid.Err}
321
return
322
}
317
- if err = AddToResultKeys(streamedCid.C, "direct"); err != nil {
323
+ if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "direct"); err != nil {
324
out <- &pinInfo{err: err}
325
return
326
}
@@ -324,21 +330,21 @@ func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreifac
330
// We need to first visit the direct pins that have priority
331
// without emitting them
332
327
- for streamedCid := range api.pinning.DirectKeys(ctx) {
333
+ for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
334
if streamedCid.Err != nil {
335
out <- &pinInfo{err: streamedCid.Err}
336
return
337
}
332
- emittedSet.Add(streamedCid.C)
338
+ emittedSet.Add(streamedCid.Pin.Key)
339
}
340
335
- for streamedCid := range api.pinning.RecursiveKeys(ctx) {
341
+ for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
342
if streamedCid.Err != nil {
343
out <- &pinInfo{err: streamedCid.Err}
344
return
345
}
340
- emittedSet.Add(streamedCid.C)
341
- rkeys = append(rkeys, streamedCid.C)
346
+ emittedSet.Add(streamedCid.Pin.Key)
347
+ rkeys = append(rkeys, streamedCid.Pin.Key)
348
}
349
}
350
if typeStr == "indirect" || typeStr == "all" {
@@ -353,7 +359,7 @@ func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreifac
359
if emittedSet.Has(c) {
360
return true // skipped
361
}
356
- err := AddToResultKeys(c, "indirect")
362
+ err := AddToResultKeys(c, "", "indirect")
363
if err != nil {
364
out <- &pinInfo{err: err}
365
return false
core/coreiface/options/pin.go
+20
-1
@@ -5,11 +5,13 @@ import "fmt"
5
// PinAddSettings represent the settings for PinAPI.Add
6
type PinAddSettings struct {
7
Recursive bool
8
+ Name string
9
}
10
11
// PinLsSettings represent the settings for PinAPI.Ls
12
type PinLsSettings struct {
12
- Type string
13
+ Type string
14
+ Detailed bool
15
}
16
17
// PinIsPinnedSettings represent the settings for PinAPI.IsPinned
@@ -194,6 +196,15 @@ func (pinLsOpts) pinType(t string) PinLsOption {
196
}
197
}
198
199
+// Detailed is an option for [Pin.Ls] which sets whether or not to return
200
+// detailed information, such as pin names and modes.
201
+func (pinLsOpts) Detailed(detailed bool) PinLsOption {
202
+ return func(settings *PinLsSettings) error {
203
+ settings.Detailed = detailed
204
+ return nil
205
+ }
206
+}
207
+
208
type pinIsPinnedOpts struct{}
209
210
// All is an option for Pin.IsPinned which will make it search in all type of pins.
@@ -263,6 +274,14 @@ func (pinOpts) Recursive(recursive bool) PinAddOption {
274
}
275
}
276
277
+// Name is an option for Pin.Add which specifies an optional name to add to the pin.
278
+func (pinOpts) Name(name string) PinAddOption {
279
+ return func(settings *PinAddSettings) error {
280
+ settings.Name = name
281
+ return nil
282
+ }
283
+}
284
+
285
// RmRecursive is an option for Pin.Rm which specifies whether to recursively
286
// unpin the object linked to by the specified object(s). This does not remove
287
// indirect pins referenced by other recursive pins.
core/coreiface/pin.go
+3
@@ -13,6 +13,9 @@ type Pin interface {
13
// Path to the pinned object
14
Path() path.ImmutablePath
15
16
+ // Name is the name of the pin.
17
+ Name() string
18
+
19
// Type of the pin
20
Type() string
21
core/coreunix/add.go
+1
-1
@@ -186,7 +186,7 @@ func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error {
186
adder.tempRoot = rnk
187
}
188
189
- err = adder.pinning.PinWithMode(ctx, rnk, pin.Recursive)
189
+ err = adder.pinning.PinWithMode(ctx, rnk, pin.Recursive, "")
190
if err != nil {
191
return err
192
}
docs/changelogs/v0.26.md
+5
@@ -7,6 +7,7 @@
7
- [Overview](#overview)
8
- [🔦 Highlights](#-highlights)
9
- [Several deprecated commands have been removed](#several-deprecated-commands-have-been-removed)
10
+ - [Support optional pin names](#support-optional-pin-names)
11
- [📝 Changelog](#-changelog)
12
- [👨👩👧👦 Contributors](#-contributors)
13
@@ -30,6 +31,10 @@ Several deprecated commands have been removed:
31
- `ipfs dns` deprecated in [April 2022, Kubo 0.13](https://github.com/ipfs/kubo/commit/76ae33a9f3f9abd166d1f6f23d6a8a0511510e3c), use `ipfs resolve /ipns/{name}` instead.
32
- `ipfs tar` deprecated [April 2022, Kubo 0.13](https://github.com/ipfs/kubo/pull/8849)
33
34
+#### Support optional pin names
35
+
36
+You can now add a name to a pin when pinning a CID. To do so, use `ipfs pin add --name "Some Name" bafy...`. You can list your pins, including their names, with `ipfs pin ls --names`.
37
+
38
### 📝 Changelog
39
40
- Export a `kubo.Start` function so users can programmatically start Kubo from within a go program.
docs/examples/kubo-as-a-library/go.mod
+1
-1
@@ -7,7 +7,7 @@ go 1.20
7
replace github.com/ipfs/kubo => ./../../..
8
9
require (
10
- github.com/ipfs/boxo v0.16.0
10
+ github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d
11
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
12
github.com/libp2p/go-libp2p v0.32.2
13
github.com/multiformats/go-multiaddr v0.12.0
docs/examples/kubo-as-a-library/go.sum
+2
-2
@@ -303,8 +303,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
303
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
304
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
305
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
306
-github.com/ipfs/boxo v0.16.0 h1:A9dUmef5a+mEFki6kbyG7el5gl65CiUBzrDeZxzTWKY=
307
-github.com/ipfs/boxo v0.16.0/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
306
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d h1:7tDzalCLHYr4tzrjNrfqZvIki2MEBSrpLBdc0bssDZk=
307
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
308
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
309
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
310
github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
fuse/ipns/common.go
+1
-1
@@ -18,7 +18,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
18
19
emptyDir := ft.EmptyDirNode()
20
21
- err := n.Pinning.Pin(ctx, emptyDir, false)
21
+ err := n.Pinning.Pin(ctx, emptyDir, false, "")
22
if err != nil {
23
return err
24
}
gc/gc.go
+8
-8
@@ -154,7 +154,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn
154
// Descendants recursively finds all the descendants of the given roots and
155
// adds them to the given cid.Set, using the provided dag.GetLinks function
156
// to walk the tree.
157
-func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots <-chan pin.StreamedCid) error {
157
+func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots <-chan pin.StreamedPin) error {
158
verifyGetLinks := func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) {
159
err := verifcid.ValidateCid(verifcid.DefaultAllowlist, c)
160
if err != nil {
@@ -188,7 +188,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots
188
}
189
190
// Walk recursively walks the dag and adds the keys to the given set
191
- err := dag.Walk(ctx, verifyGetLinks, wrapper.C, func(k cid.Cid) bool {
191
+ err := dag.Walk(ctx, verifyGetLinks, wrapper.Pin.Key, func(k cid.Cid) bool {
192
return set.Visit(toCidV1(k))
193
}, dag.Concurrent())
194
if err != nil {
@@ -226,7 +226,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo
226
}
227
return links, nil
228
}
229
- rkeys := pn.RecursiveKeys(ctx)
229
+ rkeys := pn.RecursiveKeys(ctx, false)
230
err := Descendants(ctx, getLinks, gcs, rkeys)
231
if err != nil {
232
errors = true
@@ -249,14 +249,14 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo
249
}
250
return links, nil
251
}
252
- bestEffortRootsChan := make(chan pin.StreamedCid)
252
+ bestEffortRootsChan := make(chan pin.StreamedPin)
253
go func() {
254
defer close(bestEffortRootsChan)
255
for _, root := range bestEffortRoots {
256
select {
257
case <-ctx.Done():
258
return
259
- case bestEffortRootsChan <- pin.StreamedCid{C: root}:
259
+ case bestEffortRootsChan <- pin.StreamedPin{Pin: pin.Pinned{Key: root}}:
260
}
261
}
262
}()
@@ -270,15 +270,15 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo
270
}
271
}
272
273
- dkeys := pn.DirectKeys(ctx)
273
+ dkeys := pn.DirectKeys(ctx, false)
274
for k := range dkeys {
275
if k.Err != nil {
276
return nil, k.Err
277
}
278
- gcs.Add(toCidV1(k.C))
278
+ gcs.Add(toCidV1(k.Pin.Key))
279
}
280
281
- ikeys := pn.InternalPins(ctx)
281
+ ikeys := pn.InternalPins(ctx, false)
282
err = Descendants(ctx, getLinks, gcs, ikeys)
283
if err != nil {
284
errors = true
gc/gc_test.go
+2
-2
@@ -38,14 +38,14 @@ func TestGC(t *testing.T) {
38
// direct
39
root, _, err := daggen.MakeDagNode(dserv.Add, 0, 1)
40
require.NoError(t, err)
41
- err = pinner.PinWithMode(ctx, root, pin.Direct)
41
+ err = pinner.PinWithMode(ctx, root, pin.Direct, "")
42
require.NoError(t, err)
43
expectedKept = append(expectedKept, root.Hash())
44
45
// recursive
46
root, allCids, err := daggen.MakeDagNode(dserv.Add, 5, 2)
47
require.NoError(t, err)
48
- err = pinner.PinWithMode(ctx, root, pin.Recursive)
48
+ err = pinner.PinWithMode(ctx, root, pin.Recursive, "")
49
require.NoError(t, err)
50
expectedKept = append(expectedKept, toMHs(allCids)...)
51
}
go.mod
+1
-1
@@ -17,7 +17,7 @@ require (
17
github.com/hashicorp/go-multierror v1.1.1
18
github.com/ipfs-shipyard/nopfs v0.0.12-0.20231027223058-cde3b5ba964c
19
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c
20
- github.com/ipfs/boxo v0.16.0
20
+ github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d
21
github.com/ipfs/go-block-format v0.2.0
22
github.com/ipfs/go-cid v0.4.1
23
github.com/ipfs/go-cidutil v0.1.0
go.sum
+2
-2
@@ -337,8 +337,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
337
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
338
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
339
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
340
-github.com/ipfs/boxo v0.16.0 h1:A9dUmef5a+mEFki6kbyG7el5gl65CiUBzrDeZxzTWKY=
341
-github.com/ipfs/boxo v0.16.0/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
340
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d h1:7tDzalCLHYr4tzrjNrfqZvIki2MEBSrpLBdc0bssDZk=
341
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
342
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
343
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
344
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
test/cli/pins_test.go
+63
@@ -209,4 +209,67 @@ func TestPins(t *testing.T) {
209
testPins(t, testPinsArgs{runDaemon: true, baseArg: "--cid-base=base32"})
210
testPins(t, testPinsArgs{runDaemon: true, lsArg: "--stream", baseArg: "--cid-base=base32"})
211
})
212
+
213
+ t.Run("test pinning with names cli text output", func(t *testing.T) {
214
+ t.Parallel()
215
+
216
+ node := harness.NewT(t).NewNode().Init()
217
+ cidAStr := node.IPFSAddStr(RandomStr(1000), "--pin=false")
218
+ cidBStr := node.IPFSAddStr(RandomStr(1000), "--pin=false")
219
+
220
+ _ = node.IPFS("pin", "add", "--name", "testPin", cidAStr)
221
+
222
+ outARegular := cidAStr + " recursive"
223
+ outADetailed := outARegular + " testPin"
224
+ outBRegular := cidBStr + " recursive"
225
+ outBDetailed := outBRegular + " testPin"
226
+
227
+ pinLs := func(args ...string) []string {
228
+ return strings.Split(node.IPFS(StrCat("pin", "ls", args)...).Stdout.Trimmed(), "\n")
229
+ }
230
+
231
+ lsOut := pinLs("-t=recursive")
232
+ require.Contains(t, lsOut, outARegular)
233
+ require.NotContains(t, lsOut, outADetailed)
234
+
235
+ lsOut = pinLs("-t=recursive", "--names")
236
+ require.Contains(t, lsOut, outADetailed)
237
+ require.NotContains(t, lsOut, outARegular)
238
+
239
+ _ = node.IPFS("pin", "update", cidAStr, cidBStr)
240
+ lsOut = pinLs("-t=recursive", "--names")
241
+ require.Contains(t, lsOut, outBDetailed)
242
+ require.NotContains(t, lsOut, outADetailed)
243
+ })
244
+
245
+ // JSON that is also the wire format of /api/v0
246
+ t.Run("test pinning with names json output", func(t *testing.T) {
247
+ t.Parallel()
248
+
249
+ node := harness.NewT(t).NewNode().Init()
250
+ cidAStr := node.IPFSAddStr(RandomStr(1000), "--pin=false")
251
+ cidBStr := node.IPFSAddStr(RandomStr(1000), "--pin=false")
252
+
253
+ _ = node.IPFS("pin", "add", "--name", "testPinJson", cidAStr)
254
+
255
+ outARegular := `"` + cidAStr + `":{"Type":"recursive"`
256
+ outADetailed := outARegular + `,"Name":"testPinJson"`
257
+ outBRegular := `"` + cidBStr + `":{"Type":"recursive"`
258
+ outBDetailed := outBRegular + `,"Name":"testPinJson"`
259
+
260
+ pinLs := func(args ...string) string {
261
+ return node.IPFS(StrCat("pin", "ls", "--enc=json", args)...).Stdout.Trimmed()
262
+ }
263
+
264
+ lsOut := pinLs("-t=recursive")
265
+ require.Contains(t, lsOut, outARegular)
266
+ require.NotContains(t, lsOut, outADetailed)
267
+
268
+ lsOut = pinLs("-t=recursive", "--names")
269
+ require.Contains(t, lsOut, outADetailed)
270
+
271
+ _ = node.IPFS("pin", "update", cidAStr, cidBStr)
272
+ lsOut = pinLs("-t=recursive", "--names")
273
+ require.Contains(t, lsOut, outBDetailed)
274
+ })
275
}
test/dependencies/go.mod
+1
-1
@@ -103,7 +103,7 @@ require (
103
github.com/hexops/gotextdiff v1.0.3 // indirect
104
github.com/inconshreveable/mousetrap v1.1.0 // indirect
105
github.com/ipfs/bbloom v0.0.4 // indirect
106
- github.com/ipfs/boxo v0.16.0 // indirect
106
+ github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d // indirect
107
github.com/ipfs/go-block-format v0.2.0 // indirect
108
github.com/ipfs/go-cid v0.4.1 // indirect
109
github.com/ipfs/go-datastore v0.6.0 // indirect
test/dependencies/go.sum
+2
-2
@@ -342,8 +342,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
342
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
343
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
344
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
345
-github.com/ipfs/boxo v0.16.0 h1:A9dUmef5a+mEFki6kbyG7el5gl65CiUBzrDeZxzTWKY=
346
-github.com/ipfs/boxo v0.16.0/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
345
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d h1:7tDzalCLHYr4tzrjNrfqZvIki2MEBSrpLBdc0bssDZk=
346
+github.com/ipfs/boxo v0.16.1-0.20240104124825-38fb74f76b0d/go.mod h1:jAgpNQn7T7BnibUeReXcKU9Ha1xmYNyOlwVEl193ow0=
347
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
348
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
349
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=