Skip to content

Commit

Permalink
Merge pull request #8305 from dolthub/bh/signed-commits
Browse files Browse the repository at this point in the history
Bh/signed commits
  • Loading branch information
bheni authored Sep 17, 2024
2 parents ecee0e0 + fdb34d6 commit 4eee94b
Show file tree
Hide file tree
Showing 48 changed files with 1,683 additions and 51 deletions.
2 changes: 2 additions & 0 deletions go/cmd/dolt/cli/arg_parser_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func CreateCommitArgParser() *argparser.ArgParser {
ap.SupportsFlag(AllFlag, "a", "Adds all existing, changed tables (but not new tables) in the working set to the staged set.")
ap.SupportsFlag(UpperCaseAllFlag, "A", "Adds all tables and databases (including new tables) in the working set to the staged set.")
ap.SupportsFlag(AmendFlag, "", "Amend previous commit")
ap.SupportsOptionalString(SignFlag, "S", "key-id", "Sign the commit using GPG. If no key-id is provided the key-id is taken from 'user.signingkey' the in the configuration")
return ap
}

Expand Down Expand Up @@ -279,6 +280,7 @@ func CreateLogArgParser(isTableFunction bool) *argparser.ArgParser {
ap.SupportsFlag(ParentsFlag, "", "Shows all parents of each commit in the log.")
ap.SupportsString(DecorateFlag, "", "decorate_fmt", "Shows refs next to commits. Valid options are short, full, no, and auto")
ap.SupportsStringList(NotFlag, "", "revision", "Excludes commits from revision.")
ap.SupportsFlag(ShowSignatureFlag, "", "Shows the signature of each commit.")
if isTableFunction {
ap.SupportsStringList(TablesFlag, "t", "table", "Restricts the log to commits that modified the specified tables.")
} else {
Expand Down
2 changes: 2 additions & 0 deletions go/cmd/dolt/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const (
SetUpstreamFlag = "set-upstream"
ShallowFlag = "shallow"
ShowIgnoredFlag = "ignored"
ShowSignatureFlag = "show-signature"
SignFlag = "gpg-sign"
SilentFlag = "silent"
SingleBranchFlag = "single-branch"
SkipEmptyFlag = "skip-empty"
Expand Down
6 changes: 5 additions & 1 deletion go/cmd/dolt/cli/stdio.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@ func SetIOStreams(inStream io.ReadCloser, outStream io.WriteCloser) {
}

func InitIO() (restoreIO func()) {
outFile := filepath.Join(os.TempDir(), uuid.New().String())
return InitIOWithFile(outFile)
}

func InitIOWithFile(outFile string) (restoreIO func()) {
stdOut, stdErr := os.Stdout, os.Stderr

outFile := filepath.Join(os.TempDir(), uuid.New().String())
f, err := os.Create(outFile)

if err == nil {
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/cherry-pick.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ hint: commit your changes (dolt commit -am \"<message>\") or reset them (dolt re
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, false, "auto", commit)
PrintCommitInfo(pager, 0, false, false, "auto", commit)
})

return nil
Expand Down
14 changes: 13 additions & 1 deletion go/cmd/dolt/commands/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func performCommit(ctx context.Context, commandStr string, args []string, cliCtx
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, false, "auto", commit)
PrintCommitInfo(pager, 0, false, false, "auto", commit)
})
}

Expand Down Expand Up @@ -268,6 +268,18 @@ func constructParametrizedDoltCommitQuery(msg string, apr *argparser.ArgParseRes
writeToBuffer("--skip-empty")
}

cfgSign := cliCtx.Config().GetStringOrDefault("sqlserver.global.gpgsign", "")
if apr.Contains(cli.SignFlag) || strings.ToLower(cfgSign) == "true" {
writeToBuffer("--gpg-sign")

gpgKey := apr.GetValueOrDefault(cli.SignFlag, "")
if gpgKey != "" {
param = true
writeToBuffer("?")
params = append(params, gpgKey)
}
}

