Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline usage, installation and configuration of ots-git-gpg-wrapper.sh #121

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 61 additions & 4 deletions doc/git-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,23 @@ To create and verify these signatures we simply wrap the gpg binary with our
own code, `ots-git-gpg-wrapper`. Git allows you to override the default GnuPG
binary (`/usr/bin/gpg`) with your own using the `gpg.program` config option.
Unfortunately that option doesn't let you set additional command line flags, so
we use one more wrapper, `ots-git-gpg-wrapper.sh`. You can set all this up with the
following:

git config --global gpg.program <path to ots-git-gpg-wrapper.sh>
we use one more wrapper, `ots-git-gpg-wrapper.sh`. You can set all this up with
either of the following:

```bash
# just specify ots-git-gpg-wrapper.sh and let `git` find it itself
git config --global gpg.program ots-git-gpg-wrapper.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "find it itself" doesn't git just use $PATH as normal?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. It is possible to only specify ots-git-gpg-wrapper.sh without a full path if it is in the PATH, which is a convenience as on different machines it can be installed in different places (e.g. ~/.local/bin or /usr/local/bin or /usr/bin/, ...). This is a strong argument for putting it into a standard executable location.

# manually enter the full path to ots-git-gpg-wrapper.sh
git config --global gpg.program <path/to/ots-git-gpg-wrapper.sh>
# auto-detect the full path using `which`
git config --global gpg.program "`which ots-git-gpg-wrapper.sh`"
```

> **Note:** If you get errors that it doesn't find the
> `ots-git-gpg-wrapper.sh`, make sure that your `PATH` includes the
> installation location, e.g. by appending `export
> PATH="$PATH:$HOME/.local/bin"` to your `.bashrc`. You can check the
> installation location with `pip show -f opentimestamps-client`.

Now try creating a test repository and signing a commit:

Expand Down Expand Up @@ -347,3 +360,47 @@ calendar servers:
gpg: using RSA key 6399011044E8AFB2
gpg: Good signature from "Peter Todd <[email protected]>"
gpg: aka "[jpeg image of size 5220]"


Configuration
-------------

The OpenTimestamps GPG wrapper can be configured in the following ways:


```bash
# Disable OpenTimestamps for the current repository:
git config opentimestamps.enable false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more useful to disable opentimestamps selectively for just log viewing?

Also, can you set gpg.program on a per-repo basis?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, can you set gpg.program on a per-repo basis?

Yes, you can. Without --global, git config operates only per-repo.

Would it be more useful to disable opentimestamps selectively for just log viewing?

Sure we can add something like git config opentimestamps.on_log and git config opentimestamps.on_commit or git config commit.opentimestamps etc. for more fine-grained control. But AFAIK it is not so simple or elegant to find the current git operation from within the gpg wrapper. We could find the parent git process' command-line and parse it. Doing this in a shell wrapper is not nice, however. Another argument to making ots-git-gpg-wrapper.sh a Python script (or finding a way to have ots-git-gpg-wrapper do this already.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more useful to disable opentimestamps selectively for just log viewing?

In 31c1bde I implemented picking for which git commands the OpenTimestamps wrapper should be active by setting git config opentimestamps.only-for 'commit,show' (in this case the wrapper will then only do anything different during git commit and git show).

But AFAIK it is not so simple or elegant to find the current git operation from within the gpg wrapper. We could find the parent git process' command-line and parse it. Doing this in a shell wrapper is not nice, however.

Finding out which git command is currently active works in the above way, except if one passes options directly to git (like git --no-pager log). To handle this, the wrapper would need to fully parse the git command-line, involving some guesswork. In my current implementation, the wrapper detects this and skips the check, effectively acting as if the user wanted to use OpenTimestamps for all commands.


# Disable OpenTimestamps by default for all git repositories on this machine:
git config --global opentimestamps.enable false

# Temporarily (re)enable OpenTimestamps signatures in `git log`:
OPENTIMESTAMPS=true git log --show-signature

# Temporarily ignore OpenTimestamps signatures in `git log`:
OPENTIMESTAMPS=false git log --show-signature

# Don't use OpenTimestamps for timestamping for one commit:
OPENTIMESTAMPS=false git commit -m "commit message"

# Only use OpenTimestamps for `git show` and `git commit` (not e.g. `git log`)
git config --global opentimestamps.only-for show,commit

# Don't try to use a local Bitcoin node for verification.
# This gets rids of error messages in `git show` and `git log`
# when you don't have a Bitcoin node running.
git config --global opentimestamps.flags '--no-bitcoin'
```

