|
| 1 | +//go:build !testUnit && !lint |
| 2 | +// +build !testUnit,!lint |
| 3 | + |
| 4 | +// We exclude this file from unit tests and linting because it cannot be |
| 5 | +// compiled without CGO and a specific version of libgit2 pre-installed. To keep |
| 6 | +// our linting and unit tests lightweight, those are complications we'd like to |
| 7 | +// avoid. We'll live without the linting and test this well with integration |
| 8 | +// tests. |
| 9 | + |
| 10 | +package main |
| 11 | + |
| 12 | +import ( |
| 13 | + git "github.com/libgit2/git2go/v32" |
| 14 | + "github.com/pkg/errors" |
| 15 | + "golang.org/x/crypto/ssh" |
| 16 | +) |
| 17 | + |
| 18 | +// getCredentialsCallback extracts credentials, if any, from project secrets in |
| 19 | +// the provided map and returns a callback function. If no credentials are found |
| 20 | +// in the secrets, a nil function pointer is returned. |
| 21 | +func getCredentialsCallback( |
| 22 | + secrets map[string]string, |
| 23 | +) (git.CredentialsCallback, error) { |
| 24 | + // We'll check the project's secrets first for a well-known key that we could |
| 25 | + // expect contains a private SSH key. If we find that, we'll use it. |
| 26 | + if privateKey, ok := secrets["gitSSHKey"]; ok { |
| 27 | + var signer ssh.Signer |
| 28 | + var err error |
| 29 | + // The private key may or may not be protected by a passphrase... |
| 30 | + if keyPassStr, ok := secrets["gitSSHKeyPassword"]; ok { |
| 31 | + // Auth using key and passphrase |
| 32 | + signer, err = ssh.ParsePrivateKeyWithPassphrase( |
| 33 | + []byte(privateKey), |
| 34 | + []byte(keyPassStr), |
| 35 | + ) |
| 36 | + } else { |
| 37 | + // Auth using a key without a passphrase |
| 38 | + signer, err = ssh.ParsePrivateKey([]byte(privateKey)) |
| 39 | + } |
| 40 | + if err != nil { |
| 41 | + return nil, errors.Wrap( |
| 42 | + err, |
| 43 | + `error parsing private SSH key specified by secret "gitSSHKey"`, |
| 44 | + ) |
| 45 | + } |
| 46 | + return func( |
| 47 | + string, |
| 48 | + string, |
| 49 | + git.CredentialType, |
| 50 | + ) (*git.Credential, error) { |
| 51 | + return git.NewCredentialSSHKeyFromSigner("git", signer) |
| 52 | + }, nil |
| 53 | + } |
| 54 | + |
| 55 | + // Check the project's secrets for a well-known key that we could expect |
| 56 | + // contains a password or token. |
| 57 | + if password, ok := secrets["gitPassword"]; ok { |
| 58 | + // There may or may not be a username associated with the password. It |
| 59 | + // really depends on who hosts the repository we're cloning from. GitHub, |
| 60 | + // for instance, expects the username to be any non-empty string (and the |
| 61 | + // password to be a personal access token), while Bitbucket expects a valid |
| 62 | + // username (and the password to be an app password). |
| 63 | + username := secrets["gitUsername"] |
| 64 | + // Ultimately, the username and password are used with basic auth and an |
| 65 | + // empty username isn't allowed, so if no username was specified (e.g. for a |
| 66 | + // repo hosted on GitHub), just use the string "git" as the username. |
| 67 | + if username == "" { |
| 68 | + username = "git" |
| 69 | + } |
| 70 | + return func( |
| 71 | + string, |
| 72 | + string, |
| 73 | + git.CredentialType, |
| 74 | + ) (*git.Credential, error) { |
| 75 | + // Basic auth |
| 76 | + return git.NewCredentialUserpassPlaintext(username, password) |
| 77 | + }, nil |
| 78 | + } |
| 79 | + |
| 80 | + // No credentials found |
| 81 | + return nil, nil |
| 82 | +} |
0 commit comments