func editRun()

in main.go [722:874]


func editRun(cmd *cobra.Command, args []string) error {
	ctx, client, err := clientWithContext(cmd.Context())
	if err != nil {
		return misuseError(err)
	}

	// Find the editor
	var editor string
	for _, e := range []string{"VISUAL", "EDITOR"} {
		if v := os.Getenv(e); v != "" {
			editor = v
			break
		}
	}
	if editor == "" {
		err := fmt.Errorf("no editor is set - set VISUAL or EDITOR")
		return apiError(err)
	}

	ref, err := parseRef(args[0])
	if err != nil {
		return misuseError(err)
	}

	var originalSecret *berglas.Secret

	// Get the existing secret
	switch t := ref.Type(); t {
	case berglas.ReferenceTypeSecretManager:
		originalSecret, err = client.Read(ctx, &berglas.SecretManagerReadRequest{
			Project: ref.Project(),
			Name:    ref.Name(),
			Version: ref.Version(),
		})
	case berglas.ReferenceTypeStorage:
		originalSecret, err = client.Read(ctx, &berglas.StorageReadRequest{
			Bucket:     ref.Bucket(),
			Object:     ref.Object(),
			Generation: ref.Generation(),
		})
	default:
		return misuseError(fmt.Errorf("unknown type %T", t))
	}

	if err != nil {
		return apiError(err)
	}

	// Create the tempfile
	f, err := os.CreateTemp("", "berglas-")
	if err != nil {
		err = fmt.Errorf("failed to create tempfile for secret: %w", err)
		return apiError(err)
	}

	defer func() {
		if err := os.Remove(f.Name()); err != nil {
			fmt.Fprintf(stderr, "failed to cleanup tempfile %s: %s\n", f.Name(), err)
		}
	}()

	// Write contents to the original file
	if _, err := f.Write(originalSecret.Plaintext); err != nil {
		err = fmt.Errorf("failed to write tempfile for secret: %w", err)
		return apiError(err)
	}

	if err := f.Sync(); err != nil {
		err = fmt.Errorf("failed to sync tempfile for secret: %w", err)
		return apiError(err)
	}

	if err := f.Close(); err != nil {
		err = fmt.Errorf("failed to close tempfile for secret: %w", err)
		return apiError(err)
	}

	// Spawn editor
	editorSplit := strings.Split(editor, " ")
	editorCmd, editorArgs := editorSplit[0], editorSplit[1:]
	editorArgs = append(editorArgs, f.Name())
	externalCmd := exec.CommandContext(ctx, editorCmd, editorArgs...)
	externalCmd.Stdin = stdin
	externalCmd.Stdout = stdout
	externalCmd.Stderr = stderr
	if err := externalCmd.Start(); err != nil {
		err = fmt.Errorf("failed to start editor: %w", err)
		return misuseError(err)
	}
	if err := externalCmd.Wait(); err != nil {
		if terr, ok := err.(*exec.ExitError); ok && terr.ProcessState != nil {
			code := terr.ProcessState.ExitCode()
			return exitWithCode(code, fmt.Errorf("editor did not exit 0: %w", err))
		}
		err = fmt.Errorf("unknown failure in running editor: %w", err)
		return misuseError(err)
	}

	// Read the new secret value
	newPlaintext, err := os.ReadFile(f.Name())
	if err != nil {
		err = fmt.Errorf("failed to read secret tempfile: %w", err)
		return misuseError(err)
	}

	// Error if the secret is empty
	if len(newPlaintext) == 0 {
		err := fmt.Errorf("secret is empty")
		return misuseError(err)
	}

	if bytes.Equal(newPlaintext, originalSecret.Plaintext) {
		err := fmt.Errorf("secret unchanged - not going to update")
		return misuseError(err)
	}

	// Update the secret
	switch t := ref.Type(); t {
	case berglas.ReferenceTypeSecretManager:
		updatedSecret, err := client.Update(ctx, &berglas.SecretManagerUpdateRequest{
			Project:   ref.Project(),
			Name:      ref.Name(),
			Plaintext: newPlaintext,
		})
		if err != nil {
			err = fmt.Errorf("failed to update secret: %w", err)
			return misuseError(err)
		}

		fmt.Fprintf(stdout, "Successfully updated secret [%s] to version [%s]\n",
			updatedSecret.Name, updatedSecret.Version)
	case berglas.ReferenceTypeStorage:
		updatedSecret, err := client.Update(ctx, &berglas.StorageUpdateRequest{
			Bucket:         ref.Bucket(),
			Object:         ref.Object(),
			Generation:     originalSecret.Generation,
			Key:            originalSecret.KMSKey,
			Metageneration: originalSecret.Metageneration,
			Plaintext:      newPlaintext,
		})
		if err != nil {
			err = fmt.Errorf("failed to update secret: %w", err)
			return misuseError(err)
		}

		fmt.Fprintf(stdout, "Successfully updated secret [%s] with generation [%d]\n",
			updatedSecret.Name, updatedSecret.Generation)
	default:
		return misuseError(fmt.Errorf("unknown type %T", t))
	}

	return nil
}