Troubleshooting
---------------

You can troubleshoot the OpenTimestamps process like this:

```bash
# Debug the OpenTimeStamps process
GIT_TRACE=true OPENTIMESTAMPS_GIT_GPG_WRAPPER_DEBUG=true OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS='-vvvvv' git log --show-signature
```

This however does not seem to work properly for `git commit` unfortunately.
98 changes: 97 additions & 1 deletion ots-git-gpg-wrapper.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to change the #!/bin/sh to #!/bin/bash, as you're using bash-specific commands; newer linux distros like Debian don't put bash in /bin/sh any more.

Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,100 @@
# Required because git's gpg.program option doesn't allow you to set command
# line options; see the doc/git-integration.md

ots-git-gpg-wrapper --gpg-program "`which gpg`" -- "$@"
##############################
### Configuration Examples ###
##############################

# Disable OpenTimestamps for the current repository:
#
# > git config opentimestamps.enable false
#
# Disable OpenTimestamps by default for all git repositories on this machine:
#
# > git config --global opentimestamps.enable false
#
# Temporarily (re)enable OpenTimestamps signatures in `git log`:
#
# > OPENTIMESTAMPS=true git log --show-signature
#
# Temporarily ignore OpenTimestamps signatures in `git log`:
#
# > OPENTIMESTAMPS=false git log --show-signature
#
# Don't use OpenTimestamps for timestamping for one commit:
#
# > OPENTIMESTAMPS=false git commit -m "commit message"
#
# Debug the OpenTimeStamps process for one call:
#
# > OPENTIMESTAMPS_GIT_GPG_WRAPPER_DEBUG=true OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS='-vvvvv' git log --show-signature
#
# Always debug the OpenTimeStamps process:
#
# > git config --global opentimestamps.debug true
# > git config --global opentimestamps.flags -vvvvvvv
#
# Don't attempt to connect to a local Bitcoin node (e.g. for verification).
#
# > git config --global opentimestamps.flags '--no-bitcoin'

# defaults
test -n "$GPG" || GPG=gpg
test -n "$OPENTIMESTAMPS_GIT_GPG_WRAPPER_DEBUG" || OPENTIMESTAMPS_GIT_GPG_WRAPPER_DEBUG="`git config opentimestamps.debug 2>/dev/null || true`"
test -n "$OPENTIMESTAMPS_GIT_GPG_WRAPPER" || OPENTIMESTAMPS_GIT_GPG_WRAPPER=ots-git-gpg-wrapper
test -n "$OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS" || OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS="`git config opentimestamps.flags 2>/dev/null || true`"

function debug() { if is_true "$OPENTIMESTAMPS_GIT_GPG_WRAPPER_DEBUG";then echo >&2 "ots: $@";fi }