buffer.WriteString(")")
return buffer.String(), params, nil
}
Expand Down
8 changes: 6 additions & 2 deletions go/cmd/dolt/commands/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,14 @@ func getExistingTables(revisions []string, queryist cli.Queryist, sqlCtx *sql.Co

// logCommits takes a list of sql rows that have only 1 column, commit hash, and retrieves the commit info for each hash to be printed to std out
func logCommits(apr *argparser.ArgParseResults, commitHashes []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) error {
opts := commitInfoOptions{
showSignature: apr.Contains(cli.ShowSignatureFlag),
}

var commitsInfo []CommitInfo
for _, hash := range commitHashes {
cmHash := hash[0].(string)
commit, err := getCommitInfo(queryist, sqlCtx, cmHash)
commit, err := getCommitInfoWithOptions(queryist, sqlCtx, cmHash, opts)
if commit == nil {
return fmt.Errorf("no commits found for ref %s", cmHash)
}
Expand Down Expand Up @@ -338,7 +342,7 @@ func logCompact(pager *outputpager.Pager, apr *argparser.ArgParseResults, commit

func logDefault(pager *outputpager.Pager, apr *argparser.ArgParseResults, commits []CommitInfo, sqlCtx *sql.Context, queryist cli.Queryist) error {
for _, comm := range commits {
PrintCommitInfo(pager, apr.GetIntOrDefault(cli.MinParentsFlag, 0), apr.Contains(cli.ParentsFlag), apr.GetValueOrDefault(cli.DecorateFlag, "auto"), &comm)
PrintCommitInfo(pager, apr.GetIntOrDefault(cli.MinParentsFlag, 0), apr.Contains(cli.ParentsFlag), apr.Contains(cli.ShowSignatureFlag), apr.GetValueOrDefault(cli.DecorateFlag, "auto"), &comm)
if apr.Contains(cli.StatFlag) {
if comm.parentHashes != nil && len(comm.parentHashes) == 1 { // don't print stats for merge commits
diffStats := make(map[string]*merge.MergeStats)
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ func printMergeStats(fastForward bool,
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, false, "auto", commit)
PrintCommitInfo(pager, 0, false, false, "auto", commit)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (cmd PullCmd) Exec(ctx context.Context, commandStr string, args []string, d
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, false, "auto", commit)
PrintCommitInfo(pager, 0, false, false, "auto", commit)
})
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/revert.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (cmd RevertCmd) Exec(ctx context.Context, commandStr string, args []string,
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, false, "auto", commit)
PrintCommitInfo(pager, 0, false, false, "auto", commit)
})

return 0
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func fetchAndPrintCommit(queryist cli.Queryist, sqlCtx *sql.Context, opts *showO
pager := outputpager.Start()
defer pager.Stop()

PrintCommitInfo(pager, 0, opts.showParents, opts.decoration, commit)
PrintCommitInfo(pager, 0, opts.showParents, false, opts.decoration, commit)
})

if len(parents) == 0 {
Expand Down
197 changes: 197 additions & 0 deletions go/cmd/dolt/commands/signed_commits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package commands

import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"testing"

"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/config"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/libraries/utils/gpg"

"github.com/stretchr/testify/require"
)

const keyId = "573DA8C6366D04E35CDB1A44E09A0B208F666373"

func importKey(t *testing.T, ctx context.Context) {
err := gpg.ImportKey(ctx, "testdata/signed_commits/private.pgp")
require.NoError(t, err)

ok, err := gpg.HasKey(ctx, keyId)
require.NoError(t, err)
require.True(t, ok)
}

func setupTestDB(t *testing.T, ctx context.Context, fs filesys.Filesys) string {
dir, err := os.MkdirTemp(os.TempDir(), "signed_commits")
require.NoError(t, err)
dbDir := filepath.Join(dir, "db")
err = filesys.CopyDir("testdata/signed_commits/db/", dbDir, fs)
require.NoError(t, err)

log.Println(dbDir)
return dbDir
}

func TestSignAndVerifyCommit(t *testing.T) {
tests := []struct {
name string
localCfg map[string]string
commitArgs []string
expectErr bool
}{
{
name: "sign commit with command line key id",
localCfg: map[string]string{},
commitArgs: []string{"-S", keyId, "-m", "test commit"},
expectErr: false,
},
{
name: "sign no key id, no keyid in config",
localCfg: map[string]string{},
commitArgs: []string{"-S", "-m", "test commit"},
expectErr: true,
},
}

ctx := context.Background()
importKey(t, ctx)
dbDir := setupTestDB(t, ctx, filesys.LocalFS)

global := map[string]string{
"user.name": "First Last",
"user.email": "[email protected]",
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
apr, err := cli.CreateCommitArgParser().Parse(test.commitArgs)
require.NoError(t, err)

_, err = execCommand(ctx, dbDir, CommitCmd{}, test.commitArgs, apr, map[string]string{}, global)

if test.expectErr {
require.Error(t, err)
return
} else {
require.NoError(t, err)
}

args := []string{"--show-signature"}
apr, err = cli.CreateLogArgParser(false).Parse(args)
require.NoError(t, err)

logOutput, err := execCommand(ctx, dbDir, LogCmd{}, args, apr, map[string]string{}, global)
require.NoError(t, err)
require.Contains(t, logOutput, "Good signature from \"Test User <[email protected]>\"")
})
}
}

func execCommand(ctx context.Context, wd string, cmd cli.Command, args []string, apr *argparser.ArgParseResults, local, global map[string]string) (output string, err error) {
err = os.Chdir(wd)
if err != nil {
err = fmt.Errorf("error changing directory to %s: %w", wd, err)
return
}

var fs filesys.Filesys
fs, err = filesys.LocalFilesysWithWorkingDir(wd)
if err != nil {
err = fmt.Errorf("error creating local filesystem with working directory %s: %w", wd, err)
return
}

dEnv := env.Load(context.Background(), testHomeDirFunc, fs, ".", "test")

ch := config.NewConfigHierarchy()
if global != nil {
ch.AddConfig("global", config.NewMapConfig(global))
}

if local != nil {
ch.AddConfig("local", config.NewMapConfig(local))
}

cfg := env.NewTestDoltCliConfigFromHierarchy(ch, fs)

mr, err := env.MultiEnvForDirectory(ctx, ch, fs, dEnv.Version, dEnv)
if err != nil {
err = fmt.Errorf("error creating multi repo: %w", err)
return
}

latebind, verr := BuildSqlEngineQueryist(ctx, dEnv.FS, mr, &cli.UserPassword{}, apr)
if verr != nil {
err = fmt.Errorf("error building sql engine: %w", verr)
return
}

cliCtx, err := cli.NewCliContext(apr, cfg, latebind)
if err != nil {
err = fmt.Errorf("error creating cli context: %w", err)
return
}

initialOut := os.Stdout
initialErr := os.Stderr
f, err := os.CreateTemp(os.TempDir(), "signed-commit-test-*")
if err != nil {
err = fmt.Errorf("error creating temp file: %w", err)
return
}

os.Stdout = f
os.Stderr = f
restoreIO := cli.InitIO()

defer func() {
closeErr := f.Close()
if closeErr != nil && err == nil {
err = fmt.Errorf("error closing temp file: %w", closeErr)
}

restoreIO()

os.Stdout = initialOut
os.Stderr = initialErr

outputBytes, readOutputErr := os.ReadFile(f.Name())
if readOutputErr != nil && err == nil {
err = fmt.Errorf("error reading temp file: %w", readOutputErr)
return
}

output = string(outputBytes)
}()

n := cmd.Exec(ctx, cmd.Name(), args, dEnv, cliCtx)
if n != 0 {
err = fmt.Errorf("command %s %s exited with code %d", cmd.Name(), strings.Join(args, " "), n)
return
}

return
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Empty file.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5:__DOLT__:brqj5m8e9b0s0rtjmhqp4pn1k8e2akqv:8m5mnvhavk3k6b409fajpv9iipokkeuj:00000000000000000000000000000000:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:34
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"head": "refs/heads/main",
"remotes": {},
"backups": {},
"branches": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Empty file.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5:__DOLT__:jrhi6484op854sujfe0pcqpkme0i6bto:mt40uhrd930kmdnu07taara6u8ecm84d:00000000000000000000000000000000:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:9
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"head": "refs/heads/main",
"remotes": {},
"backups": {},
"branches": {}
}
Loading

0 comments on commit 4eee94b

Please sign in to comment.