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