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