# config value pattern matching
true_pattern='^(y(es)?|true|enable)$'
false_pattern='^(no?|false|disable)$'
function check_pattern() { echo "$1" | grep -Eiq "$2"; }
function is_true() { if check_pattern "$1" "$false_pattern";then return 1;fi;check_pattern "$1" "$true_pattern"; }
# This git subcommand-detection fails if there are direct arguments to `git` before the subcommand.
# The full git cmdline should be parsed properly. Instead, we skip the git subcommand check in this case.
git_command="`cat /proc/"$PPID"/cmdline | tr '\0' '\n' | tail -n+2 | head -n1`"
if (echo "$git_command" | grep -qvx '[a-z]\+');then
debug "Can't determine git command if there are direct options to git, sorry..."
git_command=
fi
function opentimestamps_enabled() {
if test -n "$OPENTIMESTAMPS";then
if is_true "$OPENTIMESTAMPS";then
debug "Enabling OpenTimestamps due to OPENTIMESTAMPS='$OPENTIMESTAMPS'"
return 0
else
debug "Disabling OpenTimestamps due to OPENTIMESTAMPS='$OPENTIMESTAMPS'"
return 1
fi
fi
git_config_opentimestamps_only_for="`git config opentimestamps.only-for 2>/dev/null`"
if test -n "$git_config_opentimestamps_only_for" -a -n "$git_command";then
if (echo "$git_config_opentimestamps_only_for" | grep -o '[a-z]\+' | grep -qFx "$git_command" >/dev/null 2>/dev/null);then
debug "Enabling OpenTimestamps as \`git config opentimestamps.only-for\` = '$git_config_opentimestamps_only_for' contains the current git command '$git_command'"
else
debug "Disabling OpenTimestamps as \`git config opentimestamps.only-for\` = '$git_config_opentimestamps_only_for' doesn't contain the current git command '$git_command'"
return 1
fi
fi
git_config_opentimestamps_enable="`git config opentimestamps.enable 2>/dev/null`"
if test -n "$git_config_opentimestamps_enable";then
if is_true "$git_config_opentimestamps_enable";then
debug "Enabling OpenTimestamps due to \`git config opentimestamps.enable\` = '$git_config_opentimestamps_enable'"
return 0
else
debug "Disabling OpenTimestamps due to \`git config opentimestamps.enable\` = '$git_config_opentimestamps_enable'"
return 1
fi
fi
debug "Enabling OpenTimestamps"
return 0
}

if opentimestamps_enabled;then
debug "executing >>>$OPENTIMESTAMPS_GIT_GPG_WRAPPER $OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS -- $@<<<"
exec $OPENTIMESTAMPS_GIT_GPG_WRAPPER $OPENTIMESTAMPS_GIT_GPG_WRAPPER_FLAGS -- "$@"
else
debug "executing >>>$GPG $@<<<"
exec $GPG "$@"
fi
3 changes: 2 additions & 1 deletion otsclient/git_gpg_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import bitcoin
import logging
import subprocess
import shutil

import git
from opentimestamps.core.git import GitTreeTimestamper
Expand All @@ -30,7 +31,7 @@
def main():
parser = otsclient.args.make_common_options_arg_parser()

parser.add_argument("-g", "--gpg-program", action="store", default="/usr/bin/gpg",
parser.add_argument("-g", "--gpg-program", action="store", default=shutil.which("gpg") or "/usr/bin/gpg",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parser.add_argument("-g", "--gpg-program", action="store", default=shutil.which("gpg") or "/usr/bin/gpg",
parser.add_argument("-g", "--gpg-program", action="store", default="gpg",

Looks like Popen already searches in $PATH for us, so if we're going to use $PATH we don't need to figure it out ourselves.

Copy link
Author

@nobodyinperson nobodyinperson Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. But then it was also unnecessary to provide

--gpg-program "`which gpg`"

in ots-git-gpg-wrapper.sh.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nice side-effect of specifying the shutil.which()-found path is that the help page will show exactly which gpg program will be used. This wouldn't be the case if we just let Popen find it.

help="Path to the GnuPG binary (default %(default)s)")

parser.add_argument('-c','--calendar', metavar='URL', dest='calendar_urls', action='append', type=str,
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,7 @@
'ots-git-gpg-wrapper = otsclient.git_gpg_wrapper:main',
],
},

# Install the ots-git-gpg-wrapper.sh-script
scripts = ["ots-git-gpg-wrapper.sh"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue with installing the script is it'll clutter up tab-completion with another program starting with ots. Is there a better place to put this? Under /usr/lib/ or something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #121 (comment)

Not putting it into PATH forces one to hard-code an absolute path with git config gpg.program. For people synchronizing their .gitconfig for example this is annoying as the wrapper can be installed in different places on different machines.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If only the namespace cluttering is the problem with putting the ots-git-gpg-wrapper.sh into a location in PATH, we could just rename it to something like opentimestamps-git-gpg-wrapper.sh, or gpg-opentimestamps-wrapper.sh. This will work around tab-completition with shells that get winy if there are multiple possible commands (instead of just completing up to the common stem like zsh does).

)