fce44ae9 Andy McCall 1 month ago ↶
1
2
3
use super :: format:: { short_id, signature_line, when} ;
4
use super :: open;
135212de Andy McCall 1 month ago ↶
5
use super :: ui;
fce44ae9 Andy McCall 1 month ago ↶
6
use anyhow:: Result;
7
use oxy_core:: hash:: ChangeHash;
83e0677b Andy McCall 1 month ago ↶
8
use oxy_core:: identity:: Identity;
fce44ae9 Andy McCall 1 month ago ↶
9
use oxy_core:: repo:: { self , IdentityScope} ;
83e0677b Andy McCall 1 month ago ↶
10
use std:: path:: { Path, PathBuf} ;
fce44ae9 Andy McCall 1 month ago ↶
11
12
pub fn create ( name : Option< String> , email : Option< String> , local : bool ) -> Result< i32 > {
13
let mut repo = open ( ) ? ;
14
let name = name.unwrap_or_else ( | | repo.config.author_name.clone ( ) ) ;
15
let email = email.unwrap_or_else ( | | repo.config.author_email.clone ( ) ) ;
16
let scope = if local { IdentityScope:: Repo } else { IdentityScope:: User } ;
17
let ( key, _ ) = repo.create_identity ( & name, & email, scope) ? ;
94084087 Andy McCall 1 month ago ↶
18
println! ( " {} identity {} <{email} >" , ui:: done( " Created" ) , ui:: author( & name) ) ;
19
println! ( " {} " , ui:: kv( " key" , key) ) ;
20
println! ( " {} " , ui:: kv( " secret" , ui:: path( repo:: default_keys_dir( ) .display ( ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
21
match scope {
94084087 Andy McCall 1 month ago ↶
22
IdentityScope:: User => println! ( " {} " , ui:: kv( " scope" , format! ( " default author in {} " , ui:: path( repo.user_config_path ( ) .display ( ) ) ) ) ) ,
23
IdentityScope:: Repo => println! ( " {} " , ui:: kv( " scope" , " this repository's .oxy/config only" ) ) ,
fce44ae9 Andy McCall 1 month ago ↶
24
}
25
if ! repo.config.sign_changes {
94084087 Andy McCall 1 month ago ↶
26
println! ( " {} {} " , ui:: note( ) , ui:: dim( " changes will carry this author; run oxy config --user core.sign change to sign each commit" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
27
}
28
Ok ( 0 )
29
}
30
31
pub fn show ( ) -> Result< i32 > {
32
let repo = open ( ) ? ;
33
let Some ( key) = repo.config.author_key else {
34
println! ( " No identity configured; run `oxy identity create`." ) ;
35
return Ok ( 0 ) ;
36
} ;
37
let id = repo.identity ( & key) ? ;
94084087 Andy McCall 1 month ago ↶
38
println! ( " {} {} <{} >" , ui:: section( " identity" ) , ui:: author( & id.name) , id.email) ;
39
println! ( " {} " , ui:: kv( " key" , key) ) ;
40
println! ( " {} " , ui:: kv( " created" , format! ( " {} {} " , when ( id.created) , ui:: dim( format! ( " ({} )" , ui:: ago( id.created) ) ) ) ) ) ;
41
println! ( " {} " , ui:: kv( " secret" , if repo.secret_key ( ) ? .is_some ( ) { ui:: ok( format! ( " {} available" , ui:: glyph( " ✔" , " *" ) ) ) } else { ui:: err( format! ( " {} not found in the key directory" , ui:: glyph( " ✖" , " x" ) ) ) } ) ) ;
42
println! ( " {} " , ui:: kv( " signing" , if repo.config.sign_changes { " every change (core.sign = change)" } else { " at push via seals (core.sign = seal)" } ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
43
Ok ( 0 )
44
}
45
46
pub fn verify ( change : Option< & str > ) -> Result< i32 > {
47
let repo = open ( ) ? ;
48
let hashes: Vec< ChangeHash> = match change {
49
Some ( c) => vec! [ repo.resolve_change ( c) ? ] ,
50
None => repo.log ( ) ? .into_iter ( ) .filter ( | e | ! e.is_working_copy) .map ( | e | e.hash ) .collect ( ) ,
51
} ;
52
let mut bad = 0 ;
53
for h in hashes {
2863456a Andy McCall 1 month ago ↶
54
let Ok ( c) = repo.store ( ) .get_change ( & h) else {
55
println! ( " {} {} " , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) , ui:: dim( " not present (shallow)" ) ) ;
56
continue ;
57
} ;
fce44ae9 Andy McCall 1 month ago ↶
58
let line = signature_line ( & repo, & h) ? ;
135212de Andy McCall 1 month ago ↶
59
let styled = if line.starts_with ( " INVALID" ) {
fce44ae9 Andy McCall 1 month ago ↶
60
bad + = 1 ;
94084087 Andy McCall 1 month ago ↶
61
ui:: err( format! ( " {} {line} " , ui:: glyph( " ✖" , " x" ) ) )
135212de Andy McCall 1 month ago ↶
62
} else if line = = " unsigned" {
94084087 Andy McCall 1 month ago ↶
63
ui:: dim( format! ( " {} {line} " , ui:: glyph( " ·" , " -" ) ) )
135212de Andy McCall 1 month ago ↶
64
} else {
94084087 Andy McCall 1 month ago ↶
65
ui:: ok( format! ( " {} {line} " , ui:: glyph( " ✔" , " v" ) ) )
135212de Andy McCall 1 month ago ↶
66
} ;
67
println! ( " {} {} {styled} " , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
68
}
69
Ok ( if bad > 0 { 1 } else { 0 } )
70
}
71
72
pub fn config ( key : & str , value : & str , user : bool ) -> Result< i32 > {
73
if user {
74
let path = repo:: default_user_config( ) ;
75
repo:: Config:: set_user( & path, key, value) ? ;
94084087 Andy McCall 1 month ago ↶
76
println! ( " {} {key} = {value} {} " , ui:: done( " Set" ) , ui:: dim( format! ( " (in {} )" , path.display ( ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
77
} else {
78
let mut repo = open ( ) ? ;
79
repo.config.set ( key, value) ? ;
80
repo.save_config ( ) ? ;
94084087 Andy McCall 1 month ago ↶
81
println! ( " {} {key} = {value} {} " , ui:: done( " Set" ) , ui:: dim( " (in this repository)" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
82
}
83
Ok ( 0 )
84
}
83e0677b Andy McCall 1 month ago ↶
85
86
pub fn export ( path : Option< PathBuf> ) -> Result< i32 > {
87
let repo = open ( ) ? ;
88
let Some ( key) = repo.config.author_key else {
89
anyhow:: bail! ( " no identity configured; run `oxy identity create`" ) ;
90
} ;
91
let id = repo.identity ( & key) ? ;
92
let path = path.unwrap_or_else ( | | PathBuf:: from( format! ( " {} .identity" , key.short ( 16 ) ) ) ) ;
93
std:: fs:: write( & path, id.encode ( ) ) ? ;
94084087 Andy McCall 1 month ago ↶
94
println! ( " {} identity {} <{} > ({} ) to {} " , ui:: done( " Wrote" ) , ui:: author( & id.name) , id.email, key.short ( 12 ) , ui:: path( path.display ( ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
95
Ok ( 0 )
96
}
97
98
pub fn policy_init ( ) -> Result< i32 > {
99
let repo = open ( ) ? ;
100
let sp = repo.policy_init ( ) ? ;
94084087 Andy McCall 1 month ago ↶
101
println! ( " {} policy serial {} with root key {} {} " , ui:: done( " Created" ) , sp.policy.serial, sp.policy.roots[ 0 ] .short ( 12 ) , ui:: dim( " (push to publish it)" ) ) ;
83e0677b Andy McCall 1 month ago ↶
102
Ok ( 0 )
103
}
104
105
pub fn policy_show ( ) -> Result< i32 > {
106
let repo = open ( ) ? ;
107
let Some ( ( r, sp) ) = repo.current_policy ( ) ? else {
108
println! ( " No policy: this repository is unverified. Run `oxy policy init` to create one." ) ;
109
return Ok ( 0 ) ;
110
} ;
111
let p = & sp.policy;
112
let name = | k : & oxy_core:: hash:: KeyId| p.identity ( k) .map ( | i | format! ( " {} <{} >" , i.name, i.email) ) .unwrap_or_else ( | | " (unknown identity)" .into ( ) ) ;
94084087 Andy McCall 1 month ago ↶
113
let expired = p.is_expired ( oxy_core:: objects:: Hlc:: now( ) .wall_ms) ;
114
println! ( " {} {} {} serial {} {} expires {} {} " , ui:: section( " policy" ) , ui:: hash( format! ( " #{} " , r.short ( 12 ) ) ) , ui:: sep( ) , p.serial, ui:: sep( ) , when ( p.expires) , if expired { ui:: err( format! ( " {} EXPIRED" , ui:: glyph( " ✖" , " x" ) ) ) } else { String :: new( ) } ) ;
115
println! ( " {} " , ui:: section( format! ( " roots {} " , ui:: dim( format! ( " (threshold {} )" , p.root_threshold) ) ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
116
for k in & p.roots {
94084087 Andy McCall 1 month ago ↶
117
println! ( " {} {} {} " , ui:: ok( ui:: glyph( " 🔑" , " *" ) ) , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
118
}
94084087 Andy McCall 1 month ago ↶
119
println! ( " {} " , ui:: section( " rules" ) ) ;
83e0677b Andy McCall 1 month ago ↶
120
for rule in & p.rules {
94084087 Andy McCall 1 month ago ↶
121
println! ( " {} {:12} {} " , ui:: ok( ui:: glyph( " ▸" , " >" ) ) , ui:: bookmark( & rule.pattern) , ui:: dim( format! ( " threshold {} , require {} , conflicts {} " , rule.threshold, rule.require.name ( ) , if rule.allow_conflicts { " allowed" } else { " refused" } ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
122
for k in & rule.keys {
94084087 Andy McCall 1 month ago ↶
123
println! ( " {} {} " , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
124
}
125
}
126
if ! p.revoked.is_empty ( ) {
94084087 Andy McCall 1 month ago ↶
127
println! ( " {} " , ui:: section( " revoked" ) ) ;
83e0677b Andy McCall 1 month ago ↶
128
for k in & p.revoked {
94084087 Andy McCall 1 month ago ↶
129
println! ( " {} {} {} " , ui:: err( ui:: glyph( " ✖" , " x" ) ) , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
130
}
131
}
132
Ok ( 0 )
133
}
134
135
pub fn policy_grant ( identity_file : & Path, pattern : Option< & str > ) -> Result< i32 > {
136
let repo = open ( ) ? ;
137
let id = Identity:: decode( & std:: fs:: read( identity_file) ? ) ? ;
138
let key = id.hash ( ) ;
139
let sp = repo.policy_grant ( id.clone ( ) , pattern) ? ;
94084087 Andy McCall 1 month ago ↶
140
println! ( " {} {} <{} > ({} ) on {} {} " , ui:: done( " Granted" ) , ui:: author( & id.name) , id.email, key.short ( 12 ) , ui:: bookmark( pattern.unwrap_or ( " *" ) ) , ui:: dim( format! ( " (policy serial {} ; push to publish)" , sp.policy.serial) ) ) ;
83e0677b Andy McCall 1 month ago ↶
141
Ok ( 0 )
142
}
143
144
pub fn policy_revoke ( key : & str ) -> Result< i32 > {
145
let repo = open ( ) ? ;
146
let Some ( ( _ , sp) ) = repo.current_policy ( ) ? else {
147
anyhow:: bail! ( " this repository has no policy" ) ;
148
} ;
149
let matches: Vec< _ > = sp.policy.identities.iter ( ) .map ( | i | i.hash ( ) ) .filter ( | k | k.starts_with_hex ( key) ) .collect ( ) ;
150
let full = match matches.len ( ) {
151
1 => matches[ 0 ] ,
152
0 => anyhow:: bail! ( " no key in the policy starts with {key:?}" ) ,
153
_ => anyhow:: bail! ( " {key:?} is ambiguous" ) ,
154
} ;
155
let sp = repo.policy_revoke ( & full) ? ;
94084087 Andy McCall 1 month ago ↶
156
println! ( " {} {} {} " , ui:: done( " Revoked" ) , ui:: hash( full.short ( 12 ) ) , ui:: dim( format! ( " (policy serial {} ; push to publish)" , sp.policy.serial) ) ) ;
83e0677b Andy McCall 1 month ago ↶
157
Ok ( 0 )
158
}