@@ -2,6 +2,7 @@ package cmd
22
33import (
44 "bytes"
5+ "errors"
56 "flag"
67 "fmt"
78 "io"
@@ -19,8 +20,11 @@ import (
1920 "github.com/roots/trellis-cli/trellis"
2021)
2122
22- const secretName = "TRELLIS_DEPLOY_SSH_PRIVATE_KEY"
23- const deployKeyName = "Trellis deploy"
23+ const (
24+ sshKeySecret = "TRELLIS_DEPLOY_SSH_PRIVATE_KEY"
25+ sshKnownHostsSecret = "TRELLIS_DEPLOY_SSH_KNOWN_HOSTS"
26+ deployKeyName = "Trellis deploy"
27+ )
2428
2529func NewKeyGenerateCommand (ui cli.Ui , trellis * trellis.Trellis ) * KeyGenerateCommand {
2630 c := & KeyGenerateCommand {UI : ui , Trellis : trellis }
@@ -133,7 +137,7 @@ func (c *KeyGenerateCommand) Run(args []string) int {
133137 _ , err = exec .LookPath ("gh" )
134138 if err != nil {
135139 c .UI .Error ("Error: GitHub CLI not found" )
136- c .UI .Error ("gh command must be available to create a GitHub secret " )
140+ c .UI .Error ("gh command must be available to interact with GitHub" )
137141 c .UI .Error ("See https://cli.github.com" )
138142 return 1
139143 }
@@ -145,19 +149,14 @@ func (c *KeyGenerateCommand) Run(args []string) int {
145149 return 1
146150 }
147151
148- ghArgs := []string {"secret" , "set" , secretName , "--body" , string (privateKeyContent )}
149-
150- ghSecret := exec .Command ("gh" , ghArgs ... )
151- ghSecret .Stdout = io .Discard
152- ghSecret .Stderr = os .Stderr
153- err = ghSecret .Run ()
152+ err = githubCLI ("secret" , "set" , sshKeySecret , "--body" , string (privateKeyContent ))
154153 if err != nil {
155- c .UI .Error ("Error: could not create GitHub secret" )
154+ c .UI .Error ("Error: could not set GitHub secret" )
156155 c .UI .Error (err .Error ())
157156 return 1
158157 }
159158
160- c .UI .Info (fmt .Sprintf ("%s GitHub secret set [%s]" , color .GreenString ("[✓]" ), secretName ))
159+ c .UI .Info (fmt .Sprintf ("%s GitHub private key secret set [%s]" , color .GreenString ("[✓]" ), sshKeySecret ))
161160
162161 publicKeyContent , err := ioutil .ReadFile (trellisPublicKeyPath )
163162 if err != nil {
@@ -167,24 +166,42 @@ func (c *KeyGenerateCommand) Run(args []string) int {
167166 }
168167
169168 publicKeyContent = bytes .TrimSuffix (publicKeyContent , []byte ("\n " ))
170-
171169 title := fmt .Sprintf ("title=%s" , deployKeyName )
172170 key := fmt .Sprintf ("key=%s" , string (publicKeyContent ))
173- ghApiArgs := []string {"api" , "repos/{owner}/{repo}/keys" , "-f" , title , "-f" , key , "-f" , "read_only=true" }
174171
175- ghApi := exec .Command ("gh" , ghApiArgs ... )
176- ghApi .Stdout = io .Discard
177- ghApi .Stderr = os .Stderr
178- err = ghApi .Run ()
172+ err = githubCLI ("api" , "repos/{owner}/{repo}/keys" , "-f" , title , "-f" , key , "-f" , "read_only=true" )
179173 if err != nil {
180174 c .UI .Error ("Error: could not create GitHub deploy key" )
181175 c .UI .Error (err .Error ())
182176 return 1
183177 }
184- c .UI .Info (fmt .Sprintf ("%s GitHub deploy key added [%s]\n " , color .GreenString ("[✓]" ), deployKeyName ))
178+ c .UI .Info (fmt .Sprintf ("%s GitHub deploy key added [%s]" , color .GreenString ("[✓]" ), deployKeyName ))
179+
180+ hosts , err := getAnsibleHosts ()
181+ if err != nil {
182+ c .UI .Error ("Error: could not get hosts to set known hosts secret" )
183+ c .UI .Error (err .Error ())
184+ return 1
185+ }
186+
187+ keyscanOutput , err := exec .Command ("ssh-keyscan" , "-t" , "ed25519" , "-H" , strings .Join (hosts , " " )).Output ()
188+ if err != nil {
189+ c .UI .Error ("Error: could not set SSH known hosts. ssh-keyscan command failed." )
190+ c .UI .Error (err .Error ())
191+ return 1
192+ }
193+
194+ err = githubCLI ("secret" , "set" , sshKnownHostsSecret , "--body" , string (keyscanOutput ))
195+ if err != nil {
196+ c .UI .Error ("Error: could not set GitHub secret" )
197+ c .UI .Error (err .Error ())
198+ return 1
199+ }
200+
201+ c .UI .Info (fmt .Sprintf ("%s GitHub known hosts secret set [%s]" , color .GreenString ("[✓]" ), sshKnownHostsSecret ))
185202
186203 if c .provisionEnv == "" {
187- c .UI .Info ("The public key will not be usable until it's added to your server." )
204+ c .UI .Info ("\n The public key will not be usable until it's added to your server." )
188205 prompt := promptui.Prompt {
189206 Label : "Provision now and apply the new public key" ,
190207 IsConfirm : true ,
@@ -276,3 +293,54 @@ func (c *KeyGenerateCommand) AutocompleteFlags() complete.Flags {
276293 "--provision" : complete .PredictSet (environmentNames ... ),
277294 }
278295}
296+
297+ func githubCLI (args ... string ) error {
298+ ghCmd := exec .Command ("gh" , args ... )
299+ ghCmd .Stdout = io .Discard
300+ ghCmd .Stderr = os .Stderr
301+
302+ return ghCmd .Run ()
303+ }
304+
305+ func getAnsibleHosts () (hosts []string , err error ) {
306+ hostsOutput , err := exec .Command ("ansible" , "all" , "--list-hosts" ).Output ()
307+
308+ if err != nil {
309+ return nil , err
310+ }
311+
312+ hosts = parseAnsibleHosts (string (hostsOutput ))
313+
314+ if len (hosts ) == 0 {
315+ return nil , errors .New ("No hosts found by Ansible. This is either a bug in trellis-cli or your host files are invalid." )
316+ }
317+
318+ return hosts , nil
319+ }
320+
321+ /*
322+
323+ Parses the output of `ansible all --list-hosts` into a slice of host strings
324+
325+ Example input:
326+ hosts (3):
327+ 192.168.56.5
328+ 192.168.56.10
329+ your_server_hostname
330+ */
331+
332+ func parseAnsibleHosts (output string ) (hosts []string ) {
333+ lines := strings .Split (string (output ), "\n " )
334+
335+ for _ , host := range lines {
336+ host = strings .TrimSpace (host )
337+
338+ if strings .HasPrefix (host , "hosts (" ) || host == "" {
339+ continue
340+ }
341+
342+ hosts = append (hosts , host )
343+ }
344+
345+ return hosts
346+ }
0 commit comments