98286406 Andy McCall 1 month ago ↶
1
2
3
use super :: * ;
4
7aa2dc93 Andy McCall 1 month ago ↶
5
6
# [ derive ( Clone, Debug, Default ) ]
7
pub struct PushReport {
8
pub results : Vec< RefResult> ,
9
10
pub updated : Vec< RefUpdated> ,
11
12
pub imported : Vec< ChangeHash> ,
13
}
14
15
# [ derive ( Clone, Debug ) ]
16
pub struct RefUpdated {
17
pub name : String,
18
pub old_state : ObjectRef,
19
pub new_state : ObjectRef,
20
pub heads : Vec< ChangeHash> ,
21
}
22
83e0677b Andy McCall 1 month ago ↶
23
24
# [ derive ( Debug, Clone, PartialEq, Eq, Default ) ]
25
pub struct FetchReport {
26
27
pub refs : Vec< ( String, Vec< ChangeHash> , usize ) > ,
132b884b Andy McCall 1 month ago ↶
28
29
pub new_tags : usize ,
83e0677b Andy McCall 1 month ago ↶
30
31
32
pub unverified : bool ,
33
}
34
98286406 Andy McCall 1 month ago ↶
35
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
36
pub struct PullReport {
14edbaaf Andy McCall 1 month ago ↶
37
98286406 Andy McCall 1 month ago ↶
38
pub heads : Vec< ChangeHash> ,
14edbaaf Andy McCall 1 month ago ↶
39
98286406 Andy McCall 1 month ago ↶
40
pub changed : bool ,
41
pub checked_out : bool ,
14edbaaf Andy McCall 1 month ago ↶
42
43
44
pub others : Vec< String> ,
98286406 Andy McCall 1 month ago ↶
45
}
46
cb8b8082 Andy McCall 26 days ago ↶
47
48
49
pub const OBJECTS_RESULT : & str = " objects" ;
50
98286406 Andy McCall 1 month ago ↶
51
impl Repo {
52
53
54
fn ref_attest_path ( & self , bookmark : & str ) -> PathBuf {
54cd08a1 Andy McCall 1 month ago ↶
55
self .oxy.join ( " attest" ) .join ( name_file ( bookmark) )
98286406 Andy McCall 1 month ago ↶
56
}
57
58
59
pub fn latest_ref_attest ( & self , bookmark : & str ) -> Result< Option< ( ObjectRef, SignedAttest) > > {
60
match fs:: read_to_string( self .ref_attest_path ( bookmark) ) {
61
Ok ( h) => {
75167672 Andy McCall 1 month ago
62
let r = ObjectRef:: from_hex( h.trim ( ) )
63
.ok_or_else ( | | RepoError:: Msg( " corrupt attestation pointer" .into ( ) ) ) ? ;
64
Ok ( Some ( (
65
r,
66
SignedAttest {
67
attest: self .store.get_attest ( & r) ? ,
68
sigs: self .store.signatures ( & r) ? ,
69
} ,
70
) ) )
98286406 Andy McCall 1 month ago ↶
71
}
72
Err ( e) if e.kind ( ) = = std:: io:: ErrorKind:: NotFound => Ok ( None ) ,
73
Err ( e) => Err ( e.into ( ) ) ,
74
}
75
}
76
9f9e72b9 Andy McCall 1 month ago ↶
77
pub ( crate ) fn set_ref_attest ( & self , bookmark : & str , r : & ObjectRef) -> Result< ( ) > {
98286406 Andy McCall 1 month ago ↶
78
fs:: create_dir_all( self .oxy.join ( " attest" ) ) ? ;
79
write_atomic ( & self .ref_attest_path ( bookmark) , r.to_hex ( ) .as_bytes ( ) )
80
}
81
83e0677b Andy McCall 1 month ago ↶
82
pub fn remote_refs ( & self ) -> Result< RefsResponse> {
98286406 Andy McCall 1 month ago ↶
83
let view = self .view ( ) ? ;
84
let mut out = Vec :: new( ) ;
85
for ( name, state) in & view.bookmarks {
86
let latest = self .latest_ref_attest ( name) ? .map ( | ( _, a ) | a ) ;
87
let last_serial = latest.as_ref ( ) .and_then ( | a | match a.attest.body {
88
AttestBody:: Ref { serial, .. } => Some ( serial) ,
89
_ => None ,
90
} ) ;
91
let attestation = latest.filter ( | a | matches! ( & a.attest.body, AttestBody:: Ref { new_state, .. } if new_state = = state) ) ;
f53cbe7a Andy McCall 18 days ago ↶
92
let accepted = match & attestation {
93
Some ( a) => self .acceptance_of ( & a.attest.hash ( ) ) ? ,
94
None => None ,
95
} ;
75167672 Andy McCall 1 month ago
96
out.push ( RemoteRef {
97
name: name.clone ( ) ,
98
state: * state,
99
heads: self .store.get_state ( state) ? .heads,
100
attestation,
101
last_serial,
f53cbe7a Andy McCall 18 days ago ↶
102
accepted,
75167672 Andy McCall 1 month ago
103
} ) ;
98286406 Andy McCall 1 month ago ↶
104
}
28fc23d7 Andy McCall 1 month ago ↶
105
let policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp ) ;
75167672 Andy McCall 1 month ago
106
let history = self
107
.policy_history ( ) ?
108
.into_iter ( )
109
.filter ( | sp | {
110
policy
111
.as_ref ( )
112
.is_some_and ( | p | sp.policy.serial < p.policy.serial )
113
} )
114
.collect ( ) ;
115
Ok ( RefsResponse {
116
refs: out,
117
policy,
118
tags: self .tag_refs ( ) ? ,
119
history,
14edbaaf Andy McCall 1 month ago ↶
120
default_bookmark: Some ( self .config.bookmark.clone ( ) ) ,
75167672 Andy McCall 1 month ago
121
} )
98286406 Andy McCall 1 month ago ↶
122
}
123
124
125
fn closure_in_store ( & self , heads : & [ChangeHash]) -> Result< BTreeSet< ChangeHash> > {
126
let mut seen = BTreeSet:: new( ) ;
127
let mut stack: Vec< ChangeHash> = heads.to_vec ( ) ;
128
while let Some ( h) = stack.pop ( ) {
129
if seen.contains ( & h) | | ! self .store.has ( & h) {
130
continue ;
131
}
132
seen.insert ( h) ;
69816d68 Andy McCall 1 month ago ↶
133
stack.extend ( self .store.get_change_meta ( & h) ? .deps) ;
98286406 Andy McCall 1 month ago ↶
134
}
135
Ok ( seen)
136
}
137
2863456a Andy McCall 1 month ago ↶
138
139
140
pub fn shallow_changes ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
141
let mut out = Vec :: new( ) ;
142
let mut seen: BTreeSet< ChangeHash> = BTreeSet:: new( ) ;
143
let mut layer: Vec< ChangeHash> = heads.to_vec ( ) ;
144
for _ in 0 .. depth.max ( 1 ) {
145
let mut next = Vec :: new( ) ;
146
for h in layer {
147
if ! seen.insert ( h) | | ! self .store.has ( & h) {
148
continue ;
149
}
150
let c = self .store.get_change ( & h) ? ;
151
next.extend ( c.deps.iter ( ) .copied ( ) ) ;
152
out.push ( h) ;
153
}
154
if next.is_empty ( ) {
155
break ;
156
}
157
layer = next;
158
}
159
out.reverse ( ) ;
160
Ok ( out)
161
}
162
163
fn shallow_path ( & self ) -> PathBuf {
164
self .oxy.join ( " shallow" )
165
}
166
167
168
pub fn boundary ( & self ) -> BTreeSet< ChangeHash> {
75167672 Andy McCall 1 month ago
169
fs:: read_to_string( self .shallow_path ( ) )
170
.map ( | t | t.lines ( ) .filter_map ( ObjectRef:: from_hex) .collect ( ) )
171
.unwrap_or_default ( )
2863456a Andy McCall 1 month ago ↶
172
}
173
174
pub fn is_shallow ( & self ) -> bool {
175
! self .boundary ( ) .is_empty ( )
176
}
177
178
fn write_boundary ( & self , boundary : & BTreeSet< ChangeHash> ) -> Result< ( ) > {
179
if boundary.is_empty ( ) {
180
let _ = fs:: remove_file( self .shallow_path ( ) ) ;
181
return Ok ( ( ) ) ;
182
}
75167672 Andy McCall 1 month ago
183
let text: String = boundary
184
.iter ( )
185
.map ( | h | format! ( " {} \n " , h.to_hex ( ) ) )
186
.collect ( ) ;
2863456a Andy McCall 1 month ago ↶
187
write_atomic ( & self .shallow_path ( ) , text.as_bytes ( ) )
188
}
189
190
191
192
fn refresh_boundary ( & self , imported : & [ChangeHash]) -> Result< ( ) > {
193
let mut boundary = self .boundary ( ) ;
194
boundary.retain ( | h | ! self .store.has ( h) ) ;
195
for h in imported {
69816d68 Andy McCall 1 month ago ↶
196
for d in self .store.get_change_meta ( h) ? .deps {
2863456a Andy McCall 1 month ago ↶
197
if ! self .store.has ( & d) {
198
boundary.insert ( d) ;
199
}
200
}
201
}
202
self .write_boundary ( & boundary)
203
}
204
205
206
pub fn deepen ( & self , remote_name : & str , depth : u32 ) -> Result< usize > {
207
let boundary = self .boundary ( ) ;
208
if boundary.is_empty ( ) {
209
return Ok ( 0 ) ;
210
}
211
let remote = self .open_named_remote ( remote_name) ? ;
212
let wanted = remote.shallow ( & boundary.iter ( ) .copied ( ) .collect:: < Vec< _ > > ( ) , depth) ? ;
213
let bundle = remote.fetch ( & wanted, self .config.max_blob) ? ;
214
let imported = self .import_bundle_shallow ( & bundle, false ) ? ;
215
if self .config.max_blob.is_some ( ) {
216
self .register_missing_blobs ( & imported, remote_name) ? ;
217
}
218
self .refresh_boundary ( & imported) ? ;
219
Ok ( imported.len ( ) )
220
}
221
98286406 Andy McCall 1 month ago ↶
222
223
pub fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
224
let have = self .closure_in_store ( have) ? ;
2863456a Andy McCall 1 month ago ↶
225
226
227
let boundary = self .boundary ( ) ;
98286406 Andy McCall 1 month ago ↶
228
let mut out = Vec :: new( ) ;
229
let mut done: BTreeSet< ChangeHash> = have.clone ( ) ;
2863456a Andy McCall 1 month ago ↶
230
done.extend ( boundary.iter ( ) .copied ( ) ) ;
98286406 Andy McCall 1 month ago ↶
231
232
let mut stack: Vec< ( ChangeHash, bool ) > = want.iter ( ) .rev ( ) .map ( | h | ( * h, false ) ) .collect ( ) ;
233
while let Some ( ( h, expanded) ) = stack.pop ( ) {
234
if done.contains ( & h) {
235
continue ;
236
}
237
if ! self .store.has ( & h) {
238
return fail ( format! ( " change {} is not in this repository" , h.short ( 12 ) ) ) ;
239
}
240
if expanded {
241
done.insert ( h) ;
242
out.push ( h) ;
243
continue ;
244
}
245
stack.push ( ( h, true ) ) ;
69816d68 Andy McCall 1 month ago ↶
246
for d in self .store.get_change_meta ( & h) ? .deps {
98286406 Andy McCall 1 month ago ↶
247
if ! done.contains ( & d) {
248
stack.push ( ( d, false ) ) ;
249
}
250
}
251
}
252
Ok ( out)
253
}
254
255
256
257
pub fn bundle_for ( & self , changes : & [ChangeHash]) -> Result< Bundle> {
b3c5a59a Andy McCall 1 month ago ↶
258
self .bundle_for_filtered ( changes, None )
259
}
260
261
262
pub fn objects_for ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
263
let mut out = Vec :: new( ) ;
264
for r in refs {
265
let ( ty, body) = match self .store.get ( r) {
266
Ok ( x) => x,
267
Err ( StoreError:: NotFound( _ ) ) => continue ,
268
Err ( e) => return Err ( e.into ( ) ) ,
269
} ;
270
if ty = = ObjectType:: Blob {
271
for c in Blob:: decode( & body) ? .chunks {
272
if let Ok ( ( cty, cb) ) = self .store.get ( & c) {
273
out.push ( ( cty, cb) ) ;
274
}
275
}
276
}
277
out.push ( ( ty, body) ) ;
278
}
279
Ok ( out)
280
}
281
282
283
75167672 Andy McCall 1 month ago
284
pub fn bundle_for_filtered (
285
& self ,
286
changes : & [ChangeHash],
287
max_blob : Option< u64 > ,
288
) -> Result< Bundle> {
2c75672f Andy McCall 1 month ago ↶
289
self .bundle_of ( changes, max_blob, false )
290
}
291
292
293
294
295
296
pub fn bundle_raw_for ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
297
self .bundle_of ( changes, max_blob, true )
298
}
299
300
fn bundle_of (
301
& self ,
302
changes : & [ChangeHash],
303
max_blob : Option< u64 > ,
304
raw : bool ,
305
) -> Result< Bundle> {
98286406 Andy McCall 1 month ago ↶
306
let mut bundle = Bundle:: default( ) ;
307
let mut objects: BTreeSet< ObjectRef> = BTreeSet:: new( ) ;
308
let mut seals: BTreeSet< ObjectRef> = BTreeSet:: new( ) ;
309
let add_object = | r : & ObjectRef, objects : & mut BTreeSet< ObjectRef> | {
310
if ! r.is_zero ( ) & & self .store.has ( r) {
311
objects.insert ( * r) ;
312
}
313
} ;
314
for h in changes {
2c75672f Andy McCall 1 month ago ↶
315
316
let ( c, encoded) = if raw {
317
let bytes = self .store.get_typed ( h, ObjectType:: Change) ? ;
318
let ( skel, texts) = Change:: decode_skeleton( & bytes, * h) ? ;
319
for t in texts {
0f93f49c Andy McCall 1 month ago ↶
320
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
2c75672f Andy McCall 1 month ago ↶
321
let blob = Blob:: decode( & self .store.get_typed ( & r, ObjectType:: Blob) ? ) ? ;
322
objects.insert ( r) ;
323
objects.extend ( blob.chunks) ;
324
}
325
}
326
( skel, Some ( bytes) )
327
} else {
328
( self .store.get_change ( h) ? , None )
329
} ;
98286406 Andy McCall 1 month ago ↶
330
let sigs = self .store.signatures ( h) ? ;
331
add_object ( & c.author, & mut objects) ;
332
for s in & sigs {
333
add_object ( & s.key, & mut objects) ;
334
}
335
for e in & c.edits {
336
if let Edit:: Replace { old, new, .. } = e {
337
for b in [ old, new] .into_iter ( ) .flatten ( ) {
338
if self .store.has ( b) {
75167672 Andy McCall 1 month ago
339
if let Ok ( blob) =
340
Blob:: decode( & self .store.get_typed ( b, ObjectType:: Blob) ? )
341
{
b3c5a59a Andy McCall 1 month ago ↶
342
if max_blob.is_some_and ( | m | blob.length > m ) {
343
continue ;
344
}
345
objects.insert ( * b) ;
98286406 Andy McCall 1 month ago ↶
346
objects.extend ( blob.chunks.iter ( ) .copied ( ) ) ;
347
}
348
}
349
}
350
}
351
}
352
for s in self .store.seals_for ( h) ? {
353
seals.insert ( s) ;
2c75672f Andy McCall 1 month ago ↶
354
}
355
match encoded {
356
Some ( bytes) => bundle.raw_changes.push ( ( bytes, sigs) ) ,
357
None => {
358
bundle.changes.push ( ( c, sigs) ) ;
359
bundle.hashes.push ( * h) ;
360
}
98286406 Andy McCall 1 month ago ↶
361
}
362
}
363
for r in seals {
364
let attest = self .store.get_attest ( & r) ? ;
365
let sigs = self .store.signatures ( & r) ? ;
366
for s in & sigs {
367
add_object ( & s.key, & mut objects) ;
368
}
369
bundle.attests.push ( SignedAttest { attest, sigs } ) ;
370
}
371
for r in objects {
372
let ( ty, body) = self .store.get ( & r) ? ;
373
bundle.objects.push ( ( ty, body) ) ;
374
}
f0a517d2 Andy McCall 18 days ago ↶
375
self .attach_certificates ( & mut bundle) ? ;
98286406 Andy McCall 1 month ago ↶
376
Ok ( bundle)
377
}
378
f0a517d2 Andy McCall 18 days ago ↶
379
380
381
382
383
pub ( crate ) fn attach_certificates_for (
384
& self ,
385
bundle : & mut Bundle,
386
also : & [KeyId],
387
) -> Result< ( ) > {
388
let mut keys: BTreeSet< KeyId> = also.iter ( ) .copied ( ) .collect ( ) ;
389
keys.extend (
390
bundle
391
.changes
392
.iter ( )
393
.flat_map ( | ( _, s ) | s.iter ( ) .map ( | s | s.key ) ) ,
394
) ;
395
keys.extend (
396
bundle
397
.raw_changes
398
.iter ( )
399
.flat_map ( | ( _, s ) | s.iter ( ) .map ( | s | s.key ) ) ,
400
) ;
401
keys.extend (
402
bundle
403
.attests
404
.iter ( )
405
.flat_map ( | a | a.sigs.iter ( ) .map ( | s | s.key ) ) ,
406
) ;
407
for k in keys {
408
let Some ( r) = self .store.certificate_of ( & k) ? else {
409
continue ;
410
} ;
411
let cert = self .store.get_certificate ( & r) ? ;
412
if bundle
413
.certificates
414
.iter ( )
415
.any ( | c | c.cert.hash ( ) = = cert.cert.hash ( ) )
416
{
417
continue ;
418
}
419
for id in [ & k, & cert.cert.issuer] {
420
if let Ok ( body) = self .store.get_typed ( id, ObjectType:: Identity) {
421
if ! bundle
422
.objects
423
.iter ( )
424
.any ( | ( t , b ) | * t = = ObjectType:: Identity & & * b = = body)
425
{
426
bundle.objects.push ( ( ObjectType:: Identity, body) ) ;
427
}
428
}
429
}
430
bundle.certificates.push ( cert) ;
431
}
432
Ok ( ( ) )
433
}
434
435
fn attach_certificates ( & self , bundle : & mut Bundle) -> Result< ( ) > {
436
self .attach_certificates_for ( bundle, & [ ] )
437
}
438
439
440
441
fn adopt_certificates ( & self , certs : & [crate:: cert:: SignedCertificate]) -> Result< ( ) > {
442
for c in certs {
443
let issuer = self .store.get_identity ( & c.cert.issuer) .map_err ( | _| {
444
RepoError:: Msg( format! (
445
" a certificate names issuer {} , whose identity did not come with it" ,
446
c.cert.issuer.short ( 12 )
447
) )
448
} ) ? ;
449
c.verify ( & issuer)
450
.map_err ( | e | RepoError:: Msg( format! ( " a certificate does not verify: {e} " ) ) ) ? ;
451
self .store.put_identity ( & c.cert.key) ? ;
452
self .store.put_certificate ( c) ? ;
453
}
454
Ok ( ( ) )
455
}
456
457
458
459
460
461
pub ( crate ) fn certified_authorized (
462
& self ,
463
p : & crate:: policy:: Policy,
464
signer : & KeyId,
465
reference : & str ,
466
scope : Scope,
467
at_ms : u64 ,
468
) -> bool {
469
let Ok ( Some ( r) ) = self .store.certificate_of ( signer) else {
470
return false ;
471
} ;
472
let Ok ( c) = self .store.get_certificate ( & r) else {
473
return false ;
474
} ;
475
let Some ( issuer) = p
476
.identity ( & c.cert.issuer)
477
.cloned ( )
478
.or_else ( | | self .store.get_identity ( & c.cert.issuer) .ok ( ) )
479
else {
480
return false ;
481
} ;
482
c.verify ( & issuer) .is_ok ( )
483
& & c.cert.key_id ( ) = = * signer
484
& & c.cert.covers ( scope, at_ms)
485
& & p.subject_grant ( reference, & c.cert.issuer, & c.cert.subject, scope)
486
}
487
98286406 Andy McCall 1 month ago ↶
488
489
490
pub fn import_bundle ( & self , bundle : & Bundle, strict : bool ) -> Result< Vec< ChangeHash> > {
2863456a Andy McCall 1 month ago ↶
491
self .import_bundle_inner ( bundle, strict, false )
492
}
493
494
495
496
pub fn import_bundle_shallow ( & self , bundle : & Bundle, strict : bool ) -> Result< Vec< ChangeHash> > {
497
self .import_bundle_inner ( bundle, strict, true )
498
}
499
75167672 Andy McCall 1 month ago
500
fn import_bundle_inner (
501
& self ,
502
bundle : & Bundle,
503
strict : bool ,
504
shallow : bool ,
505
) -> Result< Vec< ChangeHash> > {
8f10a07a Andy McCall 1 month ago ↶
506
let batch = self .store.begin_batch ( ) ? ;
507
let result = self .import_bundle_batched ( bundle, strict, shallow) ;
508
batch.finish ( ) ? ;
509
result
510
}
511
75167672 Andy McCall 1 month ago
512
fn import_bundle_batched (
513
& self ,
514
bundle : & Bundle,
515
strict : bool ,
516
shallow : bool ,
517
) -> Result< Vec< ChangeHash> > {
2c75672f Andy McCall 1 month ago ↶
518
for ( ty, _ ) in & bundle.objects {
519
if ! matches! (
520
ty,
75167672 Andy McCall 1 month ago
521
ObjectType:: Chunk
2c75672f Andy McCall 1 month ago ↶
522
| ObjectType:: Blob
523
| ObjectType:: Identity
524
| ObjectType:: State
525
| ObjectType:: Checkpoint
526
) {
527
return fail ( format! ( " bundle may not carry {} objects" , ty.name ( ) ) ) ;
98286406 Andy McCall 1 month ago ↶
528
}
529
}
2c75672f Andy McCall 1 month ago ↶
530
531
let objects: Vec< ( ObjectType, & [u8 ]) > = bundle
532
.objects
533
.iter ( )
534
.map ( | ( ty , body ) | ( * ty, body.as_slice ( ) ) )
535
.collect ( ) ;
536
self .store.put_many ( & objects) ? ;
f0a517d2 Andy McCall 18 days ago ↶
537
self .adopt_certificates ( & bundle.certificates) ? ;
98286406 Andy McCall 1 month ago ↶
538
for sa in & bundle.attests {
539
let r = self .store.put_attest ( & sa.attest) ? ;
540
for sig in & sa.sigs {
541
self .store.put_signature ( & r, sig) ? ;
542
}
543
if let AttestBody:: Seal { hashes } = & sa.attest.body {
544
for h in hashes {
545
self .store.index_seal ( h, & r) ? ;
546
}
547
}
548
}
598009a7 Andy McCall 1 month ago ↶
549
550
551
552
let hashes: Vec< ChangeHash> = if bundle.hashes.len ( ) = = bundle.changes.len ( ) {
553
bundle.hashes.clone ( )
554
} else {
555
bundle.changes.iter ( ) .map ( | ( c , _) | c.hash ( ) ) .collect ( )
556
} ;
2c75672f Andy McCall 1 month ago ↶
557
let raw_hashes: Vec< ChangeHash> = bundle
558
.raw_changes
559
.iter ( )
560
.map ( | ( b , _) | hash_object ( ObjectType:: Change, b) )
561
.collect ( ) ;
562
let incoming: BTreeSet< ChangeHash> = hashes.iter ( ) .chain ( & raw_hashes) .copied ( ) .collect ( ) ;
2863456a Andy McCall 1 month ago ↶
563
let boundary = self .boundary ( ) ;
98286406 Andy McCall 1 month ago ↶
564
let mut imported = Vec :: new( ) ;
2c75672f Andy McCall 1 month ago ↶
565
566
567
for ( ( bytes, sigs) , h) in bundle.raw_changes.iter ( ) .zip ( raw_hashes) {
568
let ( skel, texts) = Change:: decode_skeleton( bytes, h) ? ;
569
for d in & skel.deps {
570
if ! self .store.has ( d) & & ! incoming.contains ( d) & & ! shallow & & ! boundary.contains ( d)
571
{
572
return fail ( format! (
573
" change {} depends on {} , which is neither stored nor in the bundle" ,
574
h.short ( 12 ) ,
575
d.short ( 12 )
576
) ) ;
577
}
578
}
579
for t in texts {
0f93f49c Andy McCall 1 month ago ↶
580
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
2c75672f Andy McCall 1 month ago ↶
581
if ! self .store.has ( & r) {
582
return fail ( format! (
583
" change {} came without its text blob {} " ,
584
h.short ( 12 ) ,
585
r.short ( 12 )
586
) ) ;
587
}
588
}
589
}
590
self .store.put ( ObjectType:: Change, bytes) ? ;
591
for sig in sigs {
592
self .store.put_signature ( & h, sig) ? ;
593
}
594
imported.push ( h) ;
595
}
598009a7 Andy McCall 1 month ago ↶
596
for ( ( c, sigs) , h) in bundle.changes.iter ( ) .zip ( hashes) {
98286406 Andy McCall 1 month ago ↶
597
for d in & c.deps {
75167672 Andy McCall 1 month ago
598
if ! self .store.has ( d) & & ! incoming.contains ( d) & & ! shallow & & ! boundary.contains ( d)
599
{
600
return fail ( format! (
601
" change {} depends on {} , which is neither stored nor in the bundle" ,
602
h.short ( 12 ) ,
603
d.short ( 12 )
604
) ) ;
98286406 Andy McCall 1 month ago ↶
605
}
606
}
598009a7 Andy McCall 1 month ago ↶
607
let stored = self .store.put_change ( c) ? ;
608
if stored ! = h {
609
return fail ( format! (
610
" change {} is not the change the bundle names" ,
611
h.short ( 12 )
612
) ) ;
613
}
98286406 Andy McCall 1 month ago ↶
614
for sig in sigs {
615
self .store.put_signature ( & h, sig) ? ;
616
}
617
imported.push ( h) ;
618
}
619
if strict {
620
for h in & imported {
621
if ! matches! ( self .authenticate_change ( h) ? , SignatureStatus:: Valid { .. } ) {
75167672 Andy McCall 1 month ago
622
return fail ( format! (
623
" change {} is not authenticated by its author" ,
624
h.short ( 12 )
625
) ) ;
98286406 Andy McCall 1 month ago ↶
626
}
627
}
628
}
629
Ok ( imported)
630
}
631
632
633
634
84b9e9ac Andy McCall 1 month ago ↶
635
636
7ba155f2 Andy McCall 1 month ago ↶
637
638
639
eae59556 Andy McCall 1 month ago ↶
640
pub ( crate ) fn delegate_authorized (
641
& self ,
642
p : & crate:: policy:: Policy,
643
signer : & KeyId,
644
reference : & str ,
7ba155f2 Andy McCall 1 month ago ↶
645
at_ms : u64 ,
eae59556 Andy McCall 1 month ago ↶
646
) -> bool {
84b9e9ac Andy McCall 1 month ago ↶
647
if p.is_revoked ( signer) {
648
return false ;
649
}
eae59556 Andy McCall 1 month ago ↶
650
let Some ( rule) = p.rule_for ( reference) else {
651
return false ;
652
} ;
84b9e9ac Andy McCall 1 month ago ↶
653
rule.keys.iter ( ) .any ( | k | {
654
! p.is_revoked ( k)
655
& & p.identity ( k)
656
.cloned ( )
657
.or_else ( | | self .store.get_identity ( k) .ok ( ) )
7ba155f2 Andy McCall 1 month ago ↶
658
.is_some_and ( | id | id.delegates_to ( signer, Scope:: REFS , at_ms) )
84b9e9ac Andy McCall 1 month ago ↶
659
} )
660
}
661
e5d9b051 Andy McCall 19 days ago ↶
662
663
664
665
666
667
668
pub ( crate ) fn predecessor_authorized (
669
& self ,
670
p : & crate:: policy:: Policy,
671
signer : & KeyId,
672
reference : & str ,
673
at_ms : u64 ,
674
) -> bool {
675
if p.is_revoked ( signer) {
676
return false ;
677
}
678
let Some ( rule) = p.rule_for ( reference) else {
679
return false ;
680
} ;
681
let identity = | k : & KeyId| {
682
p.identity ( k)
683
.cloned ( )
684
.or_else ( | | self .store.get_identity ( k) .ok ( ) )
685
} ;
686
rule.keys.iter ( ) .any ( | k | {
687
if p.is_revoked ( k) {
688
return false ;
689
}
690
let mut cur = identity ( k) ;
691
for _ in 0 .. 32 {
692
let Some ( id) = cur else {
693
return false ;
694
} ;
695
match id.previous {
696
Some ( prev) if prev = = * signer => return at_ms < id.created,
697
Some ( prev) if ! p.is_revoked ( & prev) => cur = identity ( & prev) ,
698
_ => return false ,
699
}
700
}
701
false
702
} )
703
}
704
75167672 Andy McCall 1 month ago
705
fn check_ref_attest (
706
& self ,
707
sa : & SignedAttest,
708
name : & str ,
709
old_state : & ObjectRef,
710
new_state : & ObjectRef,
711
last_serial : Option< u64 > ,
712
allow_equal : bool ,
713
) -> std:: result:: Result< u64 , String> {
714
let AttestBody:: Ref {
715
name: n,
716
old_state: o,
717
new_state: nw,
718
serial,
719
} = & sa.attest.body
720
else {
98286406 Andy McCall 1 month ago ↶
721
return Err ( " attestation is not a ref attestation" .into ( ) ) ;
722
} ;
723
if n ! = name | | o ! = old_state | | nw ! = new_state {
724
return Err ( " attestation does not describe this update" .into ( ) ) ;
725
}
726
if sa.attest.repo ! = self .id ( ) .map_err ( | e | e.to_string ( ) ) ? {
727
return Err ( " attestation is for a different repository" .into ( ) ) ;
728
}
729
if let Some ( last) = last_serial {
730
if * serial < last | | ( * serial = = last & & ! allow_equal) {
75167672 Andy McCall 1 month ago
731
return Err ( format! (
732
" attestation serial {serial} is not greater than the last seen {last} "
733
) ) ;
98286406 Andy McCall 1 month ago ↶
734
}
735
}
736
let r = sa.attest.hash ( ) ;
737
let sig = sa.sigs.first ( ) .ok_or ( " attestation carries no signature" ) ? ;
75167672 Andy McCall 1 month ago
738
let id = self
739
.store
740
.get_identity ( & sig.key)
741
.map_err ( | _| format! ( " unknown key {} " , sig.key.short ( 12 ) ) ) ? ;
98286406 Andy McCall 1 month ago ↶
742
verify_signature ( & id, sig, Domain:: Attest, & r) .map_err ( | e | e.to_string ( ) ) ? ;
743
Ok ( * serial)
744
}
745
83e0677b Andy McCall 1 month ago ↶
746
98286406 Andy McCall 1 month ago ↶
747
83e0677b Andy McCall 1 month ago ↶
748
749
750
75167672 Andy McCall 1 month ago
751
pub fn receive_push (
752
& self ,
753
bundle : Bundle,
754
updates : Vec< RefUpdate> ,
755
policy : Option< SignedPolicy> ,
756
tags : Vec< crate:: protocol:: TagRef> ,
757
) -> Result< Vec< RefResult> > {
119bbf50 Andy McCall 1 month ago ↶
758
Ok ( self
759
.receive_push_report ( bundle, updates, policy, tags) ?
760
.results )
7aa2dc93 Andy McCall 1 month ago ↶
761
}
762
763
764
765
766
pub fn receive_push_report (
767
& self ,
768
bundle : Bundle,
769
updates : Vec< RefUpdate> ,
770
policy : Option< SignedPolicy> ,
771
tags : Vec< crate:: protocol:: TagRef> ,
772
) -> Result< PushReport> {
773
let mut report = PushReport:: default( ) ;
774
let results = & mut report.results;
83e0677b Andy McCall 1 month ago ↶
775
if let Some ( sp) = policy {
28fc23d7 Andy McCall 1 month ago ↶
776
match self .adopt_policy_chain ( & bundle.policies, & sp, " push received" ) {
83e0677b Andy McCall 1 month ago ↶
777
Ok ( ( ) ) => {
75167672 Andy McCall 1 month ago
778
results.push ( RefResult {
779
name: " policy" .into ( ) ,
780
ok: true ,
781
reason: format! ( " serial {} " , sp.policy.serial) ,
782
} ) ;
83e0677b Andy McCall 1 month ago ↶
783
}
784
Err ( e) => {
75167672 Andy McCall 1 month ago
785
results.push ( RefResult {
786
name: " policy" .into ( ) ,
787
ok: false ,
788
reason: e,
789
} ) ;
83e0677b Andy McCall 1 month ago ↶
790
for u in updates {
75167672 Andy McCall 1 month ago
791
results.push ( RefResult {
792
name: u.name,
793
ok: false ,
794
reason: " policy update was rejected" .into ( ) ,
795
} ) ;
83e0677b Andy McCall 1 month ago ↶
796
}
7aa2dc93 Andy McCall 1 month ago ↶
797
return Ok ( report) ;
83e0677b Andy McCall 1 month ago ↶
798
}
799
}
800
}
801
let policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy ) ;
cb8b8082 Andy McCall 26 days ago ↶
802
803
804
let objects_only = updates.is_empty ( ) & & tags.is_empty ( ) ;
7aa2dc93 Andy McCall 1 month ago ↶
805
match self .import_bundle ( & bundle, policy.is_some ( ) ) {
cb8b8082 Andy McCall 26 days ago ↶
806
Ok ( imported) => {
807
if objects_only {
808
results.push ( RefResult {
809
name: OBJECTS_RESULT .into ( ) ,
810
ok: true ,
811
reason: format! ( " {} changes stored" , imported.len ( ) ) ,
812
} ) ;
813
}
814
report.imported = imported;
815
}
7aa2dc93 Andy McCall 1 month ago ↶
816
Err ( e) => {
cb8b8082 Andy McCall 26 days ago ↶
817
if objects_only {
818
results.push ( RefResult {
819
name: OBJECTS_RESULT .into ( ) ,
820
ok: false ,
821
reason: e.to_string ( ) ,
822
} ) ;
823
}
7aa2dc93 Andy McCall 1 month ago ↶
824
for u in updates {
825
results.push ( RefResult {
826
name: u.name,
827
ok: false ,
828
reason: e.to_string ( ) ,
829
} ) ;
830
}
831
return Ok ( report) ;
83e0677b Andy McCall 1 month ago ↶
832
}
833
}
98286406 Andy McCall 1 month ago ↶
834
let ( op_ref, op) = self .head_op ( ) ? ;
835
let mut view = op.view.clone ( ) ;
836
let mut names = Vec :: new( ) ;
837
for u in updates {
83e0677b Andy McCall 1 month ago ↶
838
let outcome = self .apply_update ( & mut view, & u, policy.as_ref ( ) ) ;
98286406 Andy McCall 1 month ago ↶
839
let ( ok, reason) = match outcome {
840
Ok ( ( ) ) => ( true , String :: new( ) ) ,
841
Err ( e) => ( false , e) ,
842
} ;
843
if ok {
844
names.push ( u.name.clone ( ) ) ;
119bbf50 Andy McCall 1 month ago ↶
845
let heads = self
846
.store
847
.get_state ( & u.new_state)
848
.map ( | s | s.heads )
849
.unwrap_or_default ( ) ;
850
report.updated.push ( RefUpdated {
851
name: u.name.clone ( ) ,
852
old_state: u.old_state,
853
new_state: u.new_state,
854
heads,
855
} ) ;
98286406 Andy McCall 1 month ago ↶
856
}
75167672 Andy McCall 1 month ago
857
results.push ( RefResult {
858
name: u.name,
859
ok,
860
reason,
861
} ) ;
98286406 Andy McCall 1 month ago ↶
862
}
132b884b Andy McCall 1 month ago ↶
863
for t in & tags {
864
let name = format! ( " tags/{} " , t.name) ;
b2ef93bb Andy McCall 1 month ago ↶
865
let new = ! view.tags.contains_key ( & t.name) ;
132b884b Andy McCall 1 month ago ↶
866
match self .adopt_tag ( & mut view, t, policy.as_ref ( ) ) {
867
Ok ( ( ) ) => {
b2ef93bb Andy McCall 1 month ago ↶
868
869
870
871
if new {
872
report.updated.push ( RefUpdated {
873
name: name.clone ( ) ,
874
old_state: ObjectRef:: ZERO ,
875
new_state: t.state,
876
heads: t.heads.clone ( ) ,
877
} ) ;
878
}
132b884b Andy McCall 1 month ago ↶
879
names.push ( name.clone ( ) ) ;
75167672 Andy McCall 1 month ago
880
results.push ( RefResult {
881
name,
882
ok: true ,
883
reason: String :: new( ) ,
884
} ) ;
132b884b Andy McCall 1 month ago ↶
885
}
75167672 Andy McCall 1 month ago
886
Err ( e) => results.push ( RefResult {
887
name,
888
ok: false ,
889
reason: e,
890
} ) ,
132b884b Andy McCall 1 month ago ↶
891
}
892
}
98286406 Andy McCall 1 month ago ↶
893
if ! names.is_empty ( ) {
75167672 Andy McCall 1 month ago
894
self .record_op (
895
& [ op_ref] ,
896
& format! ( " push received: {} " , names.join ( " " ) ) ,
897
view,
898
) ? ;
98286406 Andy McCall 1 month ago ↶
899
}
7aa2dc93 Andy McCall 1 month ago ↶
900
Ok ( report)
98286406 Andy McCall 1 month ago ↶
901
}
902
be451356 Andy McCall 1 month ago ↶
903
904
905
906
907
fn record_update_attest (
908
& self ,
909
u : & RefUpdate,
910
sa : & SignedAttest,
911
) -> std:: result:: Result< ( ) , String> {
912
let name = & u.name;
913
let last = self
914
.latest_ref_attest ( name)
915
.map_err ( | e | format! ( " reading the last attestation of {name:?} : {e} " ) ) ?
916
.and_then ( | ( _, a ) | match a.attest.body {
917
AttestBody:: Ref { serial, .. } => Some ( serial) ,
918
_ => None ,
919
} ) ;
920
self .check_ref_attest ( sa, name, & u.old_state, & u.new_state, last, false ) ? ;
921
let store = | e : & dyn std:: fmt:: Display| format! ( " storing the attestation of {name:?} : {e} " ) ;
922
let r = self .store.put_attest ( & sa.attest) .map_err ( | e | store ( & e) ) ? ;
923
for sig in & sa.sigs {
924
self .store.put_signature ( & r, sig) .map_err ( | e | store ( & e) ) ? ;
f53cbe7a Andy McCall 18 days ago ↶
925
}
926
self .set_ref_attest ( name, & r) .map_err ( | e | store ( & e) ) ? ;
927
928
let certified = sa
929
.sigs
930
.first ( )
931
.is_some_and ( | s | self .store.certificate_of ( & s.key) .ok ( ) .flatten ( ) .is_some ( ) ) ;
932
if certified {
933
self .record_acceptance ( & r) .map_err ( | e | store ( & e) ) ? ;
be451356 Andy McCall 1 month ago ↶
934
}
f53cbe7a Andy McCall 18 days ago ↶
935
Ok ( ( ) )
be451356 Andy McCall 1 month ago ↶
936
}
937
f53cbe7a Andy McCall 18 days ago ↶
938
939
940
pub ( crate ) fn record_acceptance ( & self , r : & ObjectRef) -> Result< ( ) > {
941
let Some ( acceptor) = & self .acceptor else {
942
return Ok ( ( ) ) ;
943
} ;
944
let ( id, sk) = ( & acceptor.0 , & acceptor.1 ) ;
945
let attest = Attest {
946
repo: self .id ( ) ? ,
947
hlc: Hlc:: now( ) ,
948
body: AttestBody:: Accepted { attest: * r } ,
949
} ;
950
let sig = sk.sign ( Domain:: Attest, & id.hash ( ) , & attest.hash ( ) ) ;
951
self .store.put_identity ( id) ? ;
952
let a = self .store.put_attest ( & attest) ? ;
953
self .store.put_signature ( & a, & sig) ? ;
954
self .store.index_add_pub ( " accepted" , r, & a) ? ;
955
Ok ( ( ) )
956
}
957
958
959
pub fn acceptance_of ( & self , r : & ObjectRef) -> Result< Option< SignedAttest> > {
960
let Some ( a) = self .store.index_get_pub ( " accepted" , r) ? .into_iter ( ) .next ( ) else {
961
return Ok ( None ) ;
962
} ;
963
Ok ( Some ( SignedAttest {
964
attest: self .store.get_attest ( & a) ? ,
965
sigs: self .store.signatures ( & a) ? ,
966
} ) )
967
}
968
969
970
971
972
973
pub ( crate ) fn certified_time (
974
& self ,
975
signer : & KeyId,
976
sa : & SignedAttest,
977
accepted : Option< & SignedAttest> ,
978
) -> u64 {
979
let own = sa.attest.hlc.wall_ms;
980
let Some ( acc) = accepted else {
981
return own;
982
} ;
983
let Ok ( Some ( c) ) = self .store.certificate_of ( signer) else {
984
return own;
985
} ;
986
let Ok ( cert) = self .store.get_certificate ( & c) else {
987
return own;
988
} ;
989
let AttestBody:: Accepted { attest } = & acc.attest.body else {
990
return own;
991
} ;
992
let Some ( sig) = acc.sigs.iter ( ) .find ( | s | s.key = = cert.cert.issuer ) else {
993
return own;
994
} ;
995
let Ok ( issuer) = self .store.get_identity ( & cert.cert.issuer) else {
996
return own;
997
} ;
998
if * attest = = sa.attest.hash ( )
999
& & verify_signature ( & issuer, sig, Domain:: Attest, & acc.attest.hash ( ) ) .is_ok ( )
1000
{
1001
acc.attest.hlc.wall_ms
1002
} else {
1003
own
1004
}
1005
}
1006
f8c69576 Andy McCall 1 month ago ↶
1007
8c30d814 Andy McCall 1 month ago ↶
1008
1009
1010
1011
f8c69576 Andy McCall 1 month ago ↶
1012
1013
1014
fn apply_deletion (
1015
& self ,
1016
view : & mut View,
1017
u : & RefUpdate,
1018
policy : Option< & crate:: policy:: Policy> ,
1019
) -> std:: result:: Result< ( ) , String> {
1020
let Some ( current) = view.bookmarks.get ( & u.name) .copied ( ) else {
1021
return Err ( format! ( " no bookmark {:?} to delete" , u.name) ) ;
1022
} ;
1023
if current ! = u.old_state {
1024
return Err ( " stale: the remote bookmark has moved; pull first" .into ( ) ) ;
1025
}
8c30d814 Andy McCall 1 month ago ↶
1026
if ! self .is_bare ( ) & & u.name = = self .config.bookmark {
f8c69576 Andy McCall 1 month ago ↶
1027
return Err ( format! (
8c30d814 Andy McCall 1 month ago ↶
1028
" {:?} is this repository's default bookmark (checked out there) and cannot be deleted" ,
f8c69576 Andy McCall 1 month ago ↶
1029
u.name
1030
) ) ;
1031
}
1032
if let Some ( p) = policy {
1033
let Some ( sa) = & u.attestation else {
1034
return Err ( format! (
1035
" deleting {:?} requires a signed reference attestation under this repository's policy" ,
1036
u.name
1037
) ) ;
1038
} ;
1039
let signer = sa
1040
.sigs
1041
.first ( )
1042
.map ( | s | s.key )
1043
.ok_or ( " attestation carries no signature" ) ? ;
f0a517d2 Andy McCall 18 days ago ↶
1044
1045
let now = Hlc:: now( ) .wall_ms;
7ba155f2 Andy McCall 1 month ago ↶
1046
if ! p.authorizes ( & signer, & u.name)
1047
& & ! self .delegate_authorized ( p, & signer, & u.name, sa.attest.hlc.wall_ms)
f0a517d2 Andy McCall 18 days ago ↶
1048
& & ! self .certified_authorized ( p, & signer, & u.name, Scope:: REFS , now)
7ba155f2 Andy McCall 1 month ago ↶
1049
{
f8c69576 Andy McCall 1 month ago ↶
1050
return Err ( format! (
1051
" key {} may not delete {:?} under this repository's policy" ,
1052
signer.short ( 12 ) ,
1053
u.name
1054
) ) ;
1055
}
1056
}
1057
if let Some ( sa) = & u.attestation {
be451356 Andy McCall 1 month ago ↶
1058
self .record_update_attest ( u, sa) ? ;
f8c69576 Andy McCall 1 month ago ↶
1059
}
1060
view.bookmarks.remove ( & u.name) ;
1061
Ok ( ( ) )
1062
}
1063
75167672 Andy McCall 1 month ago
1064
fn apply_update (
1065
& self ,
1066
view : & mut View,
1067
u : & RefUpdate,
1068
policy : Option< & crate:: policy:: Policy> ,
1069
) -> std:: result:: Result< ( ) , String> {
83e0677b Andy McCall 1 month ago ↶
1070
if u.name = = " policy" {
75167672 Andy McCall 1 month ago
1071
return Err (
1072
" the policy reference is updated by sending a policy, not a bookmark update" .into ( ) ,
1073
) ;
83e0677b Andy McCall 1 month ago ↶
1074
}
75167672 Andy McCall 1 month ago
1075
let empty = self
1076
.store
1077
.put_state ( & StateObject:: new( vec! [ ] ) )
1078
.map_err ( | e | e.to_string ( ) ) ? ;
f8c69576 Andy McCall 1 month ago ↶
1079
if u.new_state.is_zero ( ) {
1080
return self .apply_deletion ( view, u, policy) ;
1081
}
98286406 Andy McCall 1 month ago ↶
1082
let current = view.bookmarks.get ( & u.name) .copied ( ) .unwrap_or ( empty) ;
1083
if current ! = u.old_state {
1084
return Err ( " stale: the remote bookmark has moved; pull first" .into ( ) ) ;
1085
}
75167672 Andy McCall 1 month ago
1086
let new_state = self
1087
.store
1088
.get_state ( & u.new_state)
1089
.map_err ( | _| " new state object is missing" .to_string ( ) ) ? ;
98286406 Andy McCall 1 month ago ↶
1090
for h in & new_state.heads {
1091
if ! self .store.has ( h) {
1092
return Err ( format! ( " head {} is missing" , h.short ( 12 ) ) ) ;
1093
}
1094
}
1095
if ! u.force {
75167672 Andy McCall 1 month ago
1096
let old_heads = self
1097
.store
1098
.get_state ( & current)
1099
.map_err ( | e | e.to_string ( ) ) ?
1100
.heads;
1101
let closure = self
1102
.closure_in_store ( & new_state.heads)
1103
.map_err ( | e | e.to_string ( ) ) ? ;
98286406 Andy McCall 1 month ago ↶
1104
if let Some ( h) = old_heads.iter ( ) .find ( | h | ! closure.contains ( h) ) {
75167672 Andy McCall 1 month ago
1105
return Err ( format! (
1106
" not a fast-forward: current head {} would be dropped (use --force)" ,
1107
h.short ( 12 )
1108
) ) ;
98286406 Andy McCall 1 month ago ↶
1109
}
1110
}
83e0677b Andy McCall 1 month ago ↶
1111
if let Some ( p) = policy {
1112
let Some ( sa) = & u.attestation else {
75167672 Andy McCall 1 month ago
1113
return Err ( format! (
1114
" {:?} requires a signed reference attestation under this repository's policy" ,
1115
u.name
1116
) ) ;
83e0677b Andy McCall 1 month ago ↶
1117
} ;
75167672 Andy McCall 1 month ago
1118
let signer = sa
1119
.sigs
1120
.first ( )
1121
.map ( | s | s.key )
1122
.ok_or ( " attestation carries no signature" ) ? ;
f0a517d2 Andy McCall 18 days ago ↶
1123
1124
let now = Hlc:: now( ) .wall_ms;
7ba155f2 Andy McCall 1 month ago ↶
1125
if ! p.authorizes ( & signer, & u.name)
1126
& & ! self .delegate_authorized ( p, & signer, & u.name, sa.attest.hlc.wall_ms)
f0a517d2 Andy McCall 18 days ago ↶
1127
& & ! self .certified_authorized ( p, & signer, & u.name, Scope:: REFS , now)
7ba155f2 Andy McCall 1 month ago ↶
1128
{
75167672 Andy McCall 1 month ago
1129
return Err ( format! (
1130
" key {} may not update {:?} under this repository's policy" ,
1131
signer.short ( 12 ) ,
1132
u.name
1133
) ) ;
83e0677b Andy McCall 1 month ago ↶
1134
}
75167672 Andy McCall 1 month ago
1135
let old_heads = self
1136
.store
1137
.get_state ( & current)
1138
.map_err ( | e | e.to_string ( ) ) ?
1139
.heads;
83e0677b Andy McCall 1 month ago ↶
1140
self .state_allowed ( & old_heads, & new_state.heads, p, & u.name) ? ;
1141
}
98286406 Andy McCall 1 month ago ↶
1142
if let Some ( sa) = & u.attestation {
be451356 Andy McCall 1 month ago ↶
1143
self .record_update_attest ( u, sa) ? ;
98286406 Andy McCall 1 month ago ↶
1144
}
1145
view.bookmarks.insert ( u.name.clone ( ) , u.new_state) ;
1146
Ok ( ( ) )
1147
}
1148
1149
1150
1151
fn remote_url ( & self , name : & str ) -> Result< String> {
75167672 Andy McCall 1 month ago
1152
self .config.remotes.get ( name) .cloned ( ) .ok_or_else ( | | {
42ccf504 Andy McCall 27 days ago ↶
1153
let names: Vec< & str > = self .config.remotes.keys ( ) .map ( String :: as_str) .collect ( ) ;
1154
RepoError:: Msg( match names.as_slice ( ) {
1155
[ ] => format! ( " no remote named {name:?} ; add one with `oxy remote add`" ) ,
1156
_ if name = = " origin" & & names.len ( ) > 1 & & self .config.default_remote.is_none ( ) => format! (
1157
" this repository has no remote named origin, and several others ({} ): name one (`oxy pull {} `), or make one the default with `oxy config core.remote {} `" ,
1158
names.join ( " , " ) ,
1159
names[ 0 ] ,
1160
names[ 0 ]
1161
) ,
1162
_ => format! (
1163
" no remote named {name:?} ; this repository has {} " ,
1164
names.join ( " , " )
1165
) ,
1166
} )
75167672 Andy McCall 1 month ago
1167
} )
98286406 Andy McCall 1 month ago ↶
1168
}
1169
75167672 Andy McCall 1 month ago
1170
1171
1172
05e71ed6 Andy McCall 1 month ago ↶
1173
fn check_remote_id (
1174
& self ,
1175
remote : & dyn crate:: protocol:: Remote,
1176
remote_name : & str ,
1177
allow_unbound : bool ,
1178
) -> Result< ( ) > {
75167672 Andy McCall 1 month ago
1179
let theirs = remote.repo_id ( ) ? ;
1180
if theirs = = self .id ( ) ? {
1181
return Ok ( ( ) ) ;
1182
}
1183
if theirs = = crate :: protocol:: UNBOUND_ID {
1184
if allow_unbound {
1185
return Ok ( ( ) ) ;
1186
}
05e71ed6 Andy McCall 1 month ago ↶
1187
return fail ( format! (
1188
" remote {remote_name:?} is empty and unbound: push to it first"
1189
) ) ;
75167672 Andy McCall 1 month ago
1190
}
05e71ed6 Andy McCall 1 month ago ↶
1191
fail ( format! (
3e5772cc Andy McCall 1 month ago ↶
1192
" remote {remote_name:?} is a different repository (repository IDs differ); check its URL with `oxy remote list`"
05e71ed6 Andy McCall 1 month ago ↶
1193
) )
75167672 Andy McCall 1 month ago
1194
}
1195
ff1f62fe Andy McCall 1 month ago ↶
1196
1197
1198
3e5772cc Andy McCall 1 month ago ↶
1199
pub ( crate ) fn remote_options ( & self , name : & str ) -> RemoteOptions {
ff1f62fe Andy McCall 1 month ago ↶
1200
let url = self .config.remotes.get ( name) .cloned ( ) .unwrap_or_default ( ) ;
1201
let mut options = RemoteOptions {
75167672 Andy McCall 1 month ago
1202
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1203
token: self .config.remote_tokens.get ( name) .cloned ( ) ,
1204
ca: self .config.remote_cas.get ( name) .map ( PathBuf:: from) ,
9f9e72b9 Andy McCall 1 month ago ↶
1205
cache_dir: Some ( self .oxy.join ( " remotes" ) .join ( name) .join ( " strata" ) ) ,
68a92484 Andy McCall 22 days ago ↶
1206
.. RemoteOptions:: default( )
ff1f62fe Andy McCall 1 month ago ↶
1207
} ;
1208
self .fill_credentials ( & url, & mut options) ;
1209
options
1210
}
1211
1212
1213
1214
pub fn credentials ( & self ) -> crate:: credentials:: Credentials {
1215
crate :: credentials:: Credentials:: at(
1216
self .user_config
1217
.parent ( )
1218
.map ( Path:: to_path_buf)
1219
.unwrap_or_else ( default_home) ,
1220
)
1221
}
1222
1223
1224
1225
68a92484 Andy McCall 22 days ago ↶
1226
1227
1228
1229
ff1f62fe Andy McCall 1 month ago ↶
1230
fn fill_credentials ( & self , url : & str , options : & mut RemoteOptions) {
1231
if options.token.is_some ( ) & & options.ca.is_some ( ) {
1232
return ;
1233
}
68a92484 Andy McCall 22 days ago ↶
1234
let explicit =
1235
options.token.is_some ( ) | | std:: env:: var( " OXY_TOKEN" ) .is_ok_and ( | t | ! t.is_empty ( ) ) ;
ff1f62fe Andy McCall 1 month ago ↶
1236
let stored = self .credentials ( ) .options_for ( url) ;
1237
options.token = options.token.take ( ) .or ( stored.token) ;
1238
options.ca = options.ca.take ( ) .or ( stored.ca) ;
68a92484 Andy McCall 22 days ago ↶
1239
let enclosing = self .enclosing_credentials ( url) ;
1240
if options.token.is_none ( ) {
1241
options.token = enclosing.iter ( ) .find_map ( | ( t , _) | t.clone ( ) ) ;
1242
}
1243
if options.ca.is_none ( ) {
1244
options.ca = enclosing.iter ( ) .find_map ( | ( _, c ) | c.clone ( ) ) ;
1245
}
1246
if ! explicit {
1247
for t in enclosing.into_iter ( ) .filter_map ( | ( t , _) | t ) {
1248
if options.token.as_ref ( ) ! = Some ( & t) & & ! options.fallback_tokens.contains ( & t) {
1249
options.fallback_tokens.push ( t) ;
1250
}
1251
}
2b5ee371 Andy McCall 1 month ago ↶
1252
}
68a92484 Andy McCall 22 days ago ↶
1253
}
1254
1255
1256
1257
fn enclosing_credentials ( & self , url : & str ) -> Vec< ( Option< String> , Option< PathBuf> ) > {
1258
let mut out = Vec :: new( ) ;
ff1f62fe Andy McCall 1 month ago ↶
1259
let Some ( hub) = crate :: credentials:: hub_of( url) else {
68a92484 Andy McCall 22 days ago ↶
1260
return out;
ff1f62fe Andy McCall 1 month ago ↶
1261
} ;
1262
let mut at = self .root.parent ( ) ;
1263
while let Some ( dir) = at {
1264
let Ok ( cfg) = read_config_file ( & dir.join ( OXY_DIR ) .join ( " config" ) ) else {
1265
break ;
1266
} ;
1267
for ( key, remote_url) in & cfg {
1268
let Some ( name) = key
1269
.strip_prefix ( " remote." )
1270
.and_then ( | k | k.strip_suffix ( " .url" ) )
1271
else {
1272
continue ;
1273
} ;
1274
if crate :: credentials:: hub_of( remote_url) .as_deref ( ) ! = Some ( hub.as_str ( ) ) {
1275
continue ;
1276
}
68a92484 Andy McCall 22 days ago ↶
1277
out.push ( (
1278
cfg.get ( & format! ( " remote.{name} .token" ) ) .cloned ( ) ,
1279
cfg.get ( & format! ( " remote.{name} .ca" ) ) .map ( PathBuf:: from) ,
1280
) ) ;
ff1f62fe Andy McCall 1 month ago ↶
1281
}
1282
at = dir.parent ( ) ;
1283
}
68a92484 Andy McCall 22 days ago ↶
1284
out
98286406 Andy McCall 1 month ago ↶
1285
}
1286
d82e4409 Andy McCall 1 month ago ↶
1287
1288
1289
pub fn push_seals ( & self , remote_name : & str , seals : & [ObjectRef]) -> Result< ( ) > {
1290
let remote = self .open_named_remote ( remote_name) ? ;
1291
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
1292
let mut bundle = Bundle:: default( ) ;
1293
for r in seals {
1294
let attest = self .store.get_attest ( r) ? ;
1295
let sigs = self .store.signatures ( r) ? ;
4213c41d Andy McCall 1 month ago ↶
1296
bundle
1297
.attests
1298
.push ( crate :: protocol:: SignedAttest { attest, sigs } ) ;
d82e4409 Andy McCall 1 month ago ↶
1299
}
1300
let results = remote.push ( bundle, vec! [ ] , None , vec! [ ] ) ? ;
1301
if let Some ( r) = results.iter ( ) .find ( | r | ! r.ok) {
1302
return fail ( format! ( " {} : {} " , r.name, r.reason) ) ;
1303
}
1304
Ok ( ( ) )
1305
}
1306
1307
1308
pub fn remote_for_url ( & self , base : & str ) -> Option< String> {
1309
let base = base.trim_end_matches ( ' /' ) ;
4213c41d Andy McCall 1 month ago ↶
1310
self .config
1311
.remotes
1312
.iter ( )
1313
.find ( | ( _, url ) | url.trim_end_matches ( ' /' ) = = base )
1314
.map ( | ( name , _) | name.clone ( ) )
d82e4409 Andy McCall 1 month ago ↶
1315
}
1316
1317
ff1f62fe Andy McCall 1 month ago ↶
1318
d82e4409 Andy McCall 1 month ago ↶
1319
pub fn open_remote_at ( & self , url : & str ) -> Result< ( String, Box< dyn crate:: protocol:: Remote> ) > {
1320
if let Some ( name) = self .remote_for_url ( url) {
1321
return Ok ( ( name.clone ( ) , self .open_named_remote ( & name) ? ) ) ;
1322
}
1323
let options = RemoteOptions {
1324
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1325
.. self .credentials_for ( url)
d82e4409 Andy McCall 1 month ago ↶
1326
} ;
1327
Ok ( ( url.to_string ( ) , open_remote ( url, & options) ? ) )
1328
}
1329
1330
1331
1332
pub fn options_for_url ( & self , url : & str ) -> RemoteOptions {
1333
match self .remote_for_url ( url) {
1334
Some ( name) => self .remote_options ( & name) ,
1335
None => RemoteOptions {
1336
repo_id: self .id ( ) .ok ( ) ,
ff1f62fe Andy McCall 1 month ago ↶
1337
.. self .credentials_for ( url)
d82e4409 Andy McCall 1 month ago ↶
1338
} ,
1339
}
1340
}
1341
98286406 Andy McCall 1 month ago ↶
1342
fn open_named_remote ( & self , name : & str ) -> Result< Box< dyn crate:: protocol:: Remote> > {
2b5ee371 Andy McCall 1 month ago ↶
1343
open_remote ( & self .remote_url ( name) ? , & self .remote_options ( name) )
98286406 Andy McCall 1 month ago ↶
1344
}
1345
1346
fn serial_path ( & self , remote : & str , bookmark : & str ) -> PathBuf {
75167672 Andy McCall 1 month ago
1347
self .oxy
1348
.join ( " remotes" )
1349
.join ( remote)
1350
.join ( format! ( " {bookmark} .serial" ) )
98286406 Andy McCall 1 month ago ↶
1351
}
1352
1353
fn last_seen_serial ( & self , remote : & str , bookmark : & str ) -> Option< u64 > {
75167672 Andy McCall 1 month ago
1354
fs:: read_to_string( self .serial_path ( remote, bookmark) )
1355
.ok ( )
1356
.and_then ( | s | s.trim ( ) .parse ( ) .ok ( ) )
98286406 Andy McCall 1 month ago ↶
1357
}
1358
1359
fn record_serial ( & self , remote : & str , bookmark : & str , serial : u64 ) -> Result< ( ) > {
1360
let p = self .serial_path ( remote, bookmark) ;
1361
fs:: create_dir_all( p.parent ( ) .unwrap ( ) ) ? ;
1362
write_atomic ( & p, serial.to_string ( ) .as_bytes ( ) )
1363
}
1364
83e0677b Andy McCall 1 month ago ↶
1365
fn policy_serial_path ( & self , remote : & str ) -> PathBuf {
1366
self .oxy.join ( " remotes" ) .join ( remote) .join ( " policy.serial" )
1367
}
1368
1369
1370
fn remote_policy_serial ( & self , remote : & str ) -> Option< u64 > {
75167672 Andy McCall 1 month ago
1371
fs:: read_to_string( self .policy_serial_path ( remote) )
1372
.ok ( )
1373
.and_then ( | s | s.trim ( ) .parse ( ) .ok ( ) )
83e0677b Andy McCall 1 month ago ↶
1374
}
1375
1376
fn record_policy_serial ( & self , remote : & str , serial : u64 ) -> Result< ( ) > {
1377
let p = self .policy_serial_path ( remote) ;
1378
fs:: create_dir_all( p.parent ( ) .unwrap ( ) ) ? ;
1379
write_atomic ( & p, serial.to_string ( ) .as_bytes ( ) )
1380
}
1381
b3c5a59a Andy McCall 1 month ago ↶
1382
fn lazy_dir ( & self ) -> PathBuf {
1383
self .oxy.join ( " lazy" )
1384
}
1385
1386
1387
fn register_lazy ( & self , blob : & ObjectRef, remote : & str ) -> Result< ( ) > {
1388
fs:: create_dir_all( self .lazy_dir ( ) ) ? ;
1389
write_atomic ( & self .lazy_dir ( ) .join ( blob.to_hex ( ) ) , remote.as_bytes ( ) )
1390
}
1391
1392
1393
pub fn lazy_remote ( & self , blob : & ObjectRef) -> Option< String> {
75167672 Andy McCall 1 month ago
1394
fs:: read_to_string( self .lazy_dir ( ) .join ( blob.to_hex ( ) ) )
1395
.ok ( )
1396
.map ( | s | s.trim ( ) .to_string ( ) )
b3c5a59a Andy McCall 1 month ago ↶
1397
}
1398
1399
1400
pub ( crate ) fn fetch_lazy_blob ( & self , blob : & ObjectRef) -> Result< ( ) > {
1401
let Some ( remote_name) = self .lazy_remote ( blob) else {
1402
return Err ( RepoError:: Store( StoreError:: NotFound( * blob) ) ) ;
1403
} ;
1404
let remote = self .open_named_remote ( & remote_name) ? ;
1405
let objects = remote.objects ( & [ * blob] ) ? ;
1406
for ( ty, body) in objects {
1407
if matches! ( ty, ObjectType:: Blob | ObjectType:: Chunk) {
1408
self .store.put ( ty, & body) ? ;
1409
}
1410
}
1411
if ! self .store.has ( blob) {
75167672 Andy McCall 1 month ago
1412
return fail ( format! (
1413
" {remote_name} did not send blob {} " ,
1414
blob.short ( 12 )
1415
) ) ;
b3c5a59a Andy McCall 1 month ago ↶
1416
}
1417
let _ = fs:: remove_file( self .lazy_dir ( ) .join ( blob.to_hex ( ) ) ) ;
1418
Ok ( ( ) )
1419
}
1420
1421
1422
1423
fn register_missing_blobs ( & self , changes : & [ChangeHash], remote : & str ) -> Result< usize > {
1424
let mut n = 0 ;
1425
for h in changes {
1426
let c = self .store.get_change ( h) ? ;
1427
for e in & c.edits {
1428
if let Edit:: Replace { old, new, .. } = e {
1429
for b in [ old, new] .into_iter ( ) .flatten ( ) {
1430
if ! self .store.has ( b) {
1431
self .register_lazy ( b, remote) ? ;
1432
n + = 1 ;
1433
}
1434
}
1435
}
1436
}
1437
}
1438
Ok ( n)
1439
}
1440
98286406 Andy McCall 1 month ago ↶
1441
d82e4409 Andy McCall 1 month ago ↶
1442
pub fn known_heads ( & self , view : & View) -> Result< Vec< ChangeHash> > {
98286406 Andy McCall 1 month ago ↶
1443
let mut have: BTreeSet< ChangeHash> = BTreeSet:: new( ) ;
75167672 Andy McCall 1 month ago
1444
for st in view
1445
.bookmarks
1446
.values ( )
1447
.chain ( view.remotes.values ( ) .flat_map ( | b | b.values ( ) ) )
1448
{
98286406 Andy McCall 1 month ago ↶
1449
if let Ok ( s) = self .store.get_state ( st) {
1450
have.extend ( s.heads) ;
1451
}
1452
}
1453
have.extend ( view.workspaces.values ( ) .copied ( ) ) ;
1454
Ok ( have.into_iter ( ) .collect ( ) )
1455
}
1456
1457
83e0677b Andy McCall 1 month ago ↶
1458
pub fn fetch ( & self , remote_name : & str ) -> Result< FetchReport> {
98286406 Andy McCall 1 month ago ↶
1459
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
1460
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
83e0677b Andy McCall 1 month ago ↶
1461
let resp = remote.refs ( ) ? ;
1462
1463
1464
1465
1466
1467
let local_policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy ) ;
1468
let seen = self .remote_policy_serial ( remote_name) ;
1469
let mut unverified = false ;
1470
match & resp.policy {
1471
Some ( sp) => {
1472
if seen.is_some_and ( | s | sp.policy.serial < s ) {
1473
return fail ( format! ( " {remote_name} presents policy serial {} but presented {} before: refusing the downgrade" , sp.policy.serial, seen.unwrap ( ) ) ) ;
1474
}
1475
match & local_policy {
1476
Some ( l) if sp.policy.serial < l.serial => { }
1477
Some ( l) if sp.policy.serial = = l.serial => {
1478
if sp.policy.hash ( ) ! = l.hash ( ) {
75167672 Andy McCall 1 month ago
1479
return fail ( format! (
1480
" {remote_name} presents a different policy with the same serial {} " ,
1481
l.serial
1482
) ) ;
83e0677b Andy McCall 1 month ago ↶
1483
}
1484
}
28fc23d7 Andy McCall 1 month ago ↶
1485
_ => {
75167672 Andy McCall 1 month ago
1486
self .adopt_policy_chain (
1487
& resp.history,
1488
sp,
1489
& format! ( " oxy fetch {remote_name} " ) ,
1490
)
1491
.map_err ( | e | RepoError:: Msg( format! ( " policy from {remote_name} : {e} " ) ) ) ? ;
83e0677b Andy McCall 1 month ago ↶
1492
}
1493
}
1494
self .record_policy_serial ( remote_name, sp.policy.serial) ? ;
1495
}
1496
None => {
1497
if seen.is_some ( ) {
1498
return fail ( format! ( " {remote_name} presented a policy before but has none now: refusing the downgrade" ) ) ;
1499
}
1500
unverified = true ;
1501
}
1502
}
75167672 Andy McCall 1 month ago
1503
let policy = if unverified {
1504
None
1505
} else {
1506
self .current_policy ( ) ? .map ( | ( _, sp ) | sp.policy )
1507
} ;
98286406 Andy McCall 1 month ago ↶
1508
let ( op_ref, op) = self .head_op ( ) ? ;
1509
let mut view = op.view.clone ( ) ;
1510
let have = self .known_heads ( & view) ? ;
1511
let mut report = Vec :: new( ) ;
b3c5a59a Andy McCall 1 month ago ↶
1512
let max_blob = self .config.max_blob;
83e0677b Andy McCall 1 month ago ↶
1513
for r in resp.refs {
091fef80 Andy McCall 1 month ago ↶
1514
let needed = {
1515
let _t = crate :: timing:: phase( " fetch: negotiate" ) ;
1516
remote.negotiate ( & have, & r.heads) ?
1517
} ;
1518
let bundle = {
1519
let _t = crate :: timing:: phase( " fetch: bundle" ) ;
1520
remote.fetch ( & needed, max_blob) ?
1521
} ;
83e0677b Andy McCall 1 month ago ↶
1522
1523
1524
091fef80 Andy McCall 1 month ago ↶
1525
let imported = {
1526
let _t = crate :: timing:: phase( " fetch: import" ) ;
1527
self .import_bundle ( & bundle, false ) ?
1528
} ;
b3c5a59a Andy McCall 1 month ago ↶
1529
if max_blob.is_some ( ) {
1530
self .register_missing_blobs ( & imported, remote_name) ? ;
1531
}
98286406 Andy McCall 1 month ago ↶
1532
for h in & r.heads {
1533
if ! self .store.has ( h) {
1534
return fail ( format! ( " remote did not send head {} " , h.short ( 12 ) ) ) ;
1535
}
1536
}
1537
let state = self .store.put_state ( & StateObject:: new( r.heads.clone ( ) ) ) ? ;
1538
if state ! = r.state {
75167672 Andy McCall 1 month ago
1539
return fail ( format! (
1540
" remote state {} does not match its heads" ,
1541
r.state.short ( 12 )
1542
) ) ;
98286406 Andy McCall 1 month ago ↶
1543
}
83e0677b Andy McCall 1 month ago ↶
1544
if let Some ( p) = & policy {
1545
let Some ( sa) = & r.attestation else {
75167672 Andy McCall 1 month ago
1546
return fail ( format! (
1547
" {remote_name} /{} has no attestation, which its policy requires" ,
1548
r.name
1549
) ) ;
83e0677b Andy McCall 1 month ago ↶
1550
} ;
75167672 Andy McCall 1 month ago
1551
let signer = sa
1552
.sigs
1553
.first ( )
1554
.map ( | s | s.key )
1555
.ok_or_else ( | | RepoError:: Msg( " attestation carries no signature" .into ( ) ) ) ? ;
f53cbe7a Andy McCall 18 days ago ↶
1556
1557
1558
let at = self .certified_time ( & signer, sa, r.accepted.as_ref ( ) ) ;
7ba155f2 Andy McCall 1 month ago ↶
1559
if ! p.authorizes ( & signer, & r.name)
1560
& & ! self .delegate_authorized ( p, & signer, & r.name, sa.attest.hlc.wall_ms)
f53cbe7a Andy McCall 18 days ago ↶
1561
& & ! self .certified_authorized ( p, & signer, & r.name, Scope:: REFS , at)
3526d339 Andy McCall 1 month ago ↶
1562
{
75167672 Andy McCall 1 month ago
1563
return fail ( format! (
3e5772cc Andy McCall 1 month ago ↶
1564
" {remote_name} /{} : attested by {} , which the policy does not authorize for this bookmark; a policy owner can grant it with `oxy policy grant`" ,
75167672 Andy McCall 1 month ago
1565
r.name,
1566
signer.short ( 12 )
1567
) ) ;
83e0677b Andy McCall 1 month ago ↶
1568
}
1569
}
98286406 Andy McCall 1 month ago ↶
1570
if let Some ( sa) = & r.attestation {
75167672 Andy McCall 1 month ago
1571
let AttestBody:: Ref {
1572
old_state,
1573
new_state,
1574
..
1575
} = & sa.attest.body
1576
else {
98286406 Andy McCall 1 month ago ↶
1577
return fail ( " remote sent a non-ref attestation for a bookmark" ) ;
1578
} ;
1579
let ( old_state, new_state) = ( * old_state, * new_state) ;
1580
let serial = self
75167672 Andy McCall 1 month ago
1581
.check_ref_attest (
1582
sa,
1583
& r.name,
1584
& old_state,
1585
& new_state,
1586
self .last_seen_serial ( remote_name, & r.name) ,
1587
true ,
1588
)
1589
.map_err ( | e | {
1590
RepoError:: Msg( format! ( " attestation for {} /{} : {e} " , remote_name, r.name) )
1591
} ) ? ;
98286406 Andy McCall 1 month ago ↶
1592
if new_state ! = r.state {
75167672 Andy McCall 1 month ago
1593
return fail ( format! (
1594
" attestation for {} /{} does not cover its current state" ,
1595
remote_name, r.name
1596
) ) ;
98286406 Andy McCall 1 month ago ↶
1597
}
1598
let ar = self .store.put_attest ( & sa.attest) ? ;
1599
for sig in & sa.sigs {
1600
self .store.put_signature ( & ar, sig) ? ;
1601
}
1602
self .record_serial ( remote_name, & r.name, serial) ? ;
1603
}
75167672 Andy McCall 1 month ago
1604
view.remotes
1605
.entry ( remote_name.to_string ( ) )
1606
.or_default ( )
1607
.insert ( r.name.clone ( ) , state) ;
98286406 Andy McCall 1 month ago ↶
1608
report.push ( ( r.name, r.heads, imported.len ( ) ) ) ;
1609
}
132b884b Andy McCall 1 month ago ↶
1610
let mut new_tags = 0 ;
1611
for t in & resp.tags {
1612
if view.tags.contains_key ( & t.name) {
1613
continue ;
1614
}
1615
75167672 Andy McCall 1 month ago
1616
let missing: Vec< ChangeHash> = t
1617
.heads
1618
.iter ( )
1619
.filter ( | h | ! self .store.has ( h) )
1620
.copied ( )
1621
.collect ( ) ;
132b884b Andy McCall 1 month ago ↶
1622
if ! missing.is_empty ( ) {
1623
let needed = remote.negotiate ( & self .known_heads ( & view) ? , & missing) ? ;
1624
let bundle = remote.fetch ( & needed, max_blob) ? ;
1625
self .import_bundle ( & bundle, false ) ? ;
1626
}
1627
self .store.put_state ( & StateObject:: new( t.heads.clone ( ) ) ) ? ;
75167672 Andy McCall 1 month ago
1628
self .adopt_tag ( & mut view, t, policy.as_ref ( ) )
1629
.map_err ( | e | RepoError:: Msg( format! ( " {remote_name} {} : {e} " , t.name) ) ) ? ;
132b884b Andy McCall 1 month ago ↶
1630
new_tags + = 1 ;
1631
}
98286406 Andy McCall 1 month ago ↶
1632
if view ! = op.view {
1633
self .record_op ( & [ op_ref] , & format! ( " oxy fetch {remote_name} " ) , view) ? ;
1634
}
6d3eefbc Andy McCall 1 month ago ↶
1635
let _ = self .run_hook (
1636
" post-fetch" ,
1637
& format! (
1638
" {{ \" event\" :\" post-fetch\" ,\" remote\" :{} ,\" bookmarks\" :{} }} " ,
1639
json_str ( remote_name) ,
75167672 Andy McCall 1 month ago
1640
json_array ( report.iter ( ) .map ( | ( n , heads , _) | format! (
1641
" {{ \" name\" :{} ,\" heads\" :{} }} " ,
1642
json_str ( n) ,
1643
json_array ( heads.iter ( ) .map ( | h | json_str ( & h.to_hex ( ) ) ) )
1644
) ) )
6d3eefbc Andy McCall 1 month ago ↶
1645
) ,
1646
) ;
75167672 Andy McCall 1 month ago
1647
Ok ( FetchReport {
1648
refs: report,
1649
unverified,
1650
new_tags,
1651
} )
98286406 Andy McCall 1 month ago ↶
1652
}
1653
2863456a Andy McCall 1 month ago ↶
1654
1655
1656
pub fn fetch_shallow ( & self , remote_name : & str , depth : u32 ) -> Result< usize > {
1657
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
1658
self .check_remote_id ( remote.as_ref ( ) , remote_name, false ) ? ;
2863456a Andy McCall 1 month ago ↶
1659
let resp = remote.refs ( ) ? ;
1660
let bookmark = self .config.bookmark.clone ( ) ;
1661
if let Some ( sp) = & resp.policy {
75167672 Andy McCall 1 month ago
1662
self .adopt_policy_chain ( & resp.history, sp, " oxy clone --depth" )
1663
.map_err ( | e | RepoError:: Msg( format! ( " policy from {remote_name} : {e} " ) ) ) ? ;
2863456a Andy McCall 1 month ago ↶
1664
self .record_policy_serial ( remote_name, sp.policy.serial) ? ;
1665
}
1666
let Some ( r) = resp.refs.into_iter ( ) .find ( | r | r.name = = bookmark ) else {
1667
return fail ( format! ( " {remote_name} has no bookmark {bookmark:?} " ) ) ;
1668
} ;
1669
if r.heads.is_empty ( ) {
1670
return Ok ( 0 ) ;
1671
}
1672
1673
let cp_bundle = remote.checkpoint ( & r.heads) ? ;
1674
let mut cp_ref = None ;
1675
for ( ty, body) in & cp_bundle.objects {
1676
let put = self .store.put ( * ty, body) ? ;
1677
if * ty = = ObjectType:: Checkpoint {
1678
cp_ref = Some ( put) ;
1679
}
1680
}
75167672 Andy McCall 1 month ago
1681
let Some ( cp_ref) = cp_ref else {
1682
return fail ( format! ( " {remote_name} sent no checkpoint" ) ) ;
1683
} ;
2863456a Andy McCall 1 month ago ↶
1684
self .install_checkpoint ( & bookmark, & cp_ref) ? ;
1685
1686
let wanted = remote.shallow ( & r.heads, depth) ? ;
1687
let bundle = remote.fetch ( & wanted, self .config.max_blob) ? ;
1688
let imported = self .import_bundle_shallow ( & bundle, false ) ? ;
1689
if self .config.max_blob.is_some ( ) {
1690
self .register_missing_blobs ( & imported, remote_name) ? ;
1691
}
1692
self .refresh_boundary ( & imported) ? ;
1693
if let Some ( sa) = & r.attestation {
1694
let ar = self .store.put_attest ( & sa.attest) ? ;
1695
for sig in & sa.sigs {
1696
self .store.put_signature ( & ar, sig) ? ;
1697
}
1698
if let AttestBody:: Ref { serial, .. } = sa.attest.body {
1699
self .record_serial ( remote_name, & r.name, serial) ? ;
1700
}
1701
}
1702
let state = self .store.put_state ( & StateObject:: new( r.heads.clone ( ) ) ) ? ;
1703
let ( op_ref, op) = self .head_op ( ) ? ;
1704
let mut view = op.view.clone ( ) ;
75167672 Andy McCall 1 month ago
1705
view.remotes
1706
.entry ( remote_name.to_string ( ) )
1707
.or_default ( )
1708
.insert ( r.name.clone ( ) , state) ;
1709
self .record_op (
1710
& [ op_ref] ,
1711
& format! ( " oxy fetch {remote_name} --depth {depth} " ) ,
1712
view,
1713
) ? ;
2863456a Andy McCall 1 month ago ↶
1714
Ok ( imported.len ( ) )
1715
}
1716
98286406 Andy McCall 1 month ago ↶
1717
1718
1719
pub fn pull ( & self , remote_name : & str ) -> Result< PullReport> {
6caacfff Andy McCall 1 month ago ↶
1720
let _wc = self .lock_working_copy ( ) ? ;
98286406 Andy McCall 1 month ago ↶
1721
self .fetch ( remote_name) ? ;
1722
let ( op_ref, op) = self .head_op ( ) ? ;
14edbaaf Andy McCall 1 month ago ↶
1723
let current = self .config.bookmark.clone ( ) ;
1724
let mut local_current = self .heads_of ( & op.view, & current) ? ;
1725
local_current.sort ( ) ;
1726
let mut view = op.view.clone ( ) ;
1727
let mut current_heads = local_current.clone ( ) ;
1728
let mut changed = false ;
1729
let mut others = Vec :: new( ) ;
1730
let tracked = op
75167672 Andy McCall 1 month ago
1731
.view
1732
.remotes
1733
.get ( remote_name)
14edbaaf Andy McCall 1 month ago ↶
1734
.cloned ( )
1735
.unwrap_or_default ( ) ;
1736
for ( name, remote_state) in tracked {
1737
1738
1739
let local_state = op.view.bookmarks.get ( & name) .copied ( ) ;
1740
if local_state.is_none ( ) & & name ! = current {
1741
continue ;
1742
}
1743
if local_state = = Some ( remote_state) {
1744
continue ;
1745
}
1746
let local = self .heads_of ( & op.view, & name) ? ;
1747
let remote_heads = self .store.get_state ( & remote_state) ? .heads;
1748
if remote_heads.iter ( ) .all ( | h | local.contains ( h) ) {
1749
continue ;
1750
}
1751
let mut all = local.clone ( ) ;
1752
all.extend ( remote_heads) ;
1753
let st = self .load_state_history ( & all) ? ;
1754
let mut heads = st.maximal ( & all) ;
1755
heads.sort ( ) ;
1756
let mut sorted_local = local;
1757
sorted_local.sort ( ) ;
1758
if heads = = sorted_local {
1759
continue ;
1760
}
1761
view.bookmarks.insert (
1762
name.clone ( ) ,
1763
self .store.put_state ( & StateObject:: new( heads.clone ( ) ) ) ? ,
1764
) ;
1765
if name = = current {
1766
changed = true ;
1767
current_heads = heads;
1768
} else {
1769
others.push ( name) ;
1770
}
1771
}
f6dccc45 Andy McCall 26 days ago ↶
1772
if changed | | ! others.is_empty ( ) {
1773
self .record_op ( & [ op_ref] , & format! ( " oxy pull {remote_name} " ) , view) ? ;
98286406 Andy McCall 1 month ago ↶
1774
}
f6dccc45 Andy McCall 26 days ago ↶
1775
1776
1777
1778
let checked_out = self .follow_bookmark ( & current_heads, & current, " oxy pull" ) ? ;
75167672 Andy McCall 1 month ago
1779
Ok ( PullReport {
f6dccc45 Andy McCall 26 days ago ↶
1780
heads: if changed {
1781
current_heads
1782
} else {
1783
local_current
1784
} ,
14edbaaf Andy McCall 1 month ago ↶
1785
changed,
75167672 Andy McCall 1 month ago
1786
checked_out,
14edbaaf Andy McCall 1 month ago ↶
1787
others,
75167672 Andy McCall 1 month ago
1788
} )
f6dccc45 Andy McCall 26 days ago ↶
1789
}
1790
1791
1792
1793
1794
1795
1796
pub fn working_copy_behind ( & self ) -> Result< Option< usize > > {
1797
let ( _ , wc) = self .working_copy ( ) ? ;
1798
let mut base = wc.deps.clone ( ) ;
1799
base.sort ( ) ;
1800
let mut heads = self .bookmark_heads ( ) ? ;
1801
heads.sort ( ) ;
1802
if base = = heads | | heads.is_empty ( ) {
1803
return Ok ( None ) ;
1804
}
1805
let state = self .load_state_history ( & heads) ? ;
1806
if ! base.iter ( ) .all ( | h | state.contains ( h) ) {
1807
return Ok ( None ) ;
1808
}
1809
let had = if base.is_empty ( ) {
1810
0
1811
} else {
1812
self .load_state_history ( & base) ? .len ( )
1813
} ;
1814
Ok ( Some ( state.len ( ) .saturating_sub ( had) ) .filter ( | n | * n > 0 ) )
1815
}
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
pub fn follow_bookmark (
1827
& self ,
1828
heads : & [ChangeHash],
1829
bookmark : & str ,
1830
command : & str ,
1831
) -> Result< bool > {
1832
let snap = self .snapshot ( command) ? ;
1833
let clean = snap.wc.tree_ops.is_empty ( ) & & snap.wc.edits.is_empty ( ) ;
1834
let mut base = snap.base.maximal ( & snap.wc.deps) ;
1835
base.sort ( ) ;
1836
let mut target = heads.to_vec ( ) ;
1837
target.sort ( ) ;
1838
if base = = target {
1839
return Ok ( false ) ;
1840
}
1841
let state = self .load_state_history ( heads) ? ;
1842
if ! base.iter ( ) .all ( | h | state.contains ( h) ) {
1843
1844
return Ok ( false ) ;
1845
}
1846
if clean {
1847
self .checkout ( heads, false , & format! ( " {command} (checkout)" ) ) ? ;
1848
Ok ( true )
1849
} else {
1850
Ok ( self
1851
.checkout_carry ( heads, bookmark, & format! ( " {command} (checkout --carry)" ) )
1852
.is_ok ( ) )
1853
}
98286406 Andy McCall 1 month ago ↶
1854
}
1855
1856
1857
f8c69576 Andy McCall 1 month ago ↶
1858
1859
1860
1861
pub fn push_delete ( & self , remote_name : & str , bookmark : & str ) -> Result< Vec< RefResult> > {
1862
let remote = self .open_named_remote ( remote_name) ? ;
1863
self .check_remote_id ( remote.as_ref ( ) , remote_name, true ) ? ;
1864
let resp = remote.refs ( ) ? ;
1865
let Some ( r) = resp.refs.iter ( ) .find ( | r | r.name = = bookmark ) else {
1866
return fail ( format! ( " {remote_name} has no bookmark {bookmark} " ) ) ;
1867
} ;
1868
if resp.policy.is_some ( ) & & self .secret_key ( ) ? .is_none ( ) {
1869
return fail ( format! ( " {remote_name} enforces a trust policy; deleting needs an identity (`oxy identity create`)" ) ) ;
1870
}
1871
let attestation = match self .secret_key ( ) ? {
1872
Some ( _ ) => Some ( self .sign_attest ( & Attest {
1873
repo: self .id ( ) ? ,
1874
hlc: Hlc:: now( ) ,
1875
body: AttestBody:: Ref {
1876
name: bookmark.to_string ( ) ,
1877
old_state: r.state,
1878
new_state: ObjectRef:: ZERO ,
1879
serial: r.last_serial.unwrap_or ( 0 ) + 1 ,
1880
} ,
1881
} ) ? ) ,
1882
None => None ,
1883
} ;
1884
let results = remote.push (
1885
Bundle:: default( ) ,
1886
vec! [ RefUpdate {
1887
name: bookmark.to_string ( ) ,
1888
old_state: r.state,
1889
new_state: ObjectRef:: ZERO ,
1890
force: true ,
1891
attestation,
1892
} ] ,
1893
None ,
1894
vec! [ ] ,
1895
) ? ;
1896
if results.iter ( ) .any ( | x | x.ok & & x.name = = bookmark ) {
1897
let ( op_ref, op) = self .head_op ( ) ? ;
1898
let mut view = op.view.clone ( ) ;
1899
if let Some ( m) = view.remotes.get_mut ( remote_name) {
1900
m.remove ( bookmark) ;
1901
}
1902
self .record_op (
1903
& [ op_ref] ,
1904
& format! ( " oxy push {remote_name} --delete {bookmark} " ) ,
1905
view,
1906
) ? ;
1907
}
1908
Ok ( results)
1909
}
1910
34fad4a9 Andy McCall 25 days ago ↶
1911
1912
1913
1914
1915
pub fn remove_remote ( & mut self , name : & str ) -> Result< ( ) > {
1916
if ! self .config.remotes.contains_key ( name) {
1917
return fail ( format! ( " no remote {name} : `oxy remote list` shows them" ) ) ;
1918
}
1919
let prefix = format! ( " remote.{name} ." ) ;
1920
let keys: Vec< String> = self
1921
.config
1922
.repo_layer ( )
1923
.keys ( )
1924
.filter ( | k | k.starts_with ( & prefix) )
1925
.cloned ( )
1926
.collect ( ) ;
1927
for k in keys {
1928
self .config.set ( & k, " " ) ? ;
1929
}
1930
self .config.remotes.remove ( name) ;
1931
self .save_config ( ) ? ;
1932
let ( op_ref, op) = self .head_op ( ) ? ;
1933
if op.view.remotes.contains_key ( name) {
1934
let mut view = op.view.clone ( ) ;
1935
view.remotes.remove ( name) ;
1936
self .record_op ( & [ op_ref] , & format! ( " oxy remote remove {name} " ) , view) ? ;
1937
}
1938
let mirror = self .oxy.join ( " remotes" ) .join ( name) ;
1939
if mirror.is_dir ( ) {
1940
fs:: remove_dir_all( & mirror) ? ;
1941
}
1942
Ok ( ( ) )
1943
}
1944
cb8b8082 Andy McCall 26 days ago ↶
1945
1946
1947
1948
fn push_batches ( & self , changes : & [ChangeHash]) -> Result< Vec< Vec< ChangeHash> > > {
1949
let blob_len = | r : & ObjectRef| -> u64 {
1950
self .store
1951
.get_typed ( r, ObjectType:: Blob)
1952
.ok ( )
1953
.and_then ( | b | Blob:: decode( & b) .ok ( ) )
1954
.map_or ( 0 , | b | b.length )
1955
} ;
1956
let ( mut out, mut batch, mut bytes) = ( Vec :: new( ) , Vec :: new( ) , 0 u64 ) ;
1957
for h in changes {
1958
let raw = self .store.get_typed ( h, ObjectType:: Change) ? ;
1959
let ( c, texts) = Change:: decode_skeleton( & raw, * h) ? ;
1960
let mut weight = raw.len ( ) as u64 ;
1961
for t in & texts {
1962
if let crate :: objects:: TextRef:: Blob( r, _ , _ ) = t {
1963
weight + = blob_len ( r) ;
1964
}
1965
}
1966
for e in & c.edits {
1967
if let Edit:: Replace { new: Some ( b) , .. } = e {
1968
weight + = blob_len ( b) ;
1969
}
1970
}
1971
if ! batch.is_empty ( ) & & bytes + weight > self .config.push_batch_bytes {
1972
out.push ( std:: mem:: take( & mut batch) ) ;
1973
bytes = 0 ;
1974
}
1975
batch.push ( * h) ;
1976
bytes + = weight;
1977
}
1978
out.push ( batch) ;
1979
Ok ( out)
1980
}
1981
98286406 Andy McCall 1 month ago ↶
1982
pub fn push ( & self , remote_name : & str , force : bool ) -> Result< Vec< RefResult> > {
6d3eefbc Andy McCall 1 month ago ↶
1983
let bookmark = self .config.bookmark.clone ( ) ;
7b9809e6 Andy McCall 1 month ago ↶
1984
self .push_bookmark ( remote_name, & bookmark, force)
1985
}
1986
1987
75167672 Andy McCall 1 month ago
1988
pub fn push_bookmark (
cb8b8082 Andy McCall 26 days ago ↶
1989
& self ,
1990
remote_name : & str ,
1991
bookmark : & str ,
1992
force : bool ,
1993
) -> Result< Vec< RefResult> > {
1994
self .push_bookmark_with ( remote_name, bookmark, force, & mut | _ | { } )
1995
}
1996
1997
1998
1999
pub fn push_bookmark_with (
75167672 Andy McCall 1 month ago
2000
& self ,
2001
remote_name : & str ,
2002
bookmark : & str ,
2003
force : bool ,
cb8b8082 Andy McCall 26 days ago ↶
2004
progress : crate:: store:: Progress,
75167672 Andy McCall 1 month ago
2005
) -> Result< Vec< RefResult> > {
7b9809e6 Andy McCall 1 month ago ↶
2006
let ( op_ref, op) = self .head_op ( ) ? ;
2007
if ! op.view.bookmarks.contains_key ( bookmark) {
2008
return fail ( format! ( " no bookmark {bookmark} " ) ) ;
2009
}
2010
let bookmark = bookmark.to_string ( ) ;
6d3eefbc Andy McCall 1 month ago ↶
2011
let local_heads = self .heads_of ( & op.view, & bookmark) ? ;
2012
2013
self .run_hook (
2014
" pre-push" ,
75167672 Andy McCall 1 month ago
2015
& format! (
2016
" {{ \" event\" :\" pre-push\" ,\" remote\" :{} ,\" bookmark\" :{} ,\" heads\" :{} }} " ,
2017
json_str ( remote_name) ,
2018
json_str ( & bookmark) ,
2019
json_array ( local_heads.iter ( ) .map ( | h | json_str ( & h.to_hex ( ) ) ) )
2020
) ,
6d3eefbc Andy McCall 1 month ago ↶
2021
) ? ;
98286406 Andy McCall 1 month ago ↶
2022
let remote = self .open_named_remote ( remote_name) ? ;
75167672 Andy McCall 1 month ago
2023
self .check_remote_id ( remote.as_ref ( ) , remote_name, true ) ? ;
83e0677b Andy McCall 1 month ago ↶
2024
let resp = remote.refs ( ) ? ;
2025
let local_policy = self .current_policy ( ) ? .map ( | ( _, sp ) | sp ) ;
28fc23d7 Andy McCall 1 month ago ↶
2026
let resp_policy_serial = resp.policy.as_ref ( ) .map ( | p | p.policy.serial ) ;
83e0677b Andy McCall 1 month ago ↶
2027
let policy_to_send = match ( & local_policy, & resp.policy) {
2028
( Some ( l) , Some ( r) ) if l.policy.serial > r.policy.serial => Some ( l.clone ( ) ) ,
2029
( Some ( l) , None ) => Some ( l.clone ( ) ) ,
2030
( Some ( l) , Some ( r) ) if l.policy.serial < r.policy.serial => {
75167672 Andy McCall 1 month ago
2031
return fail ( format! (
2032
" {remote_name} has a newer policy (serial {} ); fetch first" ,
2033
r.policy.serial
2034
) ) ;
83e0677b Andy McCall 1 month ago ↶
2035
}
2036
_ => None ,
2037
} ;
2038
let remote_has_policy = resp.policy.is_some ( ) | | policy_to_send.is_some ( ) ;
2039
if remote_has_policy & & self .secret_key ( ) ? .is_none ( ) {
2040
return fail ( format! ( " {remote_name} enforces a trust policy; pushing needs an identity (`oxy identity create`)" ) ) ;
2041
}
132b884b Andy McCall 1 month ago ↶
2042
let remote_tags: BTreeSet< String> = resp.tags.iter ( ) .map ( | t | t.name.clone ( ) ) .collect ( ) ;
75167672 Andy McCall 1 month ago
2043
let tags_to_send: Vec< crate:: protocol:: TagRef> = self
2044
.tag_refs ( ) ?
2045
.into_iter ( )
2046
.filter ( | t | ! remote_tags.contains ( & t.name) )
2047
.collect ( ) ;
83e0677b Andy McCall 1 month ago ↶
2048
let refs = resp.refs;
98286406 Andy McCall 1 month ago ↶
2049
let empty = self .store.put_state ( & StateObject:: new( vec! [ ] ) ) ? ;
75167672 Andy McCall 1 month ago
2050
let ( remote_state, remote_heads, last_serial) =
2051
match refs.iter ( ) .find ( | r | r.name = = bookmark ) {
2052
Some ( r) => ( r.state, r.heads.clone ( ) , r.last_serial) ,
2053
None => ( empty, vec! [ ] , None ) ,
2054
} ;
2055
let new_state = self
2056
.store
2057
.put_state ( & StateObject:: new( local_heads.clone ( ) ) ) ? ;
83e0677b Andy McCall 1 month ago ↶
2058
2059
2060
2061
132b884b Andy McCall 1 month ago ↶
2062
if new_state = = remote_state & & policy_to_send.is_none ( ) & & tags_to_send.is_empty ( ) {
75167672 Andy McCall 1 month ago
2063
return Ok ( vec! [ RefResult {
2064
name: bookmark,
2065
ok: true ,
2066
reason: " already up to date" .into ( ) ,
2067
} ] ) ;
98286406 Andy McCall 1 month ago ↶
2068
}
2069
for h in & remote_heads {
2070
if ! self .store.has ( h) {
75167672 Andy McCall 1 month ago
2071
return fail ( format! (
3e5772cc Andy McCall 1 month ago ↶
2072
" the remote has change {} which you do not; pull first (`oxy pull {remote_name} `), then push again" ,
75167672 Andy McCall 1 month ago
2073
h.short ( 12 )
2074
) ) ;
98286406 Andy McCall 1 month ago ↶
2075
}
2076
}
2077
if ! force {
2078
let closure = self .closure_in_store ( & local_heads) ? ;
2079
if let Some ( h) = remote_heads.iter ( ) .find ( | h | ! closure.contains ( h) ) {
2080
return fail ( format! ( " not a fast-forward: remote head {} is not in your state; pull first or use --force" , h.short ( 12 ) ) ) ;
2081
}
2082
}
2083
let to_send = self .negotiate ( & remote_heads, & local_heads) ? ;
2084
let key = self .secret_key ( ) ? .map ( | ( k , _) | k ) ;
2085
if let Some ( key) = key {
05e71ed6 Andy McCall 1 month ago ↶
2086
cb8b8082 Andy McCall 26 days ago ↶
2087
2088
98286406 Andy McCall 1 month ago ↶
2089
let mut unsigned = Vec :: new( ) ;
2090
for h in & to_send {
cb8b8082 Andy McCall 26 days ago ↶
2091
let c = self .store.get_change_meta ( h) ? ;
a2387d54 Andy McCall 18 days ago ↶
2092
if self .seals_for_author ( & c.author, & key)
75167672 Andy McCall 1 month ago
2093
& & ! matches! ( self .authenticate_change ( h) ? , SignatureStatus:: Valid { .. } )
2094
{
98286406 Andy McCall 1 month ago ↶
2095
unsigned.push ( * h) ;
2096
}
2097
}
2098
for chunk in unsigned.chunks ( crate :: attest:: MAX_SEAL_HASHES ) {
cb8b8082 Andy McCall 26 days ago ↶
2099
self .sign_attest ( & Attest:: seal( self .id ( ) ? , chunk.to_vec ( ) ) ) ? ;
98286406 Andy McCall 1 month ago ↶
2100
}
cb8b8082 Andy McCall 26 days ago ↶
2101
}
2102
2103
2104
2105
2106
let batches = self .push_batches ( & to_send) ? ;
2107
let ( last, earlier) = batches.split_last ( ) .expect ( " at least one batch" ) ;
2108
for ( i, batch) in earlier.iter ( ) .enumerate ( ) {
2109
progress ( & format! (
2110
" part {} of {} : {} changes" ,
2111
i + 1 ,
2112
batches.len ( ) ,
2113
batch.len ( )
2114
) ) ;
2115
let results = remote.push ( self .bundle_for ( batch) ? , vec! [ ] , None , vec! [ ] ) ? ;
2116
match results.iter ( ) .find ( | r | r.name = = OBJECTS_RESULT ) {
2117
Some ( r) if r.ok => { }
2118
Some ( r) => {
2119
return fail ( format! (
2120
" {remote_name} refused part {} of the push: {} " ,
2121
i + 1 ,
2122
r.reason
2123
) )
2124
}
2125
None => {
2126
return fail ( format! (
2127
" {remote_name} does not take a push in parts, and this one is too long for one: update the server's oxy"
2128
) )
2129
}
2130
}
2131
}
2132
if batches.len ( ) > 1 {
2133
progress ( & format! (
2134
" part {} of {} : {} changes" ,
2135
batches.len ( ) ,
2136
batches.len ( ) ,
2137
last.len ( )
2138
) ) ;
98286406 Andy McCall 1 month ago ↶
2139
}
cb8b8082 Andy McCall 26 days ago ↶
2140
let mut bundle = self .bundle_for ( last) ? ;
75167672 Andy McCall 1 month ago
2141
bundle.objects.push ( (
2142
ObjectType:: State,
2143
StateObject:: new( local_heads.clone ( ) ) .encode ( ) ,
2144
) ) ;
98286406 Andy McCall 1 month ago ↶
2145
let attestation = match key {
2146
Some ( _ ) => Some ( self .sign_attest ( & Attest {
2147
repo: self .id ( ) ? ,
2148
hlc: Hlc:: now( ) ,
75167672 Andy McCall 1 month ago
2149
body: AttestBody:: Ref {
2150
name: bookmark.clone ( ) ,
2151
old_state: remote_state,
2152
new_state,
f8c69576 Andy McCall 1 month ago ↶
2153
serial: next_serial ( last_serial) ,
75167672 Andy McCall 1 month ago
2154
} ,
98286406 Andy McCall 1 month ago ↶
2155
} ) ? ) ,
2156
None => None ,
2157
} ;
68a92484 Andy McCall 22 days ago ↶
2158
2159
2160
2161
if attestation.is_some ( ) {
2162
for k in [ self .signing_key ( ) , self .config.author_key]
2163
.into_iter ( )
2164
.flatten ( )
2165
{
2166
if let Ok ( id) = self .identity ( & k) {
2167
let body = id.encode ( ) ;
2168
if ! bundle
2169
.objects
2170
.iter ( )
2171
.any ( | ( t , b ) | * t = = ObjectType:: Identity & & * b = = body)
2172
{
2173
bundle.objects.push ( ( ObjectType:: Identity, body) ) ;
2174
}
2175
}
2176
}
f0a517d2 Andy McCall 18 days ago ↶
2177
2178
let signers: Vec< KeyId> = self .signing_key ( ) .into_iter ( ) .collect ( ) ;
2179
self .attach_certificates_for ( & mut bundle, & signers) ? ;
68a92484 Andy McCall 22 days ago ↶
2180
}
98286406 Andy McCall 1 month ago ↶
2181
let serial = attestation.as_ref ( ) .and_then ( | a | match a.attest.body {
2182
AttestBody:: Ref { serial, .. } => Some ( serial) ,
2183
_ => None ,
2184
} ) ;
83e0677b Andy McCall 1 month ago ↶
2185
let sent_serial = policy_to_send.as_ref ( ) .map ( | p | p.policy.serial ) ;
132b884b Andy McCall 1 month ago ↶
2186
2187
for t in & tags_to_send {
2188
let extra = self .negotiate ( & remote_heads, & t.heads) ? ;
75167672 Andy McCall 1 month ago
2189
let extra: Vec< ChangeHash> =
2190
extra.into_iter ( ) .filter ( | h | ! to_send.contains ( h) ) .collect ( ) ;
132b884b Andy McCall 1 month ago ↶
2191
if ! extra.is_empty ( ) {
2192
let more = self .bundle_for ( & extra) ? ;
2193
bundle.changes.extend ( more.changes) ;
2c75672f Andy McCall 1 month ago ↶
2194
bundle.hashes.extend ( more.hashes) ;
132b884b Andy McCall 1 month ago ↶
2195
bundle.attests.extend ( more.attests) ;
2196
bundle.objects.extend ( more.objects) ;
2197
}
75167672 Andy McCall 1 month ago
2198
bundle.objects.push ( (
2199
ObjectType:: State,
2200
StateObject:: new( t.heads.clone ( ) ) .encode ( ) ,
2201
) ) ;
132b884b Andy McCall 1 month ago ↶
2202
}
28fc23d7 Andy McCall 1 month ago ↶
2203
if let Some ( l) = & policy_to_send {
2204
let remote_serial = resp_policy_serial;
75167672 Andy McCall 1 month ago
2205
bundle.policies = self
2206
.policy_history ( ) ?
2207
.into_iter ( )
2208
.filter ( | sp | {
2209
remote_serial.is_none_or ( | r | sp.policy.serial > r )
2210
& & sp.policy.serial < l.policy.serial
2211
} )
2212
.collect ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2213
}
75167672 Andy McCall 1 month ago
2214
let results = remote.push (
2215
bundle,
2216
vec! [ RefUpdate {
2217
name: bookmark.clone ( ) ,
2218
old_state: remote_state,
2219
new_state,
2220
force,
2221
attestation,
2222
} ] ,
2223
policy_to_send,
2224
tags_to_send,
2225
) ? ;
2226
if let ( Some ( serial) , true ) = (
2227
sent_serial,
2228
results.iter ( ) .any ( | r | r.ok & & r.name = = " policy" ) ,
2229
) {
83e0677b Andy McCall 1 month ago ↶
2230
self .record_policy_serial ( remote_name, serial) ? ;
2231
}
2232
if results.iter ( ) .any ( | r | r.ok & & r.name = = bookmark ) {
98286406 Andy McCall 1 month ago ↶
2233
let mut view = op.view.clone ( ) ;
75167672 Andy McCall 1 month ago
2234
view.remotes
2235
.entry ( remote_name.to_string ( ) )
2236
.or_default ( )
2237
.insert ( bookmark.clone ( ) , new_state) ;
98286406 Andy McCall 1 month ago ↶
2238
self .record_op ( & [ op_ref] , & format! ( " oxy push {remote_name} " ) , view) ? ;
2239
if let Some ( s) = serial {
2240
self .record_serial ( remote_name, & bookmark, s) ? ;
2241
}
2242
}
2243
Ok ( results)
2244
}
2245
2246
2247
2248
pub fn clone ( url : & str , dest : & Path) -> Result< Repo> {
7421309f Andy McCall 1 month ago ↶
2249
Self :: clone_with( url, dest, None , & [ ] )
b3c5a59a Andy McCall 1 month ago ↶
2250
}
2251
7421309f Andy McCall 1 month ago ↶
2252
2253
2254
75167672 Andy McCall 1 month ago
2255
pub fn clone_with (
2256
url : & str ,
2257
dest : & Path,
2258
max_blob : Option< u64 > ,
2259
sparse : & [String],
2260
) -> Result< Repo> {
2863456a Andy McCall 1 month ago ↶
2261
Self :: clone_full( url, dest, max_blob, sparse, None )
2262
}
2263
2264
2265
75167672 Andy McCall 1 month ago
2266
pub fn clone_shallow (
2267
url : & str ,
2268
dest : & Path,
2269
depth : u32 ,
2270
max_blob : Option< u64 > ,
2271
sparse : & [String],
2272
) -> Result< Repo> {
2863456a Andy McCall 1 month ago ↶
2273
Self :: clone_full( url, dest, max_blob, sparse, Some ( depth) )
2274
}
2275
75167672 Andy McCall 1 month ago
2276
fn clone_full (
2277
url : & str ,
2278
dest : & Path,
2279
max_blob : Option< u64 > ,
2280
sparse : & [String],
2281
depth : Option< u32 > ,
2282
) -> Result< Repo> {
ff1f62fe Andy McCall 1 month ago ↶
2283
let options = crate :: credentials:: Credentials:: user( ) .options_for ( url) ;
2284
Self :: clone_using( url, dest, max_blob, sparse, depth, & options)
a07f60a3 Andy McCall 1 month ago ↶
2285
}
2286
ff1f62fe Andy McCall 1 month ago ↶
2287
2288
2289
a07f60a3 Andy McCall 1 month ago ↶
2290
pub ( crate ) fn clone_as_subrepo ( & self , url : & str , dest : & Path) -> Result< Repo> {
2291
let options = self .credentials_for ( url) ;
ff1f62fe Andy McCall 1 month ago ↶
2292
Self :: clone_using( url, dest, None , & [ ] , None , & options)
a07f60a3 Andy McCall 1 month ago ↶
2293
}
2294
2295
2296
2297
fn remote_on_same_hub ( & self , url : & str ) -> Option< String> {
2298
let origin = | u : & str | -> Option< String> {
2299
let ( scheme, rest) = u.split_once ( " ://" ) ? ;
9f9e72b9 Andy McCall 1 month ago ↶
2300
if ! matches! ( scheme, " http" | " https" ) {
2301
return None ;
2302
}
a07f60a3 Andy McCall 1 month ago ↶
2303
let host = rest.split ( ' /' ) .next ( ) ? ;
2304
Some ( format! (
2305
" {} ://{} " ,
2306
scheme.to_ascii_lowercase ( ) ,
2307
host.to_ascii_lowercase ( )
2308
) )
2309
} ;
2310
let want = origin ( url) ? ;
2311
self .config
2312
.remotes
2313
.iter ( )
2314
.find ( | ( _, u ) | origin ( u) .as_deref ( ) = = Some ( want.as_str ( ) ) )
2315
.map ( | ( name , _) | name.clone ( ) )
2316
}
2317
2318
2319
ff1f62fe Andy McCall 1 month ago ↶
2320
2321
a07f60a3 Andy McCall 1 month ago ↶
2322
pub ( crate ) fn credentials_for ( & self , url : & str ) -> RemoteOptions {
2323
match self .remote_on_same_hub ( url) {
2324
Some ( name) => RemoteOptions {
2325
repo_id: None ,
9f9e72b9 Andy McCall 1 month ago ↶
2326
cache_dir: None ,
a07f60a3 Andy McCall 1 month ago ↶
2327
.. self .remote_options ( & name)
2328
} ,
ff1f62fe Andy McCall 1 month ago ↶
2329
None => {
2330
let mut options = RemoteOptions:: default( ) ;
2331
self .fill_credentials ( url, & mut options) ;
2332
options
2333
}
a07f60a3 Andy McCall 1 month ago ↶
2334
}
2335
}
2336
2337
fn clone_using (
2338
url : & str ,
2339
dest : & Path,
2340
max_blob : Option< u64 > ,
2341
sparse : & [String],
2342
depth : Option< u32 > ,
2343
options : & RemoteOptions,
2344
) -> Result< Repo> {
2345
let remote = open_remote ( url, options) ? ;
98286406 Andy McCall 1 month ago ↶
2346
let id = remote.repo_id ( ) ? ;
75167672 Andy McCall 1 month ago
2347
if id = = crate :: protocol:: UNBOUND_ID {
05e71ed6 Andy McCall 1 month ago ↶
2348
return fail ( format! (
2349
" {url} is empty and unbound: push a repository to it first"
2350
) ) ;
75167672 Andy McCall 1 month ago
2351
}
98286406 Andy McCall 1 month ago ↶
2352
let mut repo = Repo:: init_with_id( dest, id) ? ;
827281ee Andy McCall 1 month ago ↶
2353
repo.config
2354
.set ( " remote.origin.url" , & crate :: protocol:: stored_url( url) ) ? ;
b3c5a59a Andy McCall 1 month ago ↶
2355
if let Some ( m) = max_blob {
2356
repo.config.set ( " filter.max-blob" , & m.to_string ( ) ) ? ;
7421309f Andy McCall 1 month ago ↶
2357
} else if ! sparse.is_empty ( ) {
2358
repo.config.set ( " filter.max-blob" , " 0" ) ? ;
2359
}
2360
if ! sparse.is_empty ( ) {
2361
repo.config.set ( " filter.paths" , & sparse.join ( " ," ) ) ? ;
b3c5a59a Andy McCall 1 month ago ↶
2362
}
98286406 Andy McCall 1 month ago ↶
2363
repo.save_config ( ) ? ;
2863456a Andy McCall 1 month ago ↶
2364
if let Some ( depth) = depth {
2365
repo.fetch_shallow ( " origin" , depth) ? ;
2366
} else {
2367
repo.fetch ( " origin" ) ? ;
2368
}
98286406 Andy McCall 1 month ago ↶
2369
let ( op_ref, op) = repo.head_op ( ) ? ;
14edbaaf Andy McCall 1 month ago ↶
2370
2371
let theirs = op.view.remotes.get ( " origin" ) .cloned ( ) .unwrap_or_default ( ) ;
9f9e72b9 Andy McCall 1 month ago ↶
2372
2373
2374
let advertised = repo
2375
.open_named_remote ( " origin" )
2376
.ok ( )
2377
.and_then ( | r | r.refs ( ) .ok ( ) )
2378
.and_then ( | r | r.default_bookmark ) ;
14edbaaf Andy McCall 1 month ago ↶
2379
let bookmark = advertised
2380
.filter ( | b | theirs.contains_key ( b) )
2381
.or_else ( | | {
2382
theirs
2383
.contains_key ( & repo.config.bookmark)
2384
.then ( | | repo.config.bookmark.clone ( ) )
2385
} )
2386
.or_else ( | | {
2387
( theirs.len ( ) = = 1 )
2388
.then ( | | theirs.keys ( ) .next ( ) .cloned ( ) )
2389
.flatten ( )
2390
} )
2391
.unwrap_or_else ( | | repo.config.bookmark.clone ( ) ) ;
2392
let initial = repo.config.bookmark.clone ( ) ;
2393
if bookmark ! = repo.config.bookmark {
2394
fs:: create_dir_all( repo.workspace_bookmark_path ( ) .parent ( ) .unwrap ( ) ) ? ;
2395
write_atomic ( & repo.workspace_bookmark_path ( ) , bookmark.as_bytes ( ) ) ? ;
2396
repo.config.bookmark = bookmark.clone ( ) ;
2397
}
2398
if let Some ( state) = theirs.get ( & bookmark) .copied ( ) {
98286406 Andy McCall 1 month ago ↶
2399
let heads = repo.store.get_state ( & state) ? .heads;
2400
let mut view = op.view.clone ( ) ;
14edbaaf Andy McCall 1 month ago ↶
2401
2402
if initial ! = bookmark & & repo.heads_of ( & view, & initial) ? .is_empty ( ) {
2403
view.bookmarks.remove ( & initial) ;
2404
}
98286406 Andy McCall 1 month ago ↶
2405
view.bookmarks.insert ( bookmark, state) ;
2406
repo.record_op ( & [ op_ref] , & format! ( " oxy clone {url} " ) , view) ? ;
2407
repo.checkout ( & heads, false , " oxy clone (checkout)" ) ? ;
2408
}
2409
Ok ( repo)
f8c69576 Andy McCall 1 month ago ↶
2410
}
2411
}
2412
2413
2414
2415
2416
2417
fn next_serial ( last : Option< u64 > ) -> u64 {
2418
match last {
2419
Some ( l) => l + 1 ,
2420
None => Hlc:: now( ) .wall_ms.max ( 1 ) ,
98286406 Andy McCall 1 month ago ↶
2421
}
2422
}