diff --git a/cmd/dagger/cmd/root.go b/cmd/dagger/cmd/root.go index 254abd33..17d4bf2c 100644 --- a/cmd/dagger/cmd/root.go +++ b/cmd/dagger/cmd/root.go @@ -12,6 +12,7 @@ import ( "go.dagger.io/dagger/cmd/dagger/cmd/input" "go.dagger.io/dagger/cmd/dagger/cmd/output" "go.dagger.io/dagger/cmd/dagger/logger" + "go.dagger.io/dagger/keychain" ) var rootCmd = &cobra.Command{ @@ -25,8 +26,16 @@ func init() { rootCmd.PersistentFlags().StringP("environment", "e", "", "Select an environment") rootCmd.PersistentFlags().StringP("workspace", "w", "", "Specify a workspace (defaults to current git repository)") - rootCmd.PersistentPreRun = func(*cobra.Command, []string) { + rootCmd.PersistentPreRun = func(cmd *cobra.Command, _ []string) { + lg := logger.New() + ctx := lg.WithContext(cmd.Context()) + go checkVersion() + + err := keychain.EnsureDefaultKey(ctx) + if err != nil { + lg.Fatal().Err(err).Msg("failed to generate default key") + } } rootCmd.PersistentPostRun = func(*cobra.Command, []string) { warnVersion() diff --git a/keychain/keys.go b/keychain/keys.go index 57bc1fcd..017a563b 100644 --- a/keychain/keys.go +++ b/keychain/keys.go @@ -14,43 +14,65 @@ import ( ) func Path() (string, error) { - keysFile, err := homedir.Expand("~/.config/dagger/keys.txt") + return homedir.Expand("~/.config/dagger/keys.txt") +} + +func EnsureDefaultKey(ctx context.Context) error { + keysFile, err := Path() if err != nil { - return "", err + return err } - // if the keys file doesn't exist, attempt a migration - if _, err := os.Stat(keysFile); errors.Is(err, os.ErrNotExist) { - migrateKeys(keysFile) + // If the keys file already exists, there's nothing to do. + _, err = os.Stat(keysFile) + if err == nil { + return nil } - return keysFile, nil + // If we got a different error than not existent, abort + if !errors.Is(err, os.ErrNotExist) { + return err + } + + // Attempt a migration from the old keys file + migrated, err := migrateKeys(ctx, keysFile) + if err != nil { + return err + } + + // If we migrated a previous identity, stop here. + if migrated { + return nil + } + + // Otherwise, generate a new key + log.Ctx(ctx).Debug().Msg("generating default key pair") + _, err = Generate(ctx) + return err } // migrateKeys attempts a migration from `~/.dagger/keys.txt` to `~/.config/dagger/keys.txt` -func migrateKeys(keysFile string) error { +func migrateKeys(ctx context.Context, keysFile string) (bool, error) { oldKeysFile, err := homedir.Expand("~/.dagger/keys.txt") if err != nil { - return err + return false, err } if _, err := os.Stat(oldKeysFile); err != nil { - return err + return false, nil } if err := os.MkdirAll(filepath.Dir(keysFile), 0700); err != nil { - return err + return false, err } - return os.Rename(oldKeysFile, keysFile) + log.Ctx(ctx).Info().Msg("migrating keychain") + return true, os.Rename(oldKeysFile, keysFile) } func Default(ctx context.Context) (string, error) { keys, err := List(ctx) if err != nil { - if errors.Is(err, os.ErrNotExist) { - return Generate(ctx) - } return "", err } if len(keys) == 0 { @@ -85,7 +107,7 @@ func Generate(ctx context.Context) (string, error) { pubkey := k.Recipient().String() - log.Ctx(ctx).Debug().Str("publicKey", pubkey).Msg("generating keypair") + log.Ctx(ctx).Debug().Str("publicKey", pubkey).Msg("keypair generated") return pubkey, nil } @@ -98,7 +120,7 @@ func List(ctx context.Context) ([]*age.X25519Identity, error) { f, err := os.Open(keysFile) if err != nil { - return nil, fmt.Errorf("failed to open keys file file %q: %w", keysFile, err) + return nil, fmt.Errorf("failed to open keys file %q: %w", keysFile, err) } ids, err := age.ParseIdentities(f) if err != nil { @@ -107,7 +129,7 @@ func List(ctx context.Context) ([]*age.X25519Identity, error) { keys := make([]*age.X25519Identity, 0, len(ids)) for _, id := range ids { - key, ok := ids[0].(*age.X25519Identity) + key, ok := id.(*age.X25519Identity) if !ok { return nil, fmt.Errorf("internal error: unexpected identity type: %T", id) } diff --git a/state/workspace_test.go b/state/workspace_test.go index 8083f0e3..bd2f7a13 100644 --- a/state/workspace_test.go +++ b/state/workspace_test.go @@ -8,12 +8,15 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.dagger.io/dagger/keychain" "gopkg.in/yaml.v3" ) func TestWorkspace(t *testing.T) { ctx := context.TODO() + keychain.EnsureDefaultKey(ctx) + root, err := os.MkdirTemp(os.TempDir(), "dagger-*") require.NoError(t, err) @@ -60,6 +63,8 @@ func TestWorkspace(t *testing.T) { func TestEncryption(t *testing.T) { ctx := context.TODO() + keychain.EnsureDefaultKey(ctx) + readManifest := func(st *State) *State { data, err := os.ReadFile(path.Join(st.Path, manifestFile)) require.NoError(t, err)