4
4
use std:: {
5
5
io:: { BufRead , BufReader , Write } ,
6
6
path:: PathBuf ,
7
- process:: { Command , Stdio } ,
7
+ process:: { Child , Command , Stdio } ,
8
8
} ;
9
9
10
10
use anyhow:: Context ;
11
11
use cargo_credential:: {
12
- Action , Credential , CredentialHello , CredentialRequest , CredentialResponse , RegistryInfo ,
12
+ Action , Credential , CredentialHello , CredentialRequest , CredentialResponse , Error , RegistryInfo ,
13
13
} ;
14
14
15
15
pub struct CredentialProcessCredential {
@@ -22,31 +22,38 @@ impl<'a> CredentialProcessCredential {
22
22
path : PathBuf :: from ( path) ,
23
23
}
24
24
}
25
- }
26
25
27
- impl < ' a > Credential for CredentialProcessCredential {
28
- fn perform (
26
+ fn run (
29
27
& self ,
30
- registry : & RegistryInfo < ' _ > ,
28
+ child : & mut Child ,
31
29
action : & Action < ' _ > ,
30
+ registry : & RegistryInfo < ' _ > ,
32
31
args : & [ & str ] ,
33
- ) -> Result < CredentialResponse , cargo_credential:: Error > {
34
- let mut cmd = Command :: new ( & self . path ) ;
35
- cmd. stdout ( Stdio :: piped ( ) ) ;
36
- cmd. stdin ( Stdio :: piped ( ) ) ;
37
- cmd. arg ( "--cargo-plugin" ) ;
38
- tracing:: debug!( "credential-process: {cmd:?}" ) ;
39
- let mut child = cmd. spawn ( ) . context ( "failed to spawn credential process" ) ?;
32
+ ) -> Result < Result < CredentialResponse , Error > , Error > {
40
33
let mut output_from_child = BufReader :: new ( child. stdout . take ( ) . unwrap ( ) ) ;
41
34
let mut input_to_child = child. stdin . take ( ) . unwrap ( ) ;
42
35
let mut buffer = String :: new ( ) ;
36
+
37
+ // Read the CredentialHello
43
38
output_from_child
44
39
. read_line ( & mut buffer)
45
40
. context ( "failed to read hello from credential provider" ) ?;
46
41
let credential_hello: CredentialHello =
47
42
serde_json:: from_str ( & buffer) . context ( "failed to deserialize hello" ) ?;
48
43
tracing:: debug!( "credential-process > {credential_hello:?}" ) ;
44
+ if !credential_hello
45
+ . v
46
+ . contains ( & cargo_credential:: PROTOCOL_VERSION_1 )
47
+ {
48
+ return Err ( format ! (
49
+ "credential provider supports protocol versions {:?}, while Cargo supports {:?}" ,
50
+ credential_hello. v,
51
+ [ cargo_credential:: PROTOCOL_VERSION_1 ]
52
+ )
53
+ . into ( ) ) ;
54
+ }
49
55
56
+ // Send the Credential Request
50
57
let req = CredentialRequest {
51
58
v : cargo_credential:: PROTOCOL_VERSION_1 ,
52
59
action : action. clone ( ) ,
@@ -56,14 +63,17 @@ impl<'a> Credential for CredentialProcessCredential {
56
63
let request = serde_json:: to_string ( & req) . context ( "failed to serialize request" ) ?;
57
64
tracing:: debug!( "credential-process < {req:?}" ) ;
58
65
writeln ! ( input_to_child, "{request}" ) . context ( "failed to write to credential provider" ) ?;
59
-
60
66
buffer. clear ( ) ;
61
67
output_from_child
62
68
. read_line ( & mut buffer)
63
69
. context ( "failed to read response from credential provider" ) ?;
64
- let response: Result < CredentialResponse , cargo_credential:: Error > =
70
+
71
+ // Read the Credential Response
72
+ let response: Result < CredentialResponse , Error > =
65
73
serde_json:: from_str ( & buffer) . context ( "failed to deserialize response" ) ?;
66
74
tracing:: debug!( "credential-process > {response:?}" ) ;
75
+
76
+ // Tell the credential process we're done by closing stdin. It should exit cleanly.
67
77
drop ( input_to_child) ;
68
78
let status = child. wait ( ) . context ( "credential process never started" ) ?;
69
79
if !status. success ( ) {
@@ -75,6 +85,31 @@ impl<'a> Credential for CredentialProcessCredential {
75
85
. into ( ) ) ;
76
86
}
77
87
tracing:: trace!( "credential process exited successfully" ) ;
78
- response
88
+ Ok ( response)
89
+ }
90
+ }
91
+
92
+ impl < ' a > Credential for CredentialProcessCredential {
93
+ fn perform (
94
+ & self ,
95
+ registry : & RegistryInfo < ' _ > ,
96
+ action : & Action < ' _ > ,
97
+ args : & [ & str ] ,
98
+ ) -> Result < CredentialResponse , Error > {
99
+ let mut cmd = Command :: new ( & self . path ) ;
100
+ cmd. stdout ( Stdio :: piped ( ) ) ;
101
+ cmd. stdin ( Stdio :: piped ( ) ) ;
102
+ cmd. arg ( "--cargo-plugin" ) ;
103
+ tracing:: debug!( "credential-process: {cmd:?}" ) ;
104
+ let mut child = cmd. spawn ( ) . context ( "failed to spawn credential process" ) ?;
105
+ match self . run ( & mut child, action, registry, args) {
106
+ Err ( e) => {
107
+ // Since running the credential process itself failed, ensure the
108
+ // process is stopped.
109
+ let _ = child. kill ( ) ;
110
+ Err ( e)
111
+ }
112
+ Ok ( response) => response,
113
+ }
79
114
}
80
115
}
0 commit comments