├── rust ├── .gitignore ├── rustfmt.toml ├── Cargo.toml ├── Makefile-inc.am ├── meson.build └── src │ └── main.rs ├── tests ├── git-evtag-compute-py ├── gpghome │ ├── secring.gpg │ ├── trustdb.gpg │ ├── trusted │ │ ├── pubring.gpg │ │ └── meson.build │ ├── meson.build │ ├── key1.asc │ ├── key2.asc │ └── key3.asc ├── tap.test.in ├── tap-test ├── meson.build ├── Makefile-tests.am ├── test-basic.sh └── libtest.sh ├── COPYING.README ├── src ├── meson.build ├── Makefile-git-evtag.am ├── git-evtag-compute-py └── git-evtag.c ├── packaging ├── meson-compat-install-symlink.sh ├── rpmbuild-cwd ├── git-evtag.spec.in ├── Makefile.dist-packaging └── builddeps.sh ├── autogen.sh ├── .travis.yml ├── meson_options.txt ├── LICENSE-MIT ├── man ├── meson.build ├── Makefile-man.am └── git-evtag.xml ├── Makefile-decls.am ├── Makefile.am ├── configure.ac ├── meson.build ├── .github └── workflows │ └── check.yml ├── README.md ├── LICENSE-APACHE ├── git.mk └── COPYING /rust/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /rust/rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_try_shorthand = true 2 | -------------------------------------------------------------------------------- /tests/git-evtag-compute-py: -------------------------------------------------------------------------------- 1 | ../src/git-evtag-compute-py -------------------------------------------------------------------------------- /tests/gpghome/secring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwalters/git-evtag/HEAD/tests/gpghome/secring.gpg -------------------------------------------------------------------------------- /tests/gpghome/trustdb.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwalters/git-evtag/HEAD/tests/gpghome/trustdb.gpg -------------------------------------------------------------------------------- /tests/gpghome/trusted/pubring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwalters/git-evtag/HEAD/tests/gpghome/trusted/pubring.gpg -------------------------------------------------------------------------------- /tests/tap.test.in: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | [Test] 5 | Type=session 6 | Exec=@insttestdir@/@basename@ 7 | Output=TAP 8 | -------------------------------------------------------------------------------- /COPYING.README: -------------------------------------------------------------------------------- 1 | There are currently 3 independent implementations of `git-evtag` that 2 | live in this source tree. 3 | 4 | - src/git-evtag.c (LGPLv2+) 5 | - src/git-evtag-compute-py (LGPLv2+) 6 | - src/rust (MIT or Apache 2.0, same as Rust) 7 | -------------------------------------------------------------------------------- /tests/gpghome/trusted/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | if get_option('install_tests') 5 | install_data( 6 | 'pubring.gpg', 7 | install_dir : insttestdir / 'gpghome/trusted', 8 | ) 9 | endif 10 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | executable( 5 | 'git-evtag', 6 | ['git-evtag.c'], 7 | include_directories : common_include_directories, 8 | install : true, 9 | dependencies : [glib_dep, libgit_glib_dep], 10 | ) 11 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "git-evtag" 3 | version = "0.1.0" 4 | authors = ["Colin Walters "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | anyhow = "1.0.34" 9 | clap = "2.32.0" 10 | structopt = "0.2" 11 | git2 = "0.13.12" 12 | hex = "0.4.2" 13 | openssl = "0.10" 14 | 15 | -------------------------------------------------------------------------------- /rust/Makefile-inc.am: -------------------------------------------------------------------------------- 1 | if ENABLE_RUST 2 | bin_PROGRAMS += git-rustevtag 3 | git_rustevtag_SOURCES = \ 4 | rust/Cargo.toml \ 5 | rust/src/main.rs \ 6 | $(NULL) 7 | 8 | git-rustevtag: $(git_rustevtag_SOURCES) 9 | (cd rust && cargo build) && cp rust/target/debug/git-evtag "$@" 10 | endif 11 | 12 | EXTRA_DIST += rust/meson.build 13 | EXTRA_DIST += rust/rustfmt.toml 14 | -------------------------------------------------------------------------------- /packaging/meson-compat-install-symlink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2022 Simon McVittie 3 | # SPDX-License-Identifier: MIT 4 | 5 | # Compatibility shim for installing symlinks with Meson < 0.61 6 | 7 | set -eu 8 | 9 | case "$2" in 10 | (/*) 11 | ln -fns "$3" "$DESTDIR/$2/$1" 12 | ;; 13 | (*) 14 | ln -fns "$3" "$MESON_INSTALL_DESTDIR_PREFIX/$2/$1" 15 | ;; 16 | esac 17 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | test -n "$srcdir" || srcdir="$(dirname "$0")" 5 | test -n "$srcdir" || srcdir=. 6 | 7 | olddir="$(pwd)" 8 | cd "$srcdir" 9 | 10 | if ! command -v autoreconf >/dev/null 2>&1 11 | then 12 | echo "*** No autoreconf found, please intall it ***" 13 | exit 1 14 | fi 15 | 16 | mkdir -p m4 17 | 18 | autoreconf --force --install --verbose 19 | 20 | cd "$olddir" 21 | test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # We're just using a "stubbed out" Travis right now so we can 2 | # use Homu to auto-squash 3 | # etc. 4 | # The problem is the Ubuntu package of libgit2 is old, 5 | # and anyways I want to test with Fedora/CentOS content. 6 | language: c 7 | install: true 8 | script: test '!' -f broken.txt 9 | 10 | notifications: 11 | webhooks: http://escher.verbum.org:54856/travis 12 | email: false 13 | 14 | branches: 15 | only: 16 | - auto 17 | 18 | -------------------------------------------------------------------------------- /rust/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | cargo = find_program('cargo', required : get_option('build_rust_version')) 5 | 6 | if cargo.found() 7 | custom_target( 8 | 'git-rustevtag', 9 | input : ['Cargo.toml', 'src/main.rs'], 10 | output : 'git-rustevtag', 11 | command : [ 12 | 'sh', '-euc', 13 | '"$1" build && cp target/debug/git-evtag "$2"', 14 | 'sh', 15 | cargo, 16 | '@OUTPUT0@', 17 | ], 18 | ) 19 | endif 20 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | option( 5 | 'install_tests', 6 | type : 'boolean', 7 | description : 'Install test programs', 8 | value : false, 9 | ) 10 | option( 11 | 'man', 12 | type : 'feature', 13 | description : 'Generate man pages', 14 | value : 'auto', 15 | ) 16 | # "Option name rust is reserved" 17 | option( 18 | 'build_rust_version', 19 | type : 'feature', 20 | description : 'Compile Rust version', 21 | value : 'disabled', 22 | ) 23 | -------------------------------------------------------------------------------- /tests/gpghome/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | if get_option('install_tests') 5 | install_data( 6 | 'key1.asc', 7 | 'key2.asc', 8 | 'key3.asc', 9 | 'secring.gpg', 10 | 'trustdb.gpg', 11 | install_dir : insttestdir / 'gpghome', 12 | ) 13 | 14 | install_symlinks += { 15 | 'link_name' : 'pubring.gpg', 16 | 'install_dir' : insttestdir / 'gpghome', 17 | 'pointing_to' : 'trusted/pubring.gpg', 18 | } 19 | endif 20 | 21 | subdir('trusted') 22 | -------------------------------------------------------------------------------- /packaging/rpmbuild-cwd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # rpmbuild-cwd: 3 | # Run "rpmbuild", defining all RPM variables to use the current directory. 4 | # This matches Fedora's system. 5 | # 6 | # Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php) 7 | # Copyright (C) 2010 Red Hat, Inc. 8 | # Written by Colin Walters 9 | 10 | pwd=$(pwd) 11 | exec rpmbuild --define "_sourcedir ${pwd}" --define "_specdir ${pwd}" --define "_builddir ${pwd}" --define "_srcrpmdir ${pwd}" --define "_rpmdir ${pwd}" --define "_buildrootdir ${pwd}/.build" "$@" 12 | -------------------------------------------------------------------------------- /packaging/git-evtag.spec.in: -------------------------------------------------------------------------------- 1 | Summary: Strong GPG verification of git tags 2 | Name: git-evtag 3 | Version: 2016.1 4 | Release: 1%{?dist} 5 | 6 | License: LGPLv2+ 7 | #VCS: https://github.com/cgwalters/git-evtag 8 | URL: https://github.com/cgwalters/git-evtag 9 | Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz 10 | 11 | BuildRequires: gcc 12 | 13 | # We always run autogen.sh 14 | BuildRequires: autoconf automake libtool 15 | 16 | BuildRequires: pkgconfig(libgit2) 17 | BuildRequires: pkgconfig(gio-2.0) 18 | 19 | Requires: git 20 | Requires: gnupg2 21 | 22 | %description 23 | git-evtag wraps "git tag" functionality, adding stronger checksums 24 | that cover the complete content. 25 | 26 | %prep 27 | %autosetup 28 | 29 | %build 30 | env NOCONFIGURE=1 ./autogen.sh 31 | %configure --disable-silent-rules 32 | %make_build 33 | 34 | %install 35 | %make_install INSTALL="install -p -c" 36 | 37 | %files 38 | %license COPYING 39 | %doc README.md 40 | %{_bindir}/%{name} 41 | -------------------------------------------------------------------------------- /tests/tap-test: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Run a test in tap mode, ensuring we have a temporary directory. We 4 | # always use /var/tmp becuase we might want to use user xattrs, which 5 | # aren't available on tmpfs. 6 | # 7 | # The test binary is passed as $1 8 | 9 | set -eu 10 | 11 | srcd="$(cd "$(dirname "$1")" && pwd)" 12 | bn="$(basename "$1")" 13 | TEST_TMPDIR="${TEST_TMPDIR:-/var/tmp}" 14 | tempdir="$(mktemp -d "$TEST_TMPDIR/tap-test.XXXXXX")" 15 | touch "${tempdir}/.testtmp" 16 | function cleanup () { 17 | if test -f "${tempdir}/.testtmp"; then 18 | rm "${tempdir}" -rf 19 | fi 20 | } 21 | function skip_cleanup() { 22 | echo "Skipping cleanup of ${tempdir}" 23 | } 24 | cd "${tempdir}" 25 | rc=0 26 | timeout \ 27 | --kill-after=60 \ 28 | --signal=ABRT \ 29 | $(( 600 * ${TEST_TIMEOUT_FACTOR:-1} )) \ 30 | "${srcd}/${bn}" -k --tap || rc=$? 31 | case "${TEST_SKIP_CLEANUP:-}" in 32 | no|"") cleanup ;; 33 | err) 34 | if test "$rc" != 0; then 35 | skip_cleanup 36 | else 37 | cleanup 38 | fi ;; 39 | *) skip_cleanup ;; 40 | esac 41 | exit "$rc" 42 | -------------------------------------------------------------------------------- /src/Makefile-git-evtag.am: -------------------------------------------------------------------------------- 1 | # Makefile for C source code 2 | # 3 | # Copyright (C) 2015 Colin Walters 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General 16 | # Public License along with this library; if not, see . 17 | 18 | bin_PROGRAMS += git-evtag 19 | 20 | git_evtag_SOURCES = src/git-evtag.c \ 21 | $(NULL) 22 | 23 | git_evtag_CFLAGS = $(AM_CFLAGS) $(BUILDDEP_LIBGIT_GLIB_CFLAGS) -I$(srcdir)/src 24 | git_evtag_LDADD = $(BUILDDEP_LIBGIT_GLIB_LIBS) 25 | 26 | GITIGNOREFILES += src/.dirstamp 27 | 28 | EXTRA_DIST += src/meson.build 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /packaging/Makefile.dist-packaging: -------------------------------------------------------------------------------- 1 | # -*- mode: Makefile -*- 2 | 3 | GITREV = $$(git describe --always --tags) 4 | GITREV_FOR_PKG = $(shell echo "$(GITREV)" | sed -e 's,-,\.,g' -e 's,^v,,') 5 | 6 | srcdir=$(shell git rev-parse --show-toplevel) 7 | PACKAGE=git-evtag 8 | 9 | PKG_VER = $(PACKAGE)-$(GITREV_FOR_PKG) 10 | 11 | dist-snapshot: 12 | set -x; \ 13 | echo "PACKAGE=$(PACKAGE)"; \ 14 | TARFILE_TMP=$(PKG_VER).tar.tmp; \ 15 | echo "Archiving $(PACKAGE) at $(GITREV)"; \ 16 | (cd $(srcdir); git archive --format=tar --prefix=$(PKG_VER)/ $(GITREV)) > $${TARFILE_TMP}; \ 17 | (cd $$(git rev-parse --show-toplevel); git submodule status) | while read line; do \ 18 | rev=$$(echo $$line | cut -f 1 -d ' '); path=$$(echo $$line | cut -f 2 -d ' '); \ 19 | echo "Archiving $${path} at $${rev}"; \ 20 | (cd $(srcdir)/$$path; git archive --format=tar --prefix=$(PKG_VER)/$$path/ $${rev}) > submodule.tar; \ 21 | tar -A -f $${TARFILE_TMP} submodule.tar; \ 22 | rm submodule.tar; \ 23 | done; \ 24 | mv $(PKG_VER).tar{.tmp,}; \ 25 | rm -f $(PKG_VER).tar.xz; \ 26 | xz $(PKG_VER).tar 27 | 28 | srpm: dist-snapshot 29 | sed -e "s,^Version:.*,Version: $(GITREV_FOR_PKG)," $(PACKAGE).spec.in > $(PACKAGE).spec; \ 30 | ./rpmbuild-cwd -bs $(PACKAGE).spec 31 | 32 | rpm: srpm 33 | ./rpmbuild-cwd --rebuild $(PKG_VER)*.src.rpm 34 | -------------------------------------------------------------------------------- /man/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | manpages_xsl = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' 5 | xsltproc = find_program('xsltproc', required : get_option('man')) 6 | build_man_page = false 7 | 8 | if xsltproc.found() 9 | if run_command([ 10 | xsltproc, '--nonet', manpages_xsl, 11 | ], check : false).returncode() == 0 12 | message('Docbook XSL found, man page enabled') 13 | build_man_page = true 14 | elif get_option('man').enabled() 15 | error('Man page requested, but Docbook XSL stylesheets not found') 16 | else 17 | message('Docbook XSL not found, man page disabled automatically') 18 | endif 19 | endif 20 | 21 | if build_man_page 22 | custom_target( 23 | 'git-evtag.1', 24 | output : 'git-evtag.1', 25 | input : 'git-evtag.xml', 26 | command : [ 27 | xsltproc, 28 | '--nonet', 29 | '--stringparam', 'man.output.quietly', '1', 30 | '--stringparam', 'funcsynopsis.style', 'ansi', 31 | '--stringparam', 'man.th.extra1.suppress', '1', 32 | '--stringparam', 'man.authors.section.enabled', '0', 33 | '--stringparam', 'man.copyright.section.enabled', '0', 34 | '-o', '@OUTPUT@', 35 | manpages_xsl, 36 | '@INPUT@', 37 | ], 38 | install : true, 39 | install_dir : get_option('mandir') / 'man1', 40 | ) 41 | endif 42 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | insttestdir = get_option('libexecdir') / 'installed-tests' 5 | insttestmetadir = get_option('datadir') / 'installed-tests' / meson.project_name() 6 | 7 | test_scripts = [ 8 | 'test-basic.sh', 9 | ] 10 | 11 | test_env = environment() 12 | test_env.prepend('PATH', project_build_root / 'src') 13 | test_env.set('G_TEST_BUILDDIR', project_build_root) 14 | test_env.set('G_TEST_SRCDIR', project_source_root) 15 | 16 | foreach test_script : test_scripts 17 | test( 18 | test_script, 19 | files('tap-test'), 20 | args : [files(test_script)], 21 | env : test_env, 22 | protocol : 'tap', 23 | ) 24 | 25 | if get_option('install_tests') 26 | configure_file( 27 | input : 'tap.test.in', 28 | output : test_script + '.test', 29 | configuration : { 30 | 'basename' : test_script, 31 | 'insttestdir' : get_option('prefix') / insttestdir, 32 | }, 33 | install_dir : insttestmetadir, 34 | ) 35 | endif 36 | endforeach 37 | 38 | if get_option('install_tests') 39 | install_data( 40 | 'libtest.sh', 41 | install_dir : insttestdir, 42 | ) 43 | install_data( 44 | test_scripts + [ 45 | project_source_root / 'src/git-evtag-compute-py', 46 | ], 47 | install_dir : insttestdir, 48 | install_mode : 'rwxr-xr-x', 49 | ) 50 | endif 51 | 52 | subdir('gpghome') 53 | -------------------------------------------------------------------------------- /Makefile-decls.am: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011,2014 Colin Walters 2 | # 3 | # This library is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation; either 6 | # version 2 of the License, or (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General 14 | # Public License along with this library; if not, see . 15 | 16 | # Common variables 17 | ACLOCAL_AMFLAGS = 18 | AM_CPPFLAGS = 19 | AM_CFLAGS = 20 | DISTCHECK_CONFIGURE_FLAGS = 21 | SUBDIRS = 22 | NULL = 23 | BUILT_SOURCES = 24 | MANPAGES = 25 | CLEANFILES = 26 | EXTRA_DIST = 27 | bin_PROGRAMS = 28 | sbin_PROGRAMS = 29 | bin_SCRIPTS = 30 | lib_LTLIBRARIES = 31 | libexec_PROGRAMS = 32 | noinst_LTLIBRARIES = 33 | noinst_PROGRAMS = 34 | privlibdir = $(pkglibdir) 35 | privlib_LTLIBRARIES = 36 | pkgconfigdir = $(libdir)/pkgconfig 37 | pkgconfig_DATA = 38 | INTROSPECTION_GIRS = 39 | girdir = $(datadir)/gir-1.0 40 | gir_DATA = 41 | typelibdir = $(libdir)/girepository-1.0 42 | typelib_DATA = 43 | gsettings_SCHEMAS = 44 | TESTS = 45 | # git.mk 46 | GITIGNOREFILES = 47 | 48 | # This is a special facility to chain together hooks easily 49 | INSTALL_DATA_HOOKS = 50 | install-data-hook: $(INSTALL_DATA_HOOKS) 51 | -------------------------------------------------------------------------------- /man/Makefile-man.am: -------------------------------------------------------------------------------- 1 | # Makefile for man/ 2 | # 3 | # Copyright (C) 2016 Colin Walters 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General 16 | # Public License along with this library; if not, see . 17 | 18 | if ENABLE_MAN 19 | 20 | man1_files = git-evtag.1 21 | 22 | man1_MANS = $(addprefix man/,$(man1_files)) 23 | 24 | EXTRA_DIST += $(man1_MANS) $(man5_MANS) 25 | 26 | XSLT_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl 27 | 28 | XSLTPROC_FLAGS = \ 29 | --nonet \ 30 | --stringparam man.output.quietly 1 \ 31 | --stringparam funcsynopsis.style ansi \ 32 | --stringparam man.th.extra1.suppress 1 \ 33 | --stringparam man.authors.section.enabled 0 \ 34 | --stringparam man.copyright.section.enabled 0 35 | 36 | XSLTPROC_MAN = $(XSLTPROC) $(XSLTPROC_FLAGS) 37 | 38 | %.1: %.xml 39 | $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_STYLESHEET) $< 40 | 41 | CLEANFILES += \ 42 | $(man1_MANS) \ 43 | $(NULL) 44 | 45 | endif 46 | 47 | EXTRA_DIST += man/git-evtag.xml 48 | EXTRA_DIST += man/meson.build 49 | -------------------------------------------------------------------------------- /tests/gpghome/key1.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1 3 | 4 | mQENBFIuhBYBCADTbnocQsJgMfOELkFt3wRrAZShijoBPYZT9BrIuIKZxAbaxZJr 5 | Tbw8eIGgHZ51NCfdoikul0i82dt4hwtsACNVL5EGRmvTIKHPacb0yJMr1YBjcSwD 6 | Slo+niLPb/oVtLTbDWFt/msYKREF/lGJT9dJyXkQ5UOwWdipDaHIlwb0IKUvL7cu 7 | NpNthRFRm1M5d5M9OtqTCrCja6zckQ6OfvoStsbneHzfVWeH7vLcKBxxkfDhusVt 8 | y1iVaDk1EYT8ZxsrAWw4S7nRK/bjr86IYpFPjG2aKMd9qxyIo7hcX4r8od24jzfM 9 | v/ysOapnkTJuv8J6v7MakM1HkCz+TKF6gXxVABEBAAG0HU9zdHJlZSBUZXN0ZXIg 10 | PHRlc3RAdGVzdC5jb20+iQE5BBMBAgAjBQJSLoQWAhsDBwsJCAcDAgEGFQgCCQoL 11 | BBYCAwECHgECF4AACgkQf8oj2Ecs2vr/9wgAnme6WsWQy8CYeGH4q/5I6XFL6q1m 12 | S0+qdeGnYRmR0jJAGJ84vqDhnKxjeQzp+8Nq81DHGEJBszCkMW2o22neFi2Mo95h 13 | Dq3GWNZVldCDshjPs563AY6j7zACUN7Cy5XB3MK/vj5R/SrHBtJmSgPTx9WfmUgn 14 | n5Udg+fzSsS8z8DUtJFtexgrSnEmwH+nOmIfrsjIYL5EPg+CTTalhygROrERjINr 15 | pCYiShaFCKbuyt/XvyQ71y0JbB2yS7tDv0mL4SZjSuBQ1PkNE8ZQsymqBOJHA1Y3 16 | ppgPs1OenmtYgxaR8HQQv7uxHWZz0dmwQN93Qx8zMZwW40Odmdh1zLNQf7kBDQRS 17 | LoQWAQgA9i9QWg28qmFrPIzn90ZlNlUtFzoZy/8/lIk34awge1uO5aHydYBzkuWU 18 | jCDyBtQLWZQlwOKq8oHBbjENR2sfsmNkrYKcceQ02hSXqEJkc6jcDMCpB9eWy34K 19 | sPZmdl76Eo/vIIgRqJ9JPeGoMPaIBg2ouEz6Ft6jcX3EriYIKebCEA9wPk29z40x 20 | 7D8mBZn06WrZ3JyePfbCdNJlQANEnrk7KDMNwPhhE1wcfPkiVtqBR0/FwIoUP0jn 21 | PishIWOuFObYnXQQ2R8sxrw/V0hGqVTh+k+iNAjzEp4yPsAvB+LdMH9nCY5rU3Vo 22 | 1paEqVM1EHoBPu4NupRN0AjIJPr5UQARAQABiQEfBBgBAgAJBQJSLoQWAhsMAAoJ 23 | EH/KI9hHLNr68i4IAMdc+EgAaPZFWZcXFGBfhnOKQFC/u/W6Cu1JjqIYkGO0HxSh 24 | SfBkxArqlp37w4YVH4bUku6ja421bfGFNMtMfXjw2mU3HRdaDenP6OGv2jYmYmFt 25 | 6zi0JZZhvi8ZCcAQTStZ2Ms3hwstCMiBXPmYA7KW9Gzo4JQSKCW5haICGVSWl7kh 26 | n0OkhOTVI9uzNr7+LhYn2ib/ynSaMKeI4hZ8v1HDuY0V1E63vFPGLFBTPaoRPpnm 27 | 9yBnXMWhrbV97L6eEoe7faurSyPcF11LEFC5x8oENnbH+wtAXOayQo3lld+JRa9C 28 | JEZl8STdRU9o2NFwF8XM8BEOWntMS8aNpPoILC8= 29 | =ZNNc 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /tests/gpghome/key2.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1 3 | 4 | mQENBFUIM4ABCADYBuvzGgzv5nMy2wICv79l+N4bN9/o9hTdFEOzyAeCEaF5Wugc 5 | L9nfTgUS9NRHsSpGt9DeZVEzRm5XzccgHOPs7MlYH0Irhc4Hb9ycOO2vBZ7ZiBK/ 6 | jbY+R5GN4Ut9XIRexbXWddOjJpRUTCWQeXw5iqV9Puqm1ge2Vcal+NZOi2AFRKKe 7 | p/QI9EXIIx7ca6OWtH2SS/qE2p9obDYsMNrW+Dk623dbNKQiWaWyfRD+hB91UNbt 8 | vK7agokTeU0hKr9C8dHrhepgl9B/Hz8SFibZQQiTxSiVH3fUu10eQsyuDC/01KHp 9 | z0MR28Lc4VlCs6dsJBmGMBayHHVzbyXgw6uZABEBAAG0IU9zdHJlZSBUZXN0ZXIg 10 | SUkgPHRlc3QyQHRlc3QuY29tPokBOAQTAQIAIgUCVQgzgAIbAwYLCQgHAwIGFQgC 11 | CQoLBBYCAwECHgECF4AACgkQ2CKM/sqVDUFpJAgAirtYbbkvnlKtBxDsCu+A6qyl 12 | 7r+cW8IH5U1P4MqxqQwkAe1ZalfjuTSHVKYqt/K6gt0+4NvCee3A2JxXTvLq1hdR 13 | DNMUFAjkbZv3Y6VS8Qtj3edsviNEB7s8uyWgR/EBB312YSZCwzk5uSLzM5E7AmvM 14 | 0/ZPIAAxjz8TpQKc8vJx1/4nqgt0Wjv5B74vuOQJT26zoFygCQM76YeN+ULzk/hN 15 | hW7aNp/S2STasvEv7NgwqAe6eWy45xTrvxhEhQV760/toLbI0DuuBGr6Ue/G+Id4 16 | P/R604HAbMg3GdCztyoD3WTuvcsY6oXD7GlSEX4DZ0LA2TqQDZB+Pqe2yF7Gi7kB 17 | DQRVCDOAAQgAsxH2E9JeQPbcdXGyxLCa4FyMeziCrxn7tOEsRkeqZmb76mAOn67Q 18 | ZuZ6SXcAQDjKOBu7QNEcFQ+bAW/urohzD/sjr09vKqibLh6v8t81DE79GHI1UZ7F 19 | SuYDLgcGUvOCJej8iftJcudWuzCW9SvoykNcgPcIOYEXbbJVRr2xvK4z2a34DyWL 20 | jEXU2r6g1KNwtGyT6hZ7Ax99MKAzCFX3to0V51EXcrnUojwz/8i4Lal9t3d4P3lu 21 | FZw3ITWh4e9zNxp8aSxsAN/vQ1EHccMPrNmO+d+yjhH7inTxf+vLErP3Cs5rPjxl 22 | FgoLvBCSCT0jQ0xP+8Aa2TDyXDcMHg85QwARAQABiQEfBBgBAgAJBQJVCDOAAhsM 23 | AAoJENgijP7KlQ1Bc68H/RBn5PpUe8CA1CJ8eN4LIfRee3DjacwvjGsdgiMzcLhb 24 | Hp/ke42kentYjT+gF1ABPbeUERDlhnZ8BguKGZV+jOGDWRI2KFrQXL444aNznjn8 25 | aTOQY/d4LibwSaQ3qzf4Zp4CyZq2X2Vg3+B3HoUM6pkIL/r2ao5TnFqKubCE3sEo 26 | St+LV4eHktoAS1GXmxYKo4Q67yMVekTZt7C/VQ2a20qfAXBn2U8UA3tUvNqKtyyJ 27 | XrxeTJ+T4MMv60zdC/B/UPNjjHLNyB6culIzyiYFglGw3ctx2erJN2d+aQLrw78E 28 | vIuMy+JcH6y3JlzVGdByWbC7628OcWWa+NL/CXnYMyQ= 29 | =j9RC 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /tests/gpghome/key3.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1 3 | 4 | mQENBFUIM6EBCACr57QUYEEuxvkX20yM1LVt2jyYZRKKQsqXx/xCF+Pg1MNz6mYx 5 | Qz6R6+yZZmlADsfRdnEpRvl4Dq2g3cP0DqkjnIKwI7ffEsyXlves8OMlpwT2Vh4x 6 | 8Lx92eIEeqmb+PT8m88+x+EPVaR2R5KBHFkGXGyVgw+Ry8Oa9ZtJEKSkL/EQvzWv 7 | 5q+OR1Pm8rnIPe64XPh3yAx5SBJ2m7hykH/XMVrdGqaZvpuGBx77pmmqfMMjNWMC 8 | U09hURyuyGWUsj9lFWYgpBvAzASmJNpAf7FZTjzCwLJwqpxCYm6a3sp76yyjuY7q 9 | vgJOolRHp9F/XETsSLdy6966oBxclGNaD6gnABEBAAG0Ik9zdHJlZSBUZXN0ZXIg 10 | SUlJIDx0ZXN0M0B0ZXN0LmNvbT6JATgEEwECACIFAlUIM6ECGwMGCwkIBwMCBhUI 11 | AgkKCwQWAgMBAh4BAheAAAoJEA0V+uffRE1n0n4H/0Z8bC1cdr5cMFZ6YBaJlw4c 12 | b61krhan2qCrwQupwaXi6LHt0zMwgljOcN+X2sAlZj9Jv0CabU5S1vM1fh9DZ6OY 13 | 2OQ/Pq2lXGk22JjrbPPq5o//xTzo92Uxptuxq6O4frVzuGCo1yPlrHJh+TxbXIc8 14 | XOz9C9KTfcb9OwidSSW5LlUBzQ2e3oQLSUQPsdB3TZP5zlqPIYerWn+LdETKcOTr 15 | JyoaobFqX2BN223d3vkA1/GcuB17eBnzbnS0OWLJH+E3bsCqjtCJMEc1uTq97tyF 16 | XStIk9i0gVbA+GiK/ZFMt+a5kagR5dOUwpNZ0BE+Kzf0CtkSaSWkAh1vQV/j2E65 17 | AQ0EVQgzoQEIAMeXa6sp5kdmJn/fVw0Pk5oluBXif7BiFt+T7K03RxCOKRpne6dI 18 | SS98ruwZ1B5hn0lZO0UiL5RKpBQUrI9Y7251tz+oWohU2ZkUwwP3OcBlTXtErhe0 19 | LctcJ1nUA5NICVP5brhJR94durULiM+Rrhr12Ccs+a9bV268btNLN51z7ICMwNI6 20 | xuNxLt9orVJwP82a2eelQOgkPyFpiq7UxZ1erJg4aBVfWHP+rlxyQlzawVebbQMO 21 | gwYW+gAawTxd2x7PV9CC3KsaM+HI6wBvDOtcWlbzo+TxzcVocd5oern4Mr5Y86Gt 22 | lajuO9DVsuxxIfBrvHdRut613ShhOVlfy2MAEQEAAYkBHwQYAQIACQUCVQgzoQIb 23 | DAAKCRANFfrn30RNZyPVB/9jNFOjcNCAZSrz9vylaO0xHsPhIn4osmkiU6BvodwO 24 | n+qR4eEUw7BzoWC5QqGxUPYuDneQK7N7U31SFYjmY5Y1CDMsFtcYzjPgN5qWhtaN 25 | iNTtE9pb5f97PyLSUwcdW1y/cfDfqoAY6rpRXieo7hJv1xBtlEzJIbSSTS1SUEd1 26 | 4qwPCqNWMSM6qBcaFB5Yuw0Z/E6B1JfNTUw5J5jDxbGdOzkLx2mXCldte1axq9Lp 27 | 1V17jMTvn7Ml1QdoEAqzvt4VNQci/Su/qd3XjQ46b4dFFP03+jJv7mO6tHka2luZ 28 | RX0zfsk5q8wqbtV2k0XZFRD+22ddKMf1j4wID157lNQs 29 | =Dq+d 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 Colin Walters 2 | # 3 | # This library is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation; either 6 | # version 2 of the License, or (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public 14 | # License along with this library; if not, write to the 15 | # Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 | # Boston, MA 02111-1307, USA. 17 | 18 | include Makefile-decls.am 19 | 20 | privdatadir=$(pkglibdir) 21 | 22 | ACLOCAL_AMFLAGS += -I m4 ${ACLOCAL_FLAGS} 23 | AM_CPPFLAGS += -DDATADIR='"$(datadir)"' \ 24 | -DLIBEXECDIR='"$(libexecdir)"' \ 25 | -DLOCALEDIR=\"$(datadir)/locale\" 26 | AM_CFLAGS += $(WARN_CFLAGS) 27 | 28 | EXTRA_DIST += meson.build 29 | EXTRA_DIST += meson_options.txt 30 | EXTRA_DIST += packaging/meson-compat-install-symlink.sh 31 | 32 | GITIGNOREFILES += build-aux/ m4/ gtk-doc.make config.h.in aclocal.m4 33 | GITIGNOREFILES += git-evtag-*.tar.* 34 | 35 | include src/Makefile-git-evtag.am 36 | include man/Makefile-man.am 37 | include rust/Makefile-inc.am 38 | include tests/Makefile-tests.am 39 | 40 | -include $(top_srcdir)/git.mk 41 | 42 | $(srcdir)/.gitignore: src/Makefile-git-evtag.am 43 | $(srcdir)/.gitignore: man/Makefile-man.am 44 | $(srcdir)/.gitignore: rust/Makefile-inc.am 45 | $(srcdir)/.gitignore: tests/Makefile-tests.am 46 | 47 | distcheck-hook: distcheck-hook-meson 48 | distcheck-hook-meson: 49 | set -e; if command -v meson > /dev/null; then \ 50 | cd $(distdir); \ 51 | meson setup _build/meson; \ 52 | meson compile -C _build/meson -v; \ 53 | meson test -C _build/meson -v; \ 54 | rm -fr _build/meson; \ 55 | fi 56 | -------------------------------------------------------------------------------- /packaging/builddeps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2021 Simon McVittie 3 | # SPDX-License-Identifier: LGPL-2.0-or-later 4 | 5 | set -eux 6 | set -o pipefail 7 | 8 | usage() { 9 | if [ "${1-2}" -ne 0 ]; then 10 | exec >&2 11 | fi 12 | cat <&2 46 | usage 2 47 | ;; 48 | esac 49 | done 50 | 51 | # No more arguments please 52 | for arg in "$@"; do 53 | usage 2 54 | done 55 | 56 | if dpkg-vendor --derives-from Debian; then 57 | apt-get -y update 58 | apt-get -q -y install \ 59 | autoconf \ 60 | automake \ 61 | build-essential \ 62 | docbook-xml \ 63 | docbook-xsl \ 64 | git \ 65 | gnome-desktop-testing \ 66 | gnupg \ 67 | libgit2-glib-1.0-dev \ 68 | libtool \ 69 | meson \ 70 | pkg-config \ 71 | python3 \ 72 | xsltproc \ 73 | ${NULL+} 74 | 75 | if [ -n "${opt_clang}" ]; then 76 | apt-get -y install clang 77 | fi 78 | 79 | exit 0 80 | fi 81 | 82 | if command -v yum; then 83 | yum -y install \ 84 | autoconf \ 85 | automake \ 86 | docbook-style-xsl \ 87 | gcc \ 88 | git \ 89 | gnome-desktop-testing \ 90 | gnupg2 \ 91 | libasan \ 92 | libtool \ 93 | libtsan \ 94 | libubsan \ 95 | libxslt \ 96 | make \ 97 | meson \ 98 | pkgconfig(libgit2-glib-1.0) \ 99 | redhat-rpm-config \ 100 | rsync \ 101 | ${NULL+} 102 | 103 | if [ -n "${opt_clang}" ]; then 104 | yum -y install clang 105 | fi 106 | 107 | exit 0 108 | fi 109 | 110 | echo "Unknown distribution" >&2 111 | exit 1 112 | 113 | # vim:set sw=4 sts=4 et: 114 | -------------------------------------------------------------------------------- /man/git-evtag.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | git-evtag 26 | git-evtag 27 | 28 | 29 | 30 | Developer 31 | Colin 32 | Walters 33 | walters@verbum.org 34 | 35 | 36 | 37 | 38 | 39 | git-evtag 40 | 1 41 | 42 | 43 | 44 | git-evtag 45 | Sign a git commit with extended checksums 46 | 47 | 48 | 49 | 50 | git evtag sign OPTIONS TAGNAME 51 | 52 | 53 | git evtag verify OPTIONS TAGNAME 54 | 55 | 56 | 57 | 58 | Description 59 | 60 | 61 | While git can use GPG to sign tags and commits, it only 62 | covers a commit object, which is subject to SHA1 weaknesses. 63 | This tool uses SHA512 across the entire content of a tag, 64 | creating a stronger level of assurance. 65 | 66 | 67 | 68 | 69 | Options 70 | 71 | 72 | See the subcommand help, e.g. git-evtag sign --help. 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /tests/Makefile-tests.am: -------------------------------------------------------------------------------- 1 | # Makefile for tests code 2 | # 3 | # Copyright (C) 2013,2015 Colin Walters 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General 16 | # Public License along with this library; if not, see . 17 | 18 | AM_TESTS_ENVIRONMENT = \ 19 | PATH="$(abs_top_builddir):$${PATH}" \ 20 | G_TEST_SRCDIR="$(abs_srcdir)" \ 21 | G_TEST_BUILDDIR="$(abs_builddir)" \ 22 | $(NULL) 23 | LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh 24 | LOG_COMPILER = $(srcdir)/tests/tap-test 25 | EXTRA_DIST += tests/gpghome/meson.build 26 | EXTRA_DIST += tests/gpghome/trusted/meson.build 27 | EXTRA_DIST += tests/meson.build 28 | EXTRA_DIST += tests/tap-test 29 | EXTRA_DIST += tests/tap.test.in 30 | 31 | test_scripts = \ 32 | tests/test-basic.sh \ 33 | $(NULL) 34 | TESTS = $(test_scripts) 35 | 36 | EXTRA_DIST += tests/git-evtag-compute-py 37 | 38 | if BUILDOPT_INSTALL_TESTS 39 | 40 | insttestdir=$(pkglibexecdir)/installed-tests 41 | dist_insttest_SCRIPTS = $(test_scripts) 42 | 43 | testmetadir = $(datadir)/installed-tests/$(PACKAGE) 44 | testmeta_DATA = $(test_scripts:.sh=.test) 45 | 46 | dist_insttest_DATA = \ 47 | tests/libtest.sh \ 48 | $(NULL) 49 | 50 | dist_insttest_SCRIPTS += src/git-evtag-compute-py 51 | 52 | gpginsttestdir = $(pkglibexecdir)/installed-tests/gpghome 53 | dist_gpginsttest_DATA = tests/gpghome/secring.gpg \ 54 | tests/gpghome/trustdb.gpg \ 55 | tests/gpghome/key1.asc \ 56 | tests/gpghome/key2.asc \ 57 | tests/gpghome/key3.asc 58 | gpginsttest_trusteddir = $(pkglibexecdir)/installed-tests/gpghome/trusted 59 | dist_gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg 60 | 61 | install-gpg-data-hook: 62 | ln -sf trusted/pubring.gpg $(DESTDIR)$(gpginsttestdir)/pubring.gpg 63 | INSTALL_DATA_HOOKS += install-gpg-data-hook 64 | 65 | tests/%.test: tests/%.sh Makefile 66 | @$(MKDIR_P) tests 67 | $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ 68 | echo 'Exec=$(pkglibexecdir)/installed-tests/$(notdir $<)' >> $@.tmp; \ 69 | echo 'Type=session' >> $@.tmp; \ 70 | echo 'Output=TAP' >> $@.tmp; \ 71 | mv $@.tmp $@) 72 | 73 | endif 74 | 75 | CLEANFILES += $(testmeta_DATA) 76 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.63]) 2 | AC_INIT([git-evtag], [2022.1], [walters@verbum.org]) 3 | AC_CONFIG_HEADER([config.h]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AC_CONFIG_AUX_DIR([build-aux]) 6 | 7 | AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz subdir-objects]) 8 | AM_MAINTAINER_MODE([enable]) 9 | AM_SILENT_RULES([yes]) 10 | AC_USE_SYSTEM_EXTENSIONS 11 | AC_SYS_LARGEFILE 12 | 13 | # TAP test driver support 14 | AC_PROG_AWK 15 | AC_REQUIRE_AUX_FILE([tap-driver.sh]) 16 | 17 | AC_PROG_CC 18 | AM_PROG_CC_C_O 19 | 20 | changequote(,)dnl 21 | if test "x$GCC" = "xyes"; then 22 | WARN_CFLAGS="-Wall -Wstrict-prototypes -Werror=missing-prototypes \ 23 | -Werror=implicit-function-declaration \ 24 | -Werror=pointer-arith -Werror=init-self -Werror=format=2 \ 25 | -Werror=format-security \ 26 | -Werror=missing-include-dirs -Werror=aggregate-return \ 27 | -Werror=declaration-after-statement" 28 | fi 29 | changequote([,])dnl 30 | AC_SUBST(WARN_CFLAGS) 31 | 32 | # Initialize libtool 33 | LT_PREREQ([2.2.4]) 34 | LT_INIT([disable-static]) 35 | 36 | PKG_PROG_PKG_CONFIG 37 | PKG_CHECK_MODULES(BUILDDEP_LIBGIT_GLIB, [libgit2 gio-2.0]) 38 | save_LIBS=$LIBS 39 | LIBS=$BUILDDEP_LIBGIT_GLIB_LIBS 40 | AC_CHECK_FUNCS(git_libgit2_init) 41 | LIBS=$save_LIBS 42 | 43 | AC_ARG_ENABLE(man, 44 | [AS_HELP_STRING([--enable-man], 45 | [generate man pages [default=auto]])],, 46 | enable_man=maybe) 47 | 48 | AS_IF([test "$enable_man" != no], [ 49 | AC_PATH_PROG([XSLTPROC], [xsltproc]) 50 | AS_IF([test -z "$XSLTPROC"], [ 51 | AS_IF([test "$enable_man" = yes], [ 52 | AC_MSG_ERROR([xsltproc is required for --enable-man]) 53 | ]) 54 | enable_man=no 55 | ]) 56 | enable_man=yes 57 | ]) 58 | AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) 59 | 60 | AC_ARG_ENABLE(rust, 61 | [AS_HELP_STRING([--enable-rust], 62 | [Compile Rust version [default=no]])],, 63 | enable_rust=no) 64 | AS_IF([test "$enable_rust" != no], [ 65 | AC_PATH_PROG([CARGO], [cargo]) 66 | AS_IF([test -z "$CARGO"], [ 67 | AS_IF([test "$enable_rust" = yes], [ 68 | AC_MSG_ERROR([cargo is required for --enable-rust]) 69 | ]) 70 | enable_rust=no 71 | ]) 72 | enable_rust=yes 73 | ]) 74 | AM_CONDITIONAL(ENABLE_RUST, test "$enable_rust" != no) 75 | 76 | AC_ARG_ENABLE(installed_tests, 77 | AS_HELP_STRING([--enable-installed-tests], 78 | [Install test programs (default: no)]),, 79 | [enable_installed_tests=no]) 80 | AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) 81 | 82 | AC_CONFIG_FILES([ 83 | Makefile 84 | ]) 85 | AC_OUTPUT 86 | 87 | echo " 88 | $PACKAGE $VERSION 89 | 90 | man pages (xsltproc): $enable_man 91 | installed tests: $enable_installed_tests 92 | Rust implementation: $enable_rust 93 | " 94 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Simon McVittie 2 | # SPDX-License-Identifier: MIT 3 | 4 | project( 5 | 'git-evtag', 6 | 'c', 7 | version : '2022.1', 8 | meson_version : '>=0.50.0', 9 | default_options : [ 10 | 'warning_level=2', 11 | ], 12 | ) 13 | 14 | cc = meson.get_compiler('c') 15 | add_project_arguments('-D_GNU_SOURCE', language : 'c') 16 | common_include_directories = include_directories('.') 17 | 18 | add_project_arguments( 19 | cc.get_supported_arguments([ 20 | '-Werror=aggregate-return', 21 | '-Werror=declaration-after-statement', 22 | '-Werror=format-security', 23 | '-Werror=format=2', 24 | '-Werror=implicit-function-declaration', 25 | '-Werror=init-self', 26 | '-Werror=missing-include-dirs', 27 | '-Werror=missing-prototypes', 28 | '-Werror=pointer-arith', 29 | 30 | '-Wstrict-prototypes', 31 | ]), 32 | language : 'c', 33 | ) 34 | 35 | foreach no_warning : ['missing-field-initializers', 'unused-parameter'] 36 | add_project_arguments( 37 | cc.get_supported_arguments([ 38 | '-Wno-' + no_warning, 39 | '-Wno-error=' + no_warning, 40 | ]), 41 | language : 'c', 42 | ) 43 | endforeach 44 | 45 | # These can be replaced with meson.project_*_root() with a Meson 0.56 46 | # dependency 47 | project_build_root = meson.current_build_dir() 48 | project_source_root = meson.current_source_dir() 49 | 50 | glib_dep = dependency('gio-2.0', required : true) 51 | libgit_glib_dep = dependency('libgit2', required : true) 52 | 53 | cdata = configuration_data() 54 | cdata.set_quoted( 55 | 'DATADIR', 56 | get_option('prefix') / get_option('datadir'), 57 | ) 58 | cdata.set_quoted( 59 | 'LIBEXECDIR', 60 | get_option('prefix') / get_option('libexecdir'), 61 | ) 62 | cdata.set_quoted( 63 | 'LOCALEDIR', 64 | get_option('prefix') / get_option('datadir') / 'locale', 65 | ) 66 | cdata.set_quoted( 67 | 'PACKAGE_STRING', 68 | '@0@ @1@'.format(meson.project_name(), meson.project_version()), 69 | ) 70 | 71 | foreach function : ['git_libgit2_init'] 72 | if cc.has_function( 73 | function, 74 | dependencies : libgit_glib_dep, 75 | prefix : '#include ', 76 | ) 77 | cdata.set('HAVE_' + function.to_upper().underscorify(), 1) 78 | endif 79 | endforeach 80 | 81 | configure_file( 82 | output : 'config.h', 83 | configuration : cdata, 84 | ) 85 | 86 | install_symlinks = [] 87 | 88 | subdir('src') 89 | subdir('man') 90 | subdir('rust') 91 | subdir('tests') 92 | 93 | foreach symlink : install_symlinks 94 | if meson.version().version_compare('>=0.61.0') and not symlink['pointing_to'].startswith('/') 95 | install_symlink( 96 | symlink['link_name'], 97 | install_dir : symlink['install_dir'], 98 | pointing_to : symlink['pointing_to'], 99 | ) 100 | else 101 | meson.add_install_script( 102 | 'packaging/meson-compat-install-symlink.sh', 103 | symlink['link_name'], 104 | symlink['install_dir'], 105 | symlink['pointing_to'], 106 | ) 107 | endif 108 | endforeach 109 | -------------------------------------------------------------------------------- /src/git-evtag-compute-py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # An implementation of Git-EVTag in a mixture of Python and 4 | # git-as-subprocess. Slower than C, but easier to understand 5 | # and test. 6 | # 7 | # For correct submodule handling, requires the working directory have 8 | # submodule checkouts. 9 | 10 | from __future__ import print_function 11 | 12 | import os 13 | import sys 14 | import argparse 15 | import subprocess 16 | import hashlib 17 | 18 | try: 19 | from subprocess import DEVNULL # pylint: disable=no-name-in-module 20 | except ImportError: 21 | import os 22 | DEVNULL = open(os.devnull, 'wb') 23 | 24 | parser = argparse.ArgumentParser(description="Compute Git-EVTag checksum") 25 | parser.add_argument('rev', help='Revision to checksum') 26 | opts = parser.parse_args() 27 | 28 | csum = hashlib.sha512() 29 | 30 | stats = {'commit': 0, 31 | 'blob': 0, 32 | 'tree': 0, 33 | 'commitbytes': 0, 34 | 'blobbytes': 0, 35 | 'treebytes': 0} 36 | 37 | def checksum_bytes(otype, buf): 38 | blen = len(buf) 39 | csum.update(buf) 40 | stats[otype + 'bytes'] += blen 41 | return blen 42 | 43 | def checksum_object(repo, objid): 44 | assert objid is not None 45 | p = subprocess.Popen(['git', 'cat-file', '--batch'], 46 | stdin=subprocess.PIPE, 47 | stdout=subprocess.PIPE, 48 | close_fds=True, 49 | cwd=repo) 50 | p.stdin.write(objid.encode('ascii') + b'\n') 51 | p.stdin.close() 52 | (objid,objtype,lenstr) = p.stdout.readline().decode('ascii').split(None, 2) 53 | olen = int(lenstr) 54 | lenstr = lenstr.strip() 55 | buf = "{0} {1}\000".format(objtype, lenstr).encode('ascii') 56 | checksum_bytes(objtype, buf) 57 | 58 | stats[objtype] += 1 59 | 60 | if objtype == 'commit': 61 | buf = p.stdout.readline() 62 | olen -= checksum_bytes(objtype, buf) 63 | (treestr, treeobjid) = buf.decode('ascii').split(None, 1) 64 | treeobjid = treeobjid.strip() 65 | assert treestr == 'tree' 66 | else: 67 | treeobjid = None 68 | 69 | while olen > 0: 70 | b = p.stdout.read(min(8192, olen)) 71 | bytes_read = checksum_bytes(objtype, b) 72 | olen -= bytes_read 73 | if olen > 0: 74 | raise ValueError("Failed to read {0} bytes from object".format(olen)) 75 | p.wait() 76 | if p.returncode != 0: 77 | raise subprocess.CalledProcessError(p.returncode, 'git cat-file') 78 | return treeobjid 79 | 80 | def checksum_tree(repo, path, objid): 81 | checksum_object(repo, objid) 82 | p = subprocess.Popen(['git', 'ls-tree', objid], 83 | stdin=DEVNULL, 84 | stdout=subprocess.PIPE, 85 | close_fds=True, 86 | cwd=repo) 87 | for line in p.stdout: 88 | (mode, otype, subid, fname) = line.decode('ascii').split(None, 3) 89 | fname = fname.strip() 90 | if otype == 'blob': 91 | checksum_object(repo, subid) 92 | elif otype == 'tree': 93 | checksum_tree(repo, os.path.join(path, fname), subid) 94 | elif otype == 'commit': 95 | checksum_repo(os.path.join(repo, path, fname), subid) 96 | else: 97 | assert False 98 | p.wait() 99 | if p.returncode != 0: 100 | raise subprocess.CalledProcessError(p.returncode, 'git ls-tree') 101 | 102 | def checksum_repo(repo, objid): 103 | treeid = checksum_object(repo, objid) 104 | checksum_tree(repo, '.', treeid) 105 | 106 | checksum_repo('.', opts.rev) 107 | 108 | print("# git-evtag comment: submodules={0} commits={1} ({2}) trees={3} ({4}) blobs={5} ({6})".format(stats['commit']-1, stats['commit'], stats['commitbytes'], stats['tree'], stats['treebytes'], stats['blob'], stats['blobbytes'])) 109 | print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest())) 110 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | check: 13 | name: Build with Autotools and gcc, and test 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out 17 | uses: actions/checkout@v1 18 | - name: Install build-dependencies 19 | run: sudo ./packaging/builddeps.sh 20 | - name: Create logs dir 21 | run: mkdir test-logs 22 | - name: autogen.sh 23 | run: NOCONFIGURE=1 ./autogen.sh 24 | - name: configure 25 | run: | 26 | mkdir _build 27 | pushd _build 28 | ../configure \ 29 | --enable-installed-tests \ 30 | --enable-man \ 31 | ${NULL+} 32 | popd 33 | env: 34 | CFLAGS: >- 35 | -O2 36 | -Wp,-D_FORTIFY_SOURCE=2 37 | -fsanitize=address 38 | -fsanitize=undefined 39 | - name: make 40 | run: | 41 | make -C _build -j $(getconf _NPROCESSORS_ONLN) V=1 42 | - name: check 43 | run: | 44 | make -C _build -j $(getconf _NPROCESSORS_ONLN) check VERBOSE=1 45 | env: 46 | ASAN_OPTIONS: detect_leaks=0 47 | - name: install 48 | run: | 49 | make -C _build install DESTDIR="$(pwd)/DESTDIR" 50 | ( cd DESTDIR && find -ls ) 51 | sudo make -C _build install 52 | - name: installed-tests 53 | run: | 54 | ginsttest-runner -L test-logs --tap git-evtag 55 | env: 56 | ASAN_OPTIONS: detect_leaks=0 57 | - name: distcheck 58 | run: | 59 | make -C _build -j $(getconf _NPROCESSORS_ONLN) distcheck VERBOSE=1 60 | env: 61 | ASAN_OPTIONS: detect_leaks=0 62 | - name: Collect test logs on failure 63 | if: failure() || cancelled() 64 | run: | 65 | mv _build/test-suite/*.log test-logs/ || true 66 | mv _build/tests/*.log test-logs/ || true 67 | - name: Upload test logs 68 | uses: actions/upload-artifact@v1 69 | if: failure() || cancelled() 70 | with: 71 | name: test logs 72 | path: test-logs 73 | 74 | meson: 75 | name: Build with Meson and gcc, and test 76 | runs-on: ubuntu-latest 77 | steps: 78 | - name: Check out 79 | uses: actions/checkout@v1 80 | - name: Install build-dependencies 81 | run: sudo ./packaging/builddeps.sh 82 | - name: Create logs dir 83 | run: mkdir test-logs 84 | - name: configure 85 | run: | 86 | meson setup -Dinstall_tests=true -Dman=enabled _build 87 | - name: compile 88 | run: | 89 | meson compile -C _build -v 90 | - name: test 91 | run: | 92 | meson test -C _build -v 93 | - name: install 94 | run: | 95 | DESTDIR="$(pwd)/DESTDIR" meson install -C _build 96 | ( cd DESTDIR && find -ls ) 97 | sudo meson install -C _build 98 | - name: installed-tests 99 | run: | 100 | ginsttest-runner -L test-logs --tap git-evtag 101 | - name: Collect test logs on failure 102 | if: failure() || cancelled() 103 | run: | 104 | mv _build/meson-logs/* test-logs/ || true 105 | - name: Upload test logs 106 | uses: actions/upload-artifact@v1 107 | if: failure() || cancelled() 108 | with: 109 | name: Meson logs 110 | path: test-logs 111 | 112 | clang: 113 | name: Build with clang 114 | runs-on: ubuntu-latest 115 | steps: 116 | - name: Check out 117 | uses: actions/checkout@v2 118 | - name: Install build-dependencies 119 | run: sudo ./packaging/builddeps.sh --clang 120 | - name: autogen.sh 121 | run: NOCONFIGURE=1 ./autogen.sh 122 | # Do this one as an in-tree build to prove we can 123 | - name: configure 124 | run: | 125 | ./configure \ 126 | --enable-installed-tests \ 127 | --enable-man \ 128 | ${NULL+} 129 | env: 130 | CC: clang 131 | CFLAGS: >- 132 | -O2 133 | -Werror=unused-variable 134 | - name: make 135 | run: make -j $(getconf _NPROCESSORS_ONLN) V=1 136 | -------------------------------------------------------------------------------- /rust/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Colin Walters 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use structopt::StructOpt; 10 | 11 | use anyhow::{anyhow, bail, Result}; 12 | use std::io::Write; 13 | use std::process::Command; 14 | 15 | use git2::{Commit, Object, ObjectType, Oid, Repository, Submodule, Tree}; 16 | use openssl::hash::{DigestBytes, Hasher}; 17 | 18 | const EVTAG_SHA512: &str = "Git-EVTag-v0-SHA512:"; 19 | #[derive(Debug, StructOpt)] 20 | #[structopt(rename_all = "kebab-case")] 21 | struct VerifyOpts { 22 | /// Git tag name 23 | tagname: String, 24 | 25 | /// Don't verify the GPG signature 26 | no_signature: bool, 27 | } 28 | 29 | #[derive(Debug, StructOpt)] 30 | #[structopt(name = "git-evtag", about = "Extended Git tag verification")] 31 | #[structopt(rename_all = "kebab-case")] 32 | /// Main options struct 33 | enum Opt { 34 | /// Verify a signature 35 | Verify(VerifyOpts), 36 | } 37 | 38 | mod algorithm { 39 | use super::*; 40 | 41 | fn checksum_object(repo: &Repository, object: &Object, hash: &mut Hasher) -> Result<()> { 42 | let odb = repo.odb()?; 43 | let object = odb.read(object.id())?; 44 | let contentbuf = object.data(); 45 | let header = format!("{} {}", object.kind().str(), contentbuf.len()); 46 | hash.write_all(header.as_bytes())?; 47 | let nulbyte: [u8; 1] = [0; 1]; 48 | hash.write_all(&nulbyte)?; 49 | hash.write_all(contentbuf)?; 50 | Ok(()) 51 | } 52 | 53 | fn checksum_submodule(submodule: &Submodule, hash: &mut Hasher) -> Result<()> { 54 | let subrepo = submodule.open()?; 55 | let sub_head = submodule.workdir_id().expect("Failed to find workdir id"); 56 | let commit = subrepo.find_commit(sub_head)?; 57 | checksum_commit_contents(&subrepo, &commit, hash)?; 58 | Ok(()) 59 | } 60 | 61 | fn checksum_tree(repo: &Repository, tree: &Tree, hash: &mut Hasher) -> Result<()> { 62 | checksum_object(repo, tree.as_object(), hash)?; 63 | for entry in tree.iter() { 64 | match entry 65 | .kind() 66 | .expect("I have no idea what a None entry would be...") 67 | { 68 | ObjectType::Blob => { 69 | let object = repo.find_object(entry.id(), entry.kind())?; 70 | checksum_object(repo, &object, hash)? 71 | } 72 | ObjectType::Tree => { 73 | let tree = repo.find_tree(entry.id())?; 74 | checksum_tree(repo, &tree, hash)?; 75 | } 76 | ObjectType::Commit => { 77 | let submodule = repo.find_submodule(entry.name().expect("entry name"))?; 78 | checksum_submodule(&submodule, hash)?; 79 | } 80 | ObjectType::Tag => {} 81 | ObjectType::Any => panic!("Found an Any type?"), 82 | } 83 | } 84 | Ok(()) 85 | } 86 | 87 | fn checksum_commit_contents( 88 | repo: &Repository, 89 | commit: &Commit, 90 | hash: &mut Hasher, 91 | ) -> Result<()> { 92 | checksum_object(repo, commit.as_object(), hash)?; 93 | let tree = commit.tree()?; 94 | checksum_tree(repo, &tree, hash)?; 95 | Ok(()) 96 | } 97 | 98 | pub(crate) fn compute_evtag(repo: &Repository, specified_oid: Oid) -> Result { 99 | let mut hash = Hasher::new(openssl::hash::MessageDigest::sha512())?; 100 | let commit = repo.find_commit(specified_oid)?; 101 | algorithm::checksum_commit_contents(repo, &commit, &mut hash)?; 102 | Ok(hash.finish()?) 103 | } 104 | } 105 | 106 | fn verify(args: &VerifyOpts) -> Result<()> { 107 | let repo = Repository::discover(".")?; 108 | let long_tagname = format!("refs/tags/{}", args.tagname); 109 | 110 | let oid = repo.refname_to_id(&long_tagname)?; 111 | let tag = repo.find_tag(oid)?; 112 | let obj = tag.target()?; 113 | let specified_oid = obj.id(); 114 | let tag_oid_hexstr = format!("{}", tag.id()); 115 | 116 | if !args.no_signature { 117 | let status = Command::new("git") 118 | .arg("verify-tag") 119 | .arg(tag_oid_hexstr) 120 | .status()?; 121 | if !status.success() { 122 | bail!("verify-tag exited with error {:?}", status); 123 | } 124 | } 125 | 126 | let message = tag 127 | .message() 128 | .ok_or_else(|| anyhow!("No tag message found"))?; 129 | let found_checksum = message 130 | .lines() 131 | .find_map(|l| l.strip_prefix(EVTAG_SHA512).map(|s| s.trim().to_string())) 132 | .ok_or_else(|| anyhow!("No {} found in tag message", EVTAG_SHA512))?; 133 | 134 | let expected_checksum = hex::encode(algorithm::compute_evtag(&repo, specified_oid)?); 135 | 136 | if expected_checksum != found_checksum { 137 | anyhow::bail!( 138 | "Expected checksum {} but found {}", 139 | expected_checksum, 140 | found_checksum 141 | ); 142 | } 143 | println!( 144 | "Successfully verified {}: {}", 145 | EVTAG_SHA512, expected_checksum 146 | ); 147 | 148 | Ok(()) 149 | } 150 | 151 | fn main() -> Result<()> { 152 | match Opt::from_args() { 153 | Opt::Verify(ref opts) => verify(opts), 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /tests/test-basic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2011,2015 Colin Walters 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General 16 | # Public License along with this library; if not, see . 17 | 18 | set -e 19 | set -x 20 | set -o pipefail 21 | 22 | echo "1..7" 23 | 24 | . $(dirname $0)/libtest.sh 25 | 26 | setup_test_repository 27 | cd ${test_tmpdir} 28 | create_editor_script 'Release 2015.1' 29 | echo "ok setup" 30 | 31 | git clone repos/coolproject >&2 32 | cd coolproject 33 | trusted_git_submodule update --init >&2 34 | with_editor_script git evtag sign -u 472CDAFA v2015.1 >&2 35 | git show refs/tags/v2015.1 > tag.txt 36 | sed -e 's/^/#tag.txt /' < tag.txt 37 | ${SRCDIR}/git-evtag-compute-py HEAD > tag-py.txt 38 | sed -e 's/^/#tag-py.txt /' < tag-py.txt 39 | TAG='Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' 40 | assert_file_has_content tag.txt "${TAG}" 41 | with_editor_script git evtag verify v2015.1 | tee verify.out >&2 42 | assert_file_has_content verify.out "Successfully verified: ${TAG}" 43 | # Also test subdirectory 44 | (cd src && with_editor_script git evtag verify v2015.1 | tee ../verify2.out) >&2 45 | assert_file_has_content verify2.out "Successfully verified: ${TAG}" 46 | assert_file_has_content tag-py.txt "${TAG}" 47 | 48 | rm -f tag.txt 49 | rm -f verify.out 50 | echo "ok tag + verify" 51 | 52 | cd ${test_tmpdir} 53 | rm coolproject -rf 54 | git clone repos/coolproject >&2 55 | cd coolproject 56 | trusted_git_submodule update --init >&2 57 | echo 'super cool' > src/cool.c 58 | if with_editor_script git evtag sign -u 472CDAFA v2015.1 >&2 2>err.txt; then 59 | assert_not_reached "expected failure due to dirty tree" 60 | fi 61 | assert_file_has_content err.txt 'Attempting to tag or verify dirty tree' 62 | git checkout src/cool.c >&2 63 | # But untracked files are ok 64 | touch unknownfile 65 | with_editor_script git evtag sign -u 472CDAFA v2015.1 >&2 66 | git evtag verify v2015.1 >&2 67 | echo 'ok no tag on dirty tree' 68 | 69 | 70 | cd ${test_tmpdir} 71 | rm coolproject -rf 72 | git clone repos/coolproject >&2 73 | cd coolproject 74 | trusted_git_submodule update --init >&2 75 | with_editor_script git evtag sign -u 472CDAFA v2015.1 >&2 76 | git checkout -q HEAD^ >&2 77 | if git evtag verify v2015.1 >&2 2>err.txt; then 78 | assert_not_reached 'Expected failure due to non HEAD' 79 | fi 80 | assert_file_has_content err.txt "Target.*is not HEAD" 81 | echo 'ok no tag verify on non-HEAD' 82 | 83 | cd ${test_tmpdir} 84 | rm coolproject -rf 85 | git clone repos/coolproject >&2 86 | cd coolproject 87 | trusted_git_submodule update --init >&2 88 | with_editor_script git evtag sign --with-legacy-archive-tag -u 472CDAFA v2015.1 >&2 89 | git show refs/tags/v2015.1 > tag.txt 90 | assert_file_has_content tag.txt 'ExtendedVerify-SHA256-archive-tar: 83991ee23a027d97ad1e06432ad87c6685e02eac38706e7fbfe6e5e781939dab' 91 | gitversion=$(git --version) 92 | assert_file_has_content tag.txt "ExtendedVerify-git-version: ${gitversion}" 93 | with_editor_script git evtag verify v2015.1 | tee verify.out >&2 94 | assert_file_has_content verify.out 'Successfully verified: Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' 95 | rm -f tag.txt 96 | rm -f verify.out 97 | echo "ok tag + verify legacy" 98 | 99 | cd ${test_tmpdir} 100 | rm coolproject -rf 101 | git clone repos/coolproject >&2 102 | cd coolproject 103 | trusted_git_submodule update --init >&2 104 | with_editor_script git evtag sign --no-signature v2015.1 >&2 105 | git show refs/tags/v2015.1 > tag.txt 106 | assert_file_has_content tag.txt 'Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' 107 | assert_not_file_has_content tag.txt '-----BEGIN PGP SIGNATURE-----' 108 | if git evtag verify v2015.1 2>err.txt; then 109 | assert_not_reached 'Expected failure due to no GPG signature' 110 | fi 111 | assert_file_has_content err.txt "no signature found" 112 | git evtag verify --no-signature v2015.1 >&2 113 | rm -f tag.txt 114 | rm -f verify.out 115 | echo "ok checksum-only tag + verify" 116 | 117 | cd ${test_tmpdir} 118 | rm coolproject -rf 119 | git clone repos/coolproject2 >&2 120 | cd coolproject2 121 | trusted_git_submodule update --init >&2 122 | with_editor_script git evtag sign -u 472CDAFA v2015.1 >&2 123 | git show refs/tags/v2015.1 > tag.txt 124 | TAG='Git-EVTag-v0-SHA512: 8ef922041663821b8208d6e1037adbd51e0b19cc4dd3314436b3078bdae4073a616e6e289891fa5ad9f798630962a33350f6035fffec6ca3c499bc01f07c3d0a' 125 | assert_file_has_content tag.txt "${TAG}" 126 | with_editor_script git evtag verify v2015.1 | tee verify.out >&2 127 | assert_file_has_content verify.out "Successfully verified: ${TAG}" 128 | # Also test subdirectory 129 | (cd src && with_editor_script git evtag verify v2015.1 | tee ../verify2.out) >&2 130 | assert_file_has_content verify2.out "Successfully verified: ${TAG}" 131 | ${SRCDIR}/git-evtag-compute-py HEAD > tag-py.txt 132 | assert_file_has_content tag-py.txt "${TAG}" 133 | 134 | rm -f tag.txt 135 | rm -f verify.out 136 | echo "ok tag + verify with nested submodules" 137 | -------------------------------------------------------------------------------- /tests/libtest.sh: -------------------------------------------------------------------------------- 1 | # Source library for shell script tests 2 | # 3 | # Copyright (C) 2011,2015 Colin Walters 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General 16 | # Public License along with this library; if not, see . 17 | 18 | SRCDIR=$(dirname $0) 19 | test_tmpdir=$(pwd) 20 | 21 | export G_DEBUG=fatal-warnings 22 | 23 | export TEST_GPG_KEYID_1="472CDAFA" 24 | export TEST_GPG_KEYID_2="CA950D41" 25 | export TEST_GPG_KEYID_3="DF444D67" 26 | 27 | export GIT_AUTHOR_NAME='Colin Walters' 28 | export GIT_AUTHOR_EMAIL='walters@verbum.org' 29 | export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" 30 | export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL" 31 | 32 | # GPG when creating signatures demands a writable 33 | # homedir in order to create lockfiles. Work around 34 | # this by copying locally. 35 | cp -a ${SRCDIR}/gpghome ${test_tmpdir} 36 | chmod -R u+w ${test_tmpdir}/gpghome 37 | chmod 0700 ${test_tmpdir}/gpghome 38 | export GNUPGHOME=${test_tmpdir}/gpghome 39 | 40 | if test -n "${OT_TESTS_DEBUG}"; then 41 | set -x 42 | fi 43 | 44 | assert_not_reached () { 45 | echo $@ 1>&2; exit 1 46 | } 47 | 48 | assert_streq () { 49 | test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1) 50 | } 51 | 52 | assert_not_streq () { 53 | (! test "$1" = "$2") || (echo 1>&2 "$1 == $2"; exit 1) 54 | } 55 | 56 | assert_has_file () { 57 | test -f "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1) 58 | } 59 | 60 | assert_has_dir () { 61 | test -d "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1) 62 | } 63 | 64 | assert_not_has_file () { 65 | if test -f "$1"; then 66 | echo 1>&2 "File '$1' exists"; exit 1 67 | fi 68 | } 69 | 70 | assert_not_file_has_content () { 71 | if grep -q -e "$2" "$1"; then 72 | echo 1>&2 "File '$1' incorrectly matches regexp '$2'"; exit 1 73 | fi 74 | } 75 | 76 | assert_not_has_dir () { 77 | if test -d "$1"; then 78 | echo 1>&2 "Directory '$1' exists"; exit 1 79 | fi 80 | } 81 | 82 | assert_file_has_content () { 83 | if ! grep -q -e "$2" "$1"; then 84 | echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1 85 | fi 86 | } 87 | 88 | assert_file_empty() { 89 | if test -s "$1"; then 90 | echo 1>&2 "File '$1' is not empty"; exit 1 91 | fi 92 | } 93 | 94 | gitcommit_reset_time() { 95 | TSCOUNTER=1436222301 96 | } 97 | gitcommit_inctime() { 98 | TSCOUNTER=$(($TSCOUNTER + 1)) 99 | TSV="$TSCOUNTER +0000" 100 | env GIT_AUTHOR_DATE="$TSV" GIT_COMMITTER_DATE="$TSV" git commit "$@" 101 | } 102 | 103 | trusted_git_submodule () { 104 | # Git forbids file:/// for submodules by default, to avoid untrusted 105 | # file:/// repositories being able to break a security boundary 106 | # (CVE-2022-39253). 107 | # In this test suite, all the repositories are under our control and 108 | # we trust them, so bypass that. 109 | git -c protocol.file.allow=always submodule "$@" 110 | } 111 | 112 | setup_test_repository () { 113 | oldpwd=`pwd` 114 | 115 | cd ${test_tmpdir} 116 | mkdir coolproject 117 | cd coolproject 118 | git init -b mybranch >&2 119 | gitcommit_reset_time >&2 120 | echo 'So cool!' > README.md 121 | git add . >&2 122 | gitcommit_inctime -a -m 'Initial commit' >&2 123 | mkdir src 124 | echo 'printf("hello world")' > src/cool.c 125 | git add . >&2 126 | gitcommit_inctime -a -m 'Add C source' >&2 127 | 128 | cd ${test_tmpdir} 129 | mkdir -p repos/coolproject 130 | cd repos/coolproject && git init --bare -b mybranch >&2 131 | cd ${test_tmpdir}/coolproject 132 | git remote add origin file://${test_tmpdir}/repos/coolproject >&2 133 | git push --set-upstream origin mybranch >&2 134 | 135 | cd ${test_tmpdir} 136 | mkdir subproject 137 | cd subproject 138 | git init -b mybranch >&2 139 | echo 'this is libsub.c' > libsub.c 140 | echo 'An example submodule' > README.md 141 | git add . >&2 142 | gitcommit_inctime -a -m 'init' >&2 143 | mkdir src 144 | mv libsub.c src 145 | echo 'an update to libsub.c, now in src/' > src/libsub.c 146 | gitcommit_inctime -a -m 'an update' >&2 147 | cd ${test_tmpdir} 148 | mkdir -p repos/subproject 149 | cd repos/subproject && git init --bare -b mybranch >&2 150 | cd ${test_tmpdir}/subproject 151 | git remote add origin file://${test_tmpdir}/repos/subproject >&2 152 | git push --set-upstream origin mybranch >&2 153 | 154 | cd ${test_tmpdir}/coolproject 155 | trusted_git_submodule add ../subproject subproject >&2 156 | git add subproject >&2 157 | echo '#include subproject/src/libsub.c' >> src/cool.c 158 | gitcommit_inctime -a -m 'Add libsub' >&2 159 | git push origin mybranch >&2 160 | 161 | # Copy coolproject to create another version which has two submodules, 162 | # one which is nested deeper in the repository. 163 | cd ${test_tmpdir} 164 | cp -r repos/coolproject repos/coolproject2 165 | git clone file://${test_tmpdir}/repos/coolproject2 >&2 166 | cd coolproject2 167 | mkdir subprojects 168 | trusted_git_submodule add ../subproject subprojects/subproject >&2 169 | git add subprojects/subproject >&2 170 | gitcommit_inctime -a -m 'Add subprojects/subproject' >&2 171 | git push origin mybranch >&2 172 | 173 | cd ${test_tmpdir} 174 | rm coolproject -rf 175 | rm coolproject2 -rf 176 | rm subproject -rf 177 | 178 | cd $oldpwd 179 | } 180 | 181 | create_editor_script() { 182 | cat >${test_tmpdir}/editor.sh < \$1 186 | EOF 187 | chmod a+x editor.sh 188 | } 189 | 190 | with_editor_script() { 191 | env EDITOR=${test_tmpdir}/editor.sh "$@" 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-evtag 2 | 3 | `git-evtag` can be used as a replacement for `git-tag -s`. It 4 | will generate a strong checksum (called `Git-EVTag-v0-SHA512`) over the 5 | commit, tree, and blobs it references (and recursively over submodules). 6 | A primary rationale for this is that the underlying SHA1 algorithm of 7 | git is [under increasing threat](https://marc.info/?l=git&m=148786876821543&w=2). 8 | Further, a goal here is to create a checksum that covers the entire source of 9 | a single revision as a replacement for tarballs + checksums. 10 | 11 | git-evtag was originally discussed (long before the SHA1 collision of February 2017) 12 | on the git mailing list: 13 | 14 | - [marc.info](https://marc.info/?l=git&m=142513489318999&w=3) 15 | - [nabble.com](http://git.661346.n2.nabble.com/weaning-distributions-off-tarballs-extended-verification-of-git-tags-td7626117.html) 16 | 17 | ### Getting git-evtag 18 | 19 | See also the [the Node.js implementation](https://github.com/indutny/git-secure-tag). 20 | 21 | - [Fedora package](https://src.fedoraproject.org/rpms/git-evtag) 22 | - Building from source: Requires glib2 and libgit2. 23 | 24 | ### Using git-evtag 25 | 26 | Create a new `v2015.10` tag, covering the `HEAD` revision with GPG 27 | signature and `Git-EVTag-v0-SHA512`: 28 | 29 | ``` 30 | $ git-evtag sign v2015.10 31 | ( type your tag message, note a Git-EVTag-v0-SHA512 line in the message ) 32 | $ git show v2015.10 33 | ( Note signature covered by GPG signature ) 34 | ``` 35 | 36 | Verify a tag: 37 | 38 | ``` 39 | $ git-evtag verify v2015.10 40 | gpg: Signature made Sun 28 Jun 2015 10:49:11 AM EDT 41 | gpg: using RSA key 0xDC45FD5921C13F0B 42 | gpg: Good signature from "Colin Walters " [ultimate] 43 | gpg: aka "Colin Walters " [ultimate] 44 | Primary key fingerprint: 1CEC 7A9D F7DA 85AB EF84 3DC0 A866 D7CC AE08 7291 45 | Subkey fingerprint: AB92 8A9C F8DD 0629 09C3 7BBD DC45 FD59 21C1 3F0B 46 | Successfully verified: Git-EVTag-v0-SHA512: b05f10f9adb0eff352d90938588834508d33fdfcedbcfc332999ee397efa321d1f49a539f1b82f024111a281c1f441002e7f536b06eb04d41857b01636f6f268 47 | ``` 48 | 49 | ### Replacing tarballs - i.e. be the primary artifact 50 | 51 | This is similar to what project distributors often accomplish by using 52 | `git archive`, or `make dist`, or similar tools to generate a tarball, 53 | and then checksumming that, and (ideally) providing a GPG signature 54 | covering it. 55 | 56 | ### Tarball reproducibility 57 | 58 | The problem with `git archive` and `make dist` is that tarballs (and 59 | other tools like zip files) are not easily reproducible *exactly* from 60 | a git repository commit. The authors of git reserve the right to 61 | change the file format output by `git archive` in the future. Also, 62 | there are a variety of reasons why compressors like `gzip` and `xz` 63 | aren't necessarily reproducible, such as compression levels, included 64 | timestamps, optimizations in the algorithm, etc. See 65 | [Pristine tar](https://salsa.debian.org/debian/pristine-tar) 66 | for some examples of the difficulties involved (e.g. trying to 67 | retroactively guess the compression level arguments from the xz 68 | dictionary size). 69 | 70 | If the checksum is not reproducible, it becomes much more difficult to 71 | easily and reliably verify that a generated tarball contains the same 72 | source code as a particular git commit. 73 | 74 | What `git-evtag` implements is an algorithm for providing a strong 75 | checksum over the complete source objects for the target commit (+ 76 | trees + blobs + submodules). Then it's integrated with GPG for 77 | end-to-end verification. (Although, one could also wrap the checksum 78 | in X.509 or some other public/private signature solution). 79 | 80 | Then no out of band distribution mechanism is necessary, and better, 81 | the checksums strengthen the ability to verify integrity of the git 82 | repository. 83 | 84 | (And if you want to avoid downloading the entire history, that's what 85 | `git clone --depth=1` is for.) 86 | 87 | ### Git and SHA1 88 | 89 | NEW! The first SHA1 collision was announced February 23, 2017: 90 | 91 | - https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html 92 | - http://shattered.io/ 93 | 94 | Git uses a modified Merkle tree with SHA1, which means that if an 95 | attacker managed to create a SHA1 collision for a source file object 96 | (git blob), it would affect *all* revisions and checkouts - 97 | invalidating the security of *all* GPG signed tags whose commits point 98 | to that object. 99 | 100 | Now, the author of this tool believes that *today*, GPG signed git 101 | tags are fairly secure, especially if one is careful to ensure 102 | transport integrity (e.g. pinned TLS certificates from the origin). 103 | 104 | ### The Git-EVTag algorithm (v0) 105 | 106 | There is currently only one version of the `Git-EVTag` algorithm, 107 | called `v0` - and it only supports 108 | [SHA-512](https://en.wikipedia.org/wiki/SHA-2). It is declared 109 | stable. All further text refers to this version of the algorithm. In 110 | the unlikely event that it is necessary to introduce a new version, 111 | this tool will support all known versions. 112 | 113 | `Git-EVTag-v0-SHA512` covers the complete contents of all objects for 114 | a commit; again similar to checksumming `git archive`, except 115 | reproducible. Each object is added to the checksum in its raw 116 | canonicalized form, including the header. 117 | 118 | For a given commit (in Rust-style pseudocode): 119 | 120 | ```rust 121 | fn git_evtag(repo: GitRepo, commitid: String) -> SHA512 { 122 | let checksum = new SHA512(); 123 | walk_commit(repo, checksum, commitid) 124 | return checksum 125 | } 126 | 127 | fn walk_commit(repo: GitRepo, checksum : SHA512, commitid : String) { 128 | checksum_object(repo, checksum, commitid) 129 | let treeid = repo.load_commit(commitid).treeid(); 130 | walk(repo, checksum, treeid) 131 | } 132 | 133 | fn checksum_object(repo: GitRepo, checksum: SHA512, objid: String) -> () { 134 | // This is the canonical header of the object; 135 | // https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#Object-Storage 136 | let header : &str = repo.load_object_header(objid); 137 | // The NUL byte after the header, explicitly included in the checksum 138 | let nul = [0u8]; 139 | // The remaining raw content of the object as a byte array 140 | let body : &[u8] = repo.load_object_body(objid); 141 | 142 | checksum.update(header.as_bytes()) 143 | checksum.update(&nul); 144 | checksum.update(body) 145 | } 146 | 147 | fn walk(repo: GitRepo, checksum: SHA512, treeid: String) -> () { 148 | // First, add the tree object itself 149 | checksum_object(repo, checksum, treeid); 150 | let tree = repo.load_tree(treeid); 151 | for child in tree.children() { 152 | match childtype { 153 | Blob(blobid) => checksum_object(repo, checksum, blobid), 154 | Tree(child_treeid) => walk(repo, checksum, child_treeid), 155 | Commit(commitid, path) => { 156 | let child_repo = repo.get_submodule(path) 157 | walk_commit(child_repo, checksum, commitid) 158 | } 159 | } 160 | } 161 | } 162 | ``` 163 | 164 | This strong checksum, can be verified reproducibly offline after 165 | cloning a git repository for a particular tag. When covered by a GPG 166 | signature, it provides a strong end-to-end integrity guarantee. 167 | 168 | It's quite inexpensive and practical to compute `Git-EVTag-v0-SHA512` 169 | once per tag/release creation. At the time of this writing, on the 170 | Linux kernel (a large project by most standards), it takes about 5 171 | seconds to compute on this author's laptop. On most smaller projects, 172 | it's completely negligible. 173 | 174 | ### Aside: other aspects of tarballs 175 | 176 | This project is just addressing one small part of the larger 177 | git/tarball question. Anything else is out of scope, but a brief 178 | discussion of other aspects is included below. 179 | 180 | Historically, many projects include additional content in tarballs. 181 | For example, the GNU Autotools pregenerate a `configure` script from 182 | `configure.ac` and the like. Other projects don't include 183 | translations in git, but merge them out of band when generating 184 | tarballs. 185 | 186 | There are many other things like this, and they all harm 187 | reproducibility and continuous integration/delivery. 188 | 189 | For example, while many of my projects use Autotools, I simply have 190 | downstream authors run `autogen.sh`. It works just fine - the 191 | autotools are no longer changing often, and many downstreams want to 192 | do it anyways. 193 | 194 | For the translation issue, note that bad translations can actually 195 | crash one's application. If they're part of the git repository, they 196 | can be more easily tested as a unit continuously. 197 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /git.mk: -------------------------------------------------------------------------------- 1 | # git.mk, a small Makefile to autogenerate .gitignore files 2 | # for autotools-based projects. 3 | # 4 | # Copyright 2009, Red Hat, Inc. 5 | # Copyright 2010,2011,2012,2013 Behdad Esfahbod 6 | # Written by Behdad Esfahbod 7 | # 8 | # Copying and distribution of this file, with or without modification, 9 | # is permitted in any medium without royalty provided the copyright 10 | # notice and this notice are preserved. 11 | # 12 | # The latest version of this file can be downloaded from: 13 | GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk 14 | # 15 | # Bugs, etc, should be reported upstream at: 16 | # https://github.com/behdad/git.mk 17 | # 18 | # To use in your project, import this file in your git repo's toplevel, 19 | # then do "make -f git.mk". This modifies all Makefile.am files in 20 | # your project to -include git.mk. Remember to add that line to new 21 | # Makefile.am files you create in your project, or just rerun the 22 | # "make -f git.mk". 23 | # 24 | # This enables automatic .gitignore generation. If you need to ignore 25 | # more files, add them to the GITIGNOREFILES variable in your Makefile.am. 26 | # But think twice before doing that. If a file has to be in .gitignore, 27 | # chances are very high that it's a generated file and should be in one 28 | # of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. 29 | # 30 | # The only case that you need to manually add a file to GITIGNOREFILES is 31 | # when remove files in one of mostlyclean-local, clean-local, distclean-local, 32 | # or maintainer-clean-local make targets. 33 | # 34 | # Note that for files like editor backup, etc, there are better places to 35 | # ignore them. See "man gitignore". 36 | # 37 | # If "make maintainer-clean" removes the files but they are not recognized 38 | # by this script (that is, if "git status" shows untracked files still), send 39 | # me the output of "git status" as well as your Makefile.am and Makefile for 40 | # the directories involved and I'll diagnose. 41 | # 42 | # For a list of toplevel files that should be in MAINTAINERCLEANFILES, see 43 | # Makefile.am.sample in the git.mk git repo. 44 | # 45 | # Don't EXTRA_DIST this file. It is supposed to only live in git clones, 46 | # not tarballs. It serves no useful purpose in tarballs and clutters the 47 | # build dir. 48 | # 49 | # This file knows how to handle autoconf, automake, libtool, gtk-doc, 50 | # gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, 51 | # appstream, hotdoc. 52 | # 53 | # This makefile provides the following targets: 54 | # 55 | # - all: "make all" will build all gitignore files. 56 | # - gitignore: makes all gitignore files in the current dir and subdirs. 57 | # - .gitignore: make gitignore file for the current dir. 58 | # - gitignore-recurse: makes all gitignore files in the subdirs. 59 | # 60 | # KNOWN ISSUES: 61 | # 62 | # - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the 63 | # submodule doesn't find us. If you have configure.{in,ac} files in 64 | # subdirs, add a proxy git.mk file in those dirs that simply does: 65 | # "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. 66 | # And add those files to git. See vte/gnome-pty-helper/git.mk for 67 | # example. 68 | # 69 | 70 | 71 | 72 | ############################################################################### 73 | # Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: 74 | ############################################################################### 75 | 76 | # 77 | # Most autotools-using modules should be fine including this variable in their 78 | # toplevel MAINTAINERCLEANFILES: 79 | GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ 80 | $(srcdir)/aclocal.m4 \ 81 | $(srcdir)/autoscan.log \ 82 | $(srcdir)/configure.scan \ 83 | `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ 84 | test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ 85 | for x in \ 86 | ar-lib \ 87 | compile \ 88 | config.guess \ 89 | config.rpath \ 90 | config.sub \ 91 | depcomp \ 92 | install-sh \ 93 | ltmain.sh \ 94 | missing \ 95 | mkinstalldirs \ 96 | test-driver \ 97 | ylwrap \ 98 | ; do echo "$$AUX_DIR/$$x"; done` \ 99 | `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ 100 | head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` 101 | # 102 | # All modules should also be fine including the following variable, which 103 | # removes automake-generated Makefile.in files: 104 | GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ 105 | `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ 106 | while read f; do \ 107 | case $$f in Makefile|*/Makefile) \ 108 | test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ 109 | done` 110 | # 111 | # Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, 112 | # though it's harmless to include regardless. 113 | GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ 114 | `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ 115 | if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ 116 | for x in \ 117 | libtool.m4 \ 118 | ltoptions.m4 \ 119 | ltsugar.m4 \ 120 | ltversion.m4 \ 121 | lt~obsolete.m4 \ 122 | ; do echo "$$MACRO_DIR/$$x"; done; \ 123 | fi` 124 | # 125 | # Modules that use gettext and use AC_CONFIG_MACRO_DIR() may also include this, 126 | # though it's harmless to include regardless. 127 | GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \ 128 | `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ 129 | if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ 130 | for x in \ 131 | codeset.m4 \ 132 | extern-inline.m4 \ 133 | fcntl-o.m4 \ 134 | gettext.m4 \ 135 | glibc2.m4 \ 136 | glibc21.m4 \ 137 | iconv.m4 \ 138 | intdiv0.m4 \ 139 | intl.m4 \ 140 | intldir.m4 \ 141 | intlmacosx.m4 \ 142 | intmax.m4 \ 143 | inttypes-pri.m4 \ 144 | inttypes_h.m4 \ 145 | lcmessage.m4 \ 146 | lib-ld.m4 \ 147 | lib-link.m4 \ 148 | lib-prefix.m4 \ 149 | lock.m4 \ 150 | longlong.m4 \ 151 | nls.m4 \ 152 | po.m4 \ 153 | printf-posix.m4 \ 154 | progtest.m4 \ 155 | size_max.m4 \ 156 | stdint_h.m4 \ 157 | threadlib.m4 \ 158 | uintmax_t.m4 \ 159 | visibility.m4 \ 160 | wchar_t.m4 \ 161 | wint_t.m4 \ 162 | xsize.m4 \ 163 | ; do echo "$$MACRO_DIR/$$x"; done; \ 164 | fi` 165 | 166 | 167 | 168 | ############################################################################### 169 | # Default rule is to install ourselves in all Makefile.am files: 170 | ############################################################################### 171 | 172 | git-all: git-mk-install 173 | 174 | git-mk-install: 175 | @echo "Installing git makefile" 176 | @any_failed=; \ 177 | find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ 178 | if grep 'include .*/git.mk' $$x >/dev/null; then \ 179 | echo "$$x already includes git.mk"; \ 180 | else \ 181 | failed=; \ 182 | echo "Updating $$x"; \ 183 | { cat $$x; \ 184 | echo ''; \ 185 | echo '-include $$(top_srcdir)/git.mk'; \ 186 | } > $$x.tmp || failed=1; \ 187 | if test x$$failed = x; then \ 188 | mv $$x.tmp $$x || failed=1; \ 189 | fi; \ 190 | if test x$$failed = x; then : else \ 191 | echo "Failed updating $$x"; >&2 \ 192 | any_failed=1; \ 193 | fi; \ 194 | fi; done; test -z "$$any_failed" 195 | 196 | git-mk-update: 197 | wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk 198 | 199 | .PHONY: git-all git-mk-install git-mk-update 200 | 201 | 202 | 203 | ############################################################################### 204 | # Actual .gitignore generation: 205 | ############################################################################### 206 | 207 | $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(top_srcdir)/configure.ac 208 | @echo "git.mk: Generating $@" 209 | @{ \ 210 | if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ 211 | for x in \ 212 | $(DOC_MODULE)-decl-list.txt \ 213 | $(DOC_MODULE)-decl.txt \ 214 | tmpl/$(DOC_MODULE)-unused.sgml \ 215 | "tmpl/*.bak" \ 216 | $(REPORT_FILES) \ 217 | $(DOC_MODULE).pdf \ 218 | xml html \ 219 | ; do echo "/$$x"; done; \ 220 | FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ 221 | case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ 222 | if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \ 223 | echo "/$(DOC_MODULE).types"; \ 224 | fi; \ 225 | if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \ 226 | echo "/$(DOC_MODULE)-sections.txt"; \ 227 | fi; \ 228 | if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ 229 | for x in \ 230 | $(SETUP_FILES) \ 231 | $(DOC_MODULE).types \ 232 | ; do echo "/$$x"; done; \ 233 | fi; \ 234 | fi; \ 235 | if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ 236 | for lc in $(DOC_LINGUAS); do \ 237 | for x in \ 238 | $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ 239 | $(DOC_PAGES) \ 240 | $(DOC_INCLUDES) \ 241 | ; do echo "/$$lc/$$x"; done; \ 242 | done; \ 243 | for x in \ 244 | $(_DOC_OMF_ALL) \ 245 | $(_DOC_DSK_ALL) \ 246 | $(_DOC_HTML_ALL) \ 247 | $(_DOC_MOFILES) \ 248 | $(DOC_H_FILE) \ 249 | "*/.xml2po.mo" \ 250 | "*/*.omf.out" \ 251 | ; do echo /$$x; done; \ 252 | fi; \ 253 | if test "x$(HOTDOC)" = x; then :; else \ 254 | $(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \ 255 | echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \ 256 | echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \ 257 | ) \ 258 | for x in \ 259 | .hotdoc.d \ 260 | ; do echo "/$$x"; done; \ 261 | fi; \ 262 | if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ 263 | for lc in $(HELP_LINGUAS); do \ 264 | for x in \ 265 | $(HELP_FILES) \ 266 | "$$lc.stamp" \ 267 | "$$lc.mo" \ 268 | ; do echo "/$$lc/$$x"; done; \ 269 | done; \ 270 | fi; \ 271 | if test "x$(gsettings_SCHEMAS)" = x; then :; else \ 272 | for x in \ 273 | $(gsettings_SCHEMAS:.xml=.valid) \ 274 | $(gsettings__enum_file) \ 275 | ; do echo "/$$x"; done; \ 276 | fi; \ 277 | if test "x$(appdata_XML)" = x; then :; else \ 278 | for x in \ 279 | $(appdata_XML:.xml=.valid) \ 280 | ; do echo "/$$x"; done; \ 281 | fi; \ 282 | if test "x$(appstream_XML)" = x; then :; else \ 283 | for x in \ 284 | $(appstream_XML:.xml=.valid) \ 285 | ; do echo "/$$x"; done; \ 286 | fi; \ 287 | if test -f $(srcdir)/po/Makefile.in.in; then \ 288 | for x in \ 289 | ABOUT-NLS \ 290 | po/Makefile.in.in \ 291 | po/Makefile.in.in~ \ 292 | po/Makefile.in \ 293 | po/Makefile \ 294 | po/Makevars.template \ 295 | po/POTFILES \ 296 | po/Rules-quot \ 297 | po/stamp-it \ 298 | po/stamp-po \ 299 | po/.intltool-merge-cache \ 300 | "po/*.gmo" \ 301 | "po/*.header" \ 302 | "po/*.mo" \ 303 | "po/*.sed" \ 304 | "po/*.sin" \ 305 | po/$(GETTEXT_PACKAGE).pot \ 306 | intltool-extract.in \ 307 | intltool-merge.in \ 308 | intltool-update.in \ 309 | ; do echo "/$$x"; done; \ 310 | fi; \ 311 | if test -f $(srcdir)/configure; then \ 312 | for x in \ 313 | autom4te.cache \ 314 | configure \ 315 | config.h \ 316 | stamp-h1 \ 317 | libtool \ 318 | config.lt \ 319 | ; do echo "/$$x"; done; \ 320 | fi; \ 321 | if test "x$(DEJATOOL)" = x; then :; else \ 322 | for x in \ 323 | $(DEJATOOL) \ 324 | ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ 325 | echo /site.exp; \ 326 | fi; \ 327 | if test "x$(am__dirstamp)" = x; then :; else \ 328 | echo "$(am__dirstamp)"; \ 329 | fi; \ 330 | if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ 331 | for x in \ 332 | "*.lo" \ 333 | ".libs" "_libs" \ 334 | ; do echo "$$x"; done; \ 335 | fi; \ 336 | for x in \ 337 | .gitignore \ 338 | $(GITIGNOREFILES) \ 339 | $(CLEANFILES) \ 340 | $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ 341 | $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ 342 | $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ 343 | so_locations \ 344 | $(MOSTLYCLEANFILES) \ 345 | $(TEST_LOGS) \ 346 | $(TEST_LOGS:.log=.trs) \ 347 | $(TEST_SUITE_LOG) \ 348 | $(TESTS:=.test) \ 349 | "*.gcda" \ 350 | "*.gcno" \ 351 | $(DISTCLEANFILES) \ 352 | $(am__CONFIG_DISTCLEAN_FILES) \ 353 | $(CONFIG_CLEAN_FILES) \ 354 | TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ 355 | "*.tab.c" \ 356 | $(MAINTAINERCLEANFILES) \ 357 | $(BUILT_SOURCES) \ 358 | $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ 359 | $(filter %_vala.stamp,$(DIST_COMMON)) \ 360 | $(filter %.vapi,$(DIST_COMMON)) \ 361 | $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ 362 | Makefile \ 363 | Makefile.in \ 364 | "*.orig" \ 365 | "*.rej" \ 366 | "*.bak" \ 367 | "*~" \ 368 | ".*.sw[nop]" \ 369 | ".dirstamp" \ 370 | ; do echo "/$$x"; done; \ 371 | for x in \ 372 | "*.$(OBJEXT)" \ 373 | $(DEPDIR) \ 374 | ; do echo "$$x"; done; \ 375 | } | \ 376 | sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ 377 | sed 's@/[.]/@/@g' | \ 378 | LC_ALL=C sort | uniq > .gitignore.tmp && \ 379 | (mv .gitignore.tmp $@ || (echo "WARNING: Cannot create $@ file; skipping"; \ 380 | $(RM) .gitignore.tmp)); 381 | 382 | all: $(srcdir)/.gitignore gitignore-recurse-maybe 383 | gitignore: $(srcdir)/.gitignore gitignore-recurse 384 | 385 | gitignore-recurse-maybe: 386 | @for subdir in $(DIST_SUBDIRS); do \ 387 | case " $(SUBDIRS) " in \ 388 | *" $$subdir "*) :;; \ 389 | *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ 390 | esac; \ 391 | done 392 | gitignore-recurse: 393 | @for subdir in $(DIST_SUBDIRS); do \ 394 | test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ 395 | done 396 | 397 | maintainer-clean: gitignore-clean 398 | gitignore-clean: 399 | -rm -f $(srcdir)/.gitignore 400 | 401 | .PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe 402 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the library GPL. It is 10 | numbered 2 because it goes with version 2 of the ordinary GPL.] 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | Licenses are intended to guarantee your freedom to share and change 17 | free software--to make sure the software is free for all its users. 18 | 19 | This license, the Library General Public License, applies to some 20 | specially designated Free Software Foundation software, and to any 21 | other libraries whose authors decide to use it. You can use it for 22 | your libraries, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if 34 | you distribute copies of the library, or if you modify it. 35 | 36 | For example, if you distribute copies of the library, whether gratis 37 | or for a fee, you must give the recipients all the rights that we gave 38 | you. You must make sure that they, too, receive or can get the source 39 | code. If you link a program with the library, you must provide 40 | complete object files to the recipients so that they can relink them 41 | with the library, after making changes to the library and recompiling 42 | it. And you must show them these terms so they know their rights. 43 | 44 | Our method of protecting your rights has two steps: (1) copyright 45 | the library, and (2) offer you this license which gives you legal 46 | permission to copy, distribute and/or modify the library. 47 | 48 | Also, for each distributor's protection, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | library. If the library is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original 52 | version, so that any problems introduced by others will not reflect on 53 | the original authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that companies distributing free 57 | software will individually obtain patent licenses, thus in effect 58 | transforming the program into proprietary software. To prevent this, 59 | we have made it clear that any patent must be licensed for everyone's 60 | free use or not licensed at all. 61 | 62 | Most GNU software, including some libraries, is covered by the ordinary 63 | GNU General Public License, which was designed for utility programs. This 64 | license, the GNU Library General Public License, applies to certain 65 | designated libraries. This license is quite different from the ordinary 66 | one; be sure to read it in full, and don't assume that anything in it is 67 | the same as in the ordinary license. 68 | 69 | The reason we have a separate public license for some libraries is that 70 | they blur the distinction we usually make between modifying or adding to a 71 | program and simply using it. Linking a program with a library, without 72 | changing the library, is in some sense simply using the library, and is 73 | analogous to running a utility program or application program. However, in 74 | a textual and legal sense, the linked executable is a combined work, a 75 | derivative of the original library, and the ordinary General Public License 76 | treats it as such. 77 | 78 | Because of this blurred distinction, using the ordinary General 79 | Public License for libraries did not effectively promote software 80 | sharing, because most developers did not use the libraries. We 81 | concluded that weaker conditions might promote sharing better. 82 | 83 | However, unrestricted linking of non-free programs would deprive the 84 | users of those programs of all benefit from the free status of the 85 | libraries themselves. This Library General Public License is intended to 86 | permit developers of non-free programs to use free libraries, while 87 | preserving your freedom as a user of such programs to change the free 88 | libraries that are incorporated in them. (We have not seen how to achieve 89 | this as regards changes in header files, but we have achieved it as regards 90 | changes in the actual functions of the Library.) The hope is that this 91 | will lead to faster development of free libraries. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. Pay close attention to the difference between a 95 | "work based on the library" and a "work that uses the library". The 96 | former contains code derived from the library, while the latter only 97 | works together with the library. 98 | 99 | Note that it is possible for a library to be covered by the ordinary 100 | General Public License rather than by this special one. 101 | 102 | GNU LIBRARY GENERAL PUBLIC LICENSE 103 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 104 | 105 | 0. This License Agreement applies to any software library which 106 | contains a notice placed by the copyright holder or other authorized 107 | party saying it may be distributed under the terms of this Library 108 | General Public License (also called "this License"). Each licensee is 109 | addressed as "you". 110 | 111 | A "library" means a collection of software functions and/or data 112 | prepared so as to be conveniently linked with application programs 113 | (which use some of those functions and data) to form executables. 114 | 115 | The "Library", below, refers to any such software library or work 116 | which has been distributed under these terms. A "work based on the 117 | Library" means either the Library or any derivative work under 118 | copyright law: that is to say, a work containing the Library or a 119 | portion of it, either verbatim or with modifications and/or translated 120 | straightforwardly into another language. (Hereinafter, translation is 121 | included without limitation in the term "modification".) 122 | 123 | "Source code" for a work means the preferred form of the work for 124 | making modifications to it. For a library, complete source code means 125 | all the source code for all modules it contains, plus any associated 126 | interface definition files, plus the scripts used to control compilation 127 | and installation of the library. 128 | 129 | Activities other than copying, distribution and modification are not 130 | covered by this License; they are outside its scope. The act of 131 | running a program using the Library is not restricted, and output from 132 | such a program is covered only if its contents constitute a work based 133 | on the Library (independent of the use of the Library in a tool for 134 | writing it). Whether that is true depends on what the Library does 135 | and what the program that uses the Library does. 136 | 137 | 1. You may copy and distribute verbatim copies of the Library's 138 | complete source code as you receive it, in any medium, provided that 139 | you conspicuously and appropriately publish on each copy an 140 | appropriate copyright notice and disclaimer of warranty; keep intact 141 | all the notices that refer to this License and to the absence of any 142 | warranty; and distribute a copy of this License along with the 143 | Library. 144 | 145 | You may charge a fee for the physical act of transferring a copy, 146 | and you may at your option offer warranty protection in exchange for a 147 | fee. 148 | 149 | 2. You may modify your copy or copies of the Library or any portion 150 | of it, thus forming a work based on the Library, and copy and 151 | distribute such modifications or work under the terms of Section 1 152 | above, provided that you also meet all of these conditions: 153 | 154 | a) The modified work must itself be a software library. 155 | 156 | b) You must cause the files modified to carry prominent notices 157 | stating that you changed the files and the date of any change. 158 | 159 | c) You must cause the whole of the work to be licensed at no 160 | charge to all third parties under the terms of this License. 161 | 162 | d) If a facility in the modified Library refers to a function or a 163 | table of data to be supplied by an application program that uses 164 | the facility, other than as an argument passed when the facility 165 | is invoked, then you must make a good faith effort to ensure that, 166 | in the event an application does not supply such function or 167 | table, the facility still operates, and performs whatever part of 168 | its purpose remains meaningful. 169 | 170 | (For example, a function in a library to compute square roots has 171 | a purpose that is entirely well-defined independent of the 172 | application. Therefore, Subsection 2d requires that any 173 | application-supplied function or table used by this function must 174 | be optional: if the application does not supply it, the square 175 | root function must still compute square roots.) 176 | 177 | These requirements apply to the modified work as a whole. If 178 | identifiable sections of that work are not derived from the Library, 179 | and can be reasonably considered independent and separate works in 180 | themselves, then this License, and its terms, do not apply to those 181 | sections when you distribute them as separate works. But when you 182 | distribute the same sections as part of a whole which is a work based 183 | on the Library, the distribution of the whole must be on the terms of 184 | this License, whose permissions for other licensees extend to the 185 | entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest 189 | your rights to work written entirely by you; rather, the intent is to 190 | exercise the right to control the distribution of derivative or 191 | collective works based on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library 194 | with the Library (or with a work based on the Library) on a volume of 195 | a storage or distribution medium does not bring the other work under 196 | the scope of this License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public 199 | License instead of this License to a given copy of the Library. To do 200 | this, you must alter all the notices that refer to this License, so 201 | that they refer to the ordinary GNU General Public License, version 2, 202 | instead of to this License. (If a newer version than version 2 of the 203 | ordinary GNU General Public License has appeared, then you can specify 204 | that version instead if you wish.) Do not make any other change in 205 | these notices. 206 | 207 | Once this change is made in a given copy, it is irreversible for 208 | that copy, so the ordinary GNU General Public License applies to all 209 | subsequent copies and derivative works made from that copy. 210 | 211 | This option is useful when you wish to copy part of the code of 212 | the Library into a program that is not a library. 213 | 214 | 4. You may copy and distribute the Library (or a portion or 215 | derivative of it, under Section 2) in object code or executable form 216 | under the terms of Sections 1 and 2 above provided that you accompany 217 | it with the complete corresponding machine-readable source code, which 218 | must be distributed under the terms of Sections 1 and 2 above on a 219 | medium customarily used for software interchange. 220 | 221 | If distribution of object code is made by offering access to copy 222 | from a designated place, then offering equivalent access to copy the 223 | source code from the same place satisfies the requirement to 224 | distribute the source code, even though third parties are not 225 | compelled to copy the source along with the object code. 226 | 227 | 5. A program that contains no derivative of any portion of the 228 | Library, but is designed to work with the Library by being compiled or 229 | linked with it, is called a "work that uses the Library". Such a 230 | work, in isolation, is not a derivative work of the Library, and 231 | therefore falls outside the scope of this License. 232 | 233 | However, linking a "work that uses the Library" with the Library 234 | creates an executable that is a derivative of the Library (because it 235 | contains portions of the Library), rather than a "work that uses the 236 | library". The executable is therefore covered by this License. 237 | Section 6 states terms for distribution of such executables. 238 | 239 | When a "work that uses the Library" uses material from a header file 240 | that is part of the Library, the object code for the work may be a 241 | derivative work of the Library even though the source code is not. 242 | Whether this is true is especially significant if the work can be 243 | linked without the Library, or if the work is itself a library. The 244 | threshold for this to be true is not precisely defined by law. 245 | 246 | If such an object file uses only numerical parameters, data 247 | structure layouts and accessors, and small macros and small inline 248 | functions (ten lines or less in length), then the use of the object 249 | file is unrestricted, regardless of whether it is legally a derivative 250 | work. (Executables containing this object code plus portions of the 251 | Library will still fall under Section 6.) 252 | 253 | Otherwise, if the work is a derivative of the Library, you may 254 | distribute the object code for the work under the terms of Section 6. 255 | Any executables containing that work also fall under Section 6, 256 | whether or not they are linked directly with the Library itself. 257 | 258 | 6. As an exception to the Sections above, you may also compile or 259 | link a "work that uses the Library" with the Library to produce a 260 | work containing portions of the Library, and distribute that work 261 | under terms of your choice, provided that the terms permit 262 | modification of the work for the customer's own use and reverse 263 | engineering for debugging such modifications. 264 | 265 | You must give prominent notice with each copy of the work that the 266 | Library is used in it and that the Library and its use are covered by 267 | this License. You must supply a copy of this License. If the work 268 | during execution displays copyright notices, you must include the 269 | copyright notice for the Library among them, as well as a reference 270 | directing the user to the copy of this License. Also, you must do one 271 | of these things: 272 | 273 | a) Accompany the work with the complete corresponding 274 | machine-readable source code for the Library including whatever 275 | changes were used in the work (which must be distributed under 276 | Sections 1 and 2 above); and, if the work is an executable linked 277 | with the Library, with the complete machine-readable "work that 278 | uses the Library", as object code and/or source code, so that the 279 | user can modify the Library and then relink to produce a modified 280 | executable containing the modified Library. (It is understood 281 | that the user who changes the contents of definitions files in the 282 | Library will not necessarily be able to recompile the application 283 | to use the modified definitions.) 284 | 285 | b) Accompany the work with a written offer, valid for at 286 | least three years, to give the same user the materials 287 | specified in Subsection 6a, above, for a charge no more 288 | than the cost of performing this distribution. 289 | 290 | c) If distribution of the work is made by offering access to copy 291 | from a designated place, offer equivalent access to copy the above 292 | specified materials from the same place. 293 | 294 | d) Verify that the user has already received a copy of these 295 | materials or that you have already sent this user a copy. 296 | 297 | For an executable, the required form of the "work that uses the 298 | Library" must include any data and utility programs needed for 299 | reproducing the executable from it. However, as a special exception, 300 | the source code distributed need not include anything that is normally 301 | distributed (in either source or binary form) with the major 302 | components (compiler, kernel, and so on) of the operating system on 303 | which the executable runs, unless that component itself accompanies 304 | the executable. 305 | 306 | It may happen that this requirement contradicts the license 307 | restrictions of other proprietary libraries that do not normally 308 | accompany the operating system. Such a contradiction means you cannot 309 | use both them and the Library together in an executable that you 310 | distribute. 311 | 312 | 7. You may place library facilities that are a work based on the 313 | Library side-by-side in a single library together with other library 314 | facilities not covered by this License, and distribute such a combined 315 | library, provided that the separate distribution of the work based on 316 | the Library and of the other library facilities is otherwise 317 | permitted, and provided that you do these two things: 318 | 319 | a) Accompany the combined library with a copy of the same work 320 | based on the Library, uncombined with any other library 321 | facilities. This must be distributed under the terms of the 322 | Sections above. 323 | 324 | b) Give prominent notice with the combined library of the fact 325 | that part of it is a work based on the Library, and explaining 326 | where to find the accompanying uncombined form of the same work. 327 | 328 | 8. You may not copy, modify, sublicense, link with, or distribute 329 | the Library except as expressly provided under this License. Any 330 | attempt otherwise to copy, modify, sublicense, link with, or 331 | distribute the Library is void, and will automatically terminate your 332 | rights under this License. However, parties who have received copies, 333 | or rights, from you under this License will not have their licenses 334 | terminated so long as such parties remain in full compliance. 335 | 336 | 9. You are not required to accept this License, since you have not 337 | signed it. However, nothing else grants you permission to modify or 338 | distribute the Library or its derivative works. These actions are 339 | prohibited by law if you do not accept this License. Therefore, by 340 | modifying or distributing the Library (or any work based on the 341 | Library), you indicate your acceptance of this License to do so, and 342 | all its terms and conditions for copying, distributing or modifying 343 | the Library or works based on it. 344 | 345 | 10. Each time you redistribute the Library (or any work based on the 346 | Library), the recipient automatically receives a license from the 347 | original licensor to copy, distribute, link with or modify the Library 348 | subject to these terms and conditions. You may not impose any further 349 | restrictions on the recipients' exercise of the rights granted herein. 350 | You are not responsible for enforcing compliance by third parties to 351 | this License. 352 | 353 | 11. If, as a consequence of a court judgment or allegation of patent 354 | infringement or for any other reason (not limited to patent issues), 355 | conditions are imposed on you (whether by court order, agreement or 356 | otherwise) that contradict the conditions of this License, they do not 357 | excuse you from the conditions of this License. If you cannot 358 | distribute so as to satisfy simultaneously your obligations under this 359 | License and any other pertinent obligations, then as a consequence you 360 | may not distribute the Library at all. For example, if a patent 361 | license would not permit royalty-free redistribution of the Library by 362 | all those who receive copies directly or indirectly through you, then 363 | the only way you could satisfy both it and this License would be to 364 | refrain entirely from distribution of the Library. 365 | 366 | If any portion of this section is held invalid or unenforceable under any 367 | particular circumstance, the balance of the section is intended to apply, 368 | and the section as a whole is intended to apply in other circumstances. 369 | 370 | It is not the purpose of this section to induce you to infringe any 371 | patents or other property right claims or to contest validity of any 372 | such claims; this section has the sole purpose of protecting the 373 | integrity of the free software distribution system which is 374 | implemented by public license practices. Many people have made 375 | generous contributions to the wide range of software distributed 376 | through that system in reliance on consistent application of that 377 | system; it is up to the author/donor to decide if he or she is willing 378 | to distribute software through any other system and a licensee cannot 379 | impose that choice. 380 | 381 | This section is intended to make thoroughly clear what is believed to 382 | be a consequence of the rest of this License. 383 | 384 | 12. If the distribution and/or use of the Library is restricted in 385 | certain countries either by patents or by copyrighted interfaces, the 386 | original copyright holder who places the Library under this License may add 387 | an explicit geographical distribution limitation excluding those countries, 388 | so that distribution is permitted only in or among countries not thus 389 | excluded. In such case, this License incorporates the limitation as if 390 | written in the body of this License. 391 | 392 | 13. The Free Software Foundation may publish revised and/or new 393 | versions of the Library General Public License from time to time. 394 | Such new versions will be similar in spirit to the present version, 395 | but may differ in detail to address new problems or concerns. 396 | 397 | Each version is given a distinguishing version number. If the Library 398 | specifies a version number of this License which applies to it and 399 | "any later version", you have the option of following the terms and 400 | conditions either of that version or of any later version published by 401 | the Free Software Foundation. If the Library does not specify a 402 | license version number, you may choose any version ever published by 403 | the Free Software Foundation. 404 | 405 | 14. If you wish to incorporate parts of the Library into other free 406 | programs whose distribution conditions are incompatible with these, 407 | write to the author to ask for permission. For software which is 408 | copyrighted by the Free Software Foundation, write to the Free 409 | Software Foundation; we sometimes make exceptions for this. Our 410 | decision will be guided by the two goals of preserving the free status 411 | of all derivatives of our free software and of promoting the sharing 412 | and reuse of software generally. 413 | 414 | NO WARRANTY 415 | 416 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 417 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 418 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 419 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 420 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 421 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 422 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 423 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 424 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 425 | 426 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 427 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 428 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 429 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 430 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 431 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 432 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 433 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 434 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 435 | DAMAGES. 436 | 437 | END OF TERMS AND CONDITIONS 438 | 439 | How to Apply These Terms to Your New Libraries 440 | 441 | If you develop a new library, and you want it to be of the greatest 442 | possible use to the public, we recommend making it free software that 443 | everyone can redistribute and change. You can do so by permitting 444 | redistribution under these terms (or, alternatively, under the terms of the 445 | ordinary General Public License). 446 | 447 | To apply these terms, attach the following notices to the library. It is 448 | safest to attach them to the start of each source file to most effectively 449 | convey the exclusion of warranty; and each file should have at least the 450 | "copyright" line and a pointer to where the full notice is found. 451 | 452 | 453 | Copyright (C) 454 | 455 | This library is free software; you can redistribute it and/or 456 | modify it under the terms of the GNU Library General Public 457 | License as published by the Free Software Foundation; either 458 | version 2 of the License, or (at your option) any later version. 459 | 460 | This library is distributed in the hope that it will be useful, 461 | but WITHOUT ANY WARRANTY; without even the implied warranty of 462 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 463 | Library General Public License for more details. 464 | 465 | You should have received a copy of the GNU Library General Public 466 | License along with this library; if not, write to the Free Software 467 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 468 | 469 | Also add information on how to contact you by electronic and paper mail. 470 | 471 | You should also get your employer (if you work as a programmer) or your 472 | school, if any, to sign a "copyright disclaimer" for the library, if 473 | necessary. Here is a sample; alter the names: 474 | 475 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 476 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 477 | 478 | , 1 April 1990 479 | Ty Coon, President of Vice 480 | 481 | That's all there is to it! 482 | -------------------------------------------------------------------------------- /src/git-evtag.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- 2 | * 3 | * Copyright (C) 2015 Colin Walters 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General 16 | * Public License along with this library; if not, see . 17 | */ 18 | 19 | #include "config.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if !GLIB_CHECK_VERSION(2, 70, 0) 27 | /* The functionality of check_wait_status was available under a misleading 28 | * name in older versions of GLib. The argument was always a wait-status, 29 | * not an exit status. */ 30 | #define g_spawn_check_wait_status(status, error) g_spawn_check_exit_status (status, error) 31 | #endif 32 | 33 | #define EVTAG_SHA512 "Git-EVTag-v0-SHA512:" 34 | #define LEGACY_EVTAG_ARCHIVE_TAR "ExtendedVerify-SHA256-archive-tar:" 35 | #define LEGACY_EVTAG_ARCHIVE_TAR_GITVERSION "ExtendedVerify-git-version:" 36 | 37 | struct EvTag; 38 | 39 | typedef struct { 40 | const char *name; 41 | gboolean (*fn) (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error); 42 | } Subcommand; 43 | 44 | #define SUBCOMMANDPROTO(name) static gboolean git_evtag_builtin_ ## name (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) 45 | 46 | SUBCOMMANDPROTO(sign); 47 | SUBCOMMANDPROTO(verify); 48 | 49 | static Subcommand commands[] = { 50 | { "sign", git_evtag_builtin_sign }, 51 | { "verify", git_evtag_builtin_verify }, 52 | { NULL, NULL } 53 | }; 54 | 55 | static gboolean opt_verbose; 56 | static gboolean opt_version; 57 | static gboolean opt_print_only; 58 | static gboolean opt_no_signature; 59 | static gboolean opt_with_legacy_archive_tag; 60 | static char *opt_keyid; 61 | 62 | static GOptionEntry global_entries[] = { 63 | { "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version information and exit", NULL }, 64 | { NULL } 65 | }; 66 | 67 | static GOptionEntry sign_options[] = { 68 | { "print-only", 0, 0, G_OPTION_ARG_NONE, &opt_print_only, "Don't create a tag, just compute and print evtag data", NULL }, 69 | { "no-signature", 0, 0, G_OPTION_ARG_NONE, &opt_no_signature, "Do create or verify GPG signature", NULL }, 70 | { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print statistics on what we're hashing", NULL }, 71 | { "local-user", 'u', 0, G_OPTION_ARG_STRING, &opt_keyid, "Use the given GPG KEYID", "KEYID" }, 72 | { "with-legacy-archive-tag", 'u', 0, G_OPTION_ARG_NONE, &opt_with_legacy_archive_tag, "Also append a legacy variant of the checksum using `git archive`", NULL }, 73 | { NULL } 74 | }; 75 | 76 | static GOptionEntry verify_options[] = { 77 | { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print statistics on what we're hashing", NULL }, 78 | { "no-signature", 0, 0, G_OPTION_ARG_NONE, &opt_no_signature, "Do create or verify GPG signature", NULL }, 79 | { NULL } 80 | }; 81 | 82 | static gboolean 83 | option_context_parse (GOptionContext *context, 84 | const GOptionEntry *main_entries, 85 | int *argc, 86 | char ***argv, 87 | GCancellable *cancellable, 88 | GError **error) 89 | { 90 | gboolean ret = FALSE; 91 | 92 | if (main_entries != NULL) 93 | g_option_context_add_main_entries (context, main_entries, NULL); 94 | 95 | g_option_context_add_main_entries (context, global_entries, NULL); 96 | 97 | if (!g_option_context_parse (context, argc, argv, error)) 98 | goto out; 99 | 100 | if (opt_version) 101 | { 102 | g_print ("%s\n +default\n", PACKAGE_STRING); 103 | exit (EXIT_SUCCESS); 104 | } 105 | 106 | ret = TRUE; 107 | out: 108 | return ret; 109 | } 110 | 111 | static char * 112 | debug_oid2str (const git_oid *oid) __attribute ((unused)); 113 | 114 | static char * 115 | debug_oid2str (const git_oid *oid) 116 | { 117 | static char buf[GIT_OID_HEXSZ]; 118 | return git_oid_tostr (buf, sizeof (buf), oid); 119 | } 120 | 121 | static gboolean 122 | spawn_sync_require_success (char **argv, 123 | GSpawnFlags flags, 124 | GError **error) 125 | { 126 | gboolean ret = FALSE; 127 | int wait_status; 128 | 129 | if (!g_spawn_sync (NULL, argv, NULL, 130 | flags, NULL, NULL, 131 | NULL, NULL, &wait_status, error)) 132 | goto out; 133 | if (!g_spawn_check_wait_status (wait_status, error)) 134 | goto out; 135 | 136 | ret = TRUE; 137 | out: 138 | return ret; 139 | } 140 | 141 | static gboolean 142 | handle_libgit_ret (int r, GError **error) 143 | { 144 | const git_error *giterror; 145 | 146 | if (!r) 147 | return TRUE; 148 | 149 | giterror = giterr_last(); 150 | g_assert (giterror != NULL); 151 | 152 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 153 | giterror->message ? giterror->message: "???"); 154 | return FALSE; 155 | } 156 | 157 | struct EvTag { 158 | git_repository *top_repo; 159 | 160 | GChecksum *checksum; 161 | guint n_submodules; 162 | guint n_commits; 163 | guint64 commit_bytes; 164 | guint n_trees; 165 | guint64 tree_bytes; 166 | guint n_blobs; 167 | guint64 blob_bytes; 168 | }; 169 | 170 | static void 171 | checksum_odb_object (struct EvTag *self, 172 | git_odb_object *object) 173 | { 174 | git_otype otype = git_odb_object_type (object); 175 | const char *otypestr = git_object_type2string (otype); 176 | size_t size = git_odb_object_size (object); 177 | char *header; 178 | size_t headerlen; 179 | 180 | header = g_strdup_printf ("%s %" G_GSIZE_FORMAT, otypestr, size); 181 | /* Also include the trailing NUL byte */ 182 | headerlen = strlen (header) + 1; 183 | g_checksum_update (self->checksum, (guint8*)header, headerlen); 184 | g_free (header); 185 | 186 | switch (otype) 187 | { 188 | case GIT_OBJ_BLOB: 189 | self->n_blobs++; 190 | self->blob_bytes += size + headerlen; 191 | break; 192 | case GIT_OBJ_COMMIT: 193 | self->n_commits++; 194 | self->commit_bytes += size + headerlen; 195 | break; 196 | case GIT_OBJ_TREE: 197 | self->n_trees++; 198 | self->tree_bytes += size + headerlen; 199 | break; 200 | default: 201 | g_assert_not_reached (); 202 | } 203 | 204 | g_checksum_update (self->checksum, git_odb_object_data (object), size); 205 | } 206 | 207 | struct TreeWalkData { 208 | gboolean caught_error; 209 | struct EvTag *evtag; 210 | git_repository *repo; 211 | git_odb *odb; 212 | GCancellable *cancellable; 213 | GError **error; 214 | }; 215 | 216 | static gboolean 217 | checksum_object_id (struct TreeWalkData *twdata, 218 | const git_oid *oid, 219 | GError **error) 220 | { 221 | gboolean ret = FALSE; 222 | int r; 223 | git_odb_object *odbobj = NULL; 224 | 225 | r = git_odb_read (&odbobj, twdata->odb, oid); 226 | if (!handle_libgit_ret (r, error)) 227 | goto out; 228 | 229 | checksum_odb_object (twdata->evtag, odbobj); 230 | 231 | ret = TRUE; 232 | out: 233 | if (odbobj) 234 | git_odb_object_free (odbobj); 235 | return ret; 236 | } 237 | 238 | static int 239 | checksum_submodule (struct TreeWalkData *twdata, git_submodule *sm); 240 | 241 | static int 242 | checksum_tree_callback (const char *root, 243 | const git_tree_entry *entry, 244 | void *data) 245 | { 246 | int iter_r = 1; 247 | int tmp_r; 248 | struct TreeWalkData *twdata = data; 249 | git_otype otype = git_tree_entry_type (entry); 250 | 251 | if (twdata->caught_error) 252 | return -1; 253 | 254 | switch (otype) 255 | { 256 | case GIT_OBJ_TREE: 257 | case GIT_OBJ_BLOB: 258 | if (!checksum_object_id (twdata, git_tree_entry_id (entry), 259 | twdata->error)) 260 | { 261 | twdata->caught_error = TRUE; 262 | return -1; 263 | } 264 | break; 265 | case GIT_OBJ_COMMIT: 266 | { 267 | git_submodule *submod = NULL; 268 | char *submodule_path = g_build_filename (root, git_tree_entry_name (entry), NULL); 269 | tmp_r = git_submodule_lookup (&submod, twdata->repo, submodule_path); 270 | g_free (submodule_path); 271 | 272 | if (!handle_libgit_ret (tmp_r, twdata->error)) 273 | goto out; 274 | 275 | tmp_r = checksum_submodule (twdata, submod); 276 | if (tmp_r != 0) 277 | goto out; 278 | 279 | git_submodule_free (submod); 280 | } 281 | break; 282 | default: 283 | g_assert_not_reached (); 284 | } 285 | 286 | iter_r = 0; 287 | out: 288 | if (iter_r != 0) 289 | twdata->caught_error = TRUE; 290 | return iter_r; 291 | } 292 | 293 | static gboolean 294 | checksum_commit_contents (struct TreeWalkData *twdata, 295 | const git_oid *commit_oid, 296 | GCancellable *cancellable, GError **error) 297 | { 298 | gboolean ret = FALSE; 299 | int r; 300 | git_commit *commit = NULL; 301 | git_tree *tree = NULL; 302 | 303 | r = git_commit_lookup (&commit, twdata->repo, commit_oid); 304 | if (!handle_libgit_ret (r, error)) 305 | goto out; 306 | 307 | if (!checksum_object_id (twdata, commit_oid, error)) 308 | goto out; 309 | 310 | r = git_commit_tree (&tree, commit); 311 | if (!handle_libgit_ret (r, error)) 312 | goto out; 313 | 314 | if (!checksum_object_id (twdata, git_object_id((git_object*)tree), error)) 315 | goto out; 316 | 317 | r = git_tree_walk (tree, GIT_TREEWALK_PRE, checksum_tree_callback, twdata); 318 | if (twdata->caught_error) 319 | goto out; 320 | if (!handle_libgit_ret (r, error)) 321 | goto out; 322 | 323 | ret = TRUE; 324 | out: 325 | if (commit) 326 | git_commit_free (commit); 327 | if (tree) 328 | git_tree_free (tree); 329 | return ret; 330 | } 331 | 332 | static int 333 | checksum_submodule (struct TreeWalkData *parent_twdata, git_submodule *sub) 334 | { 335 | int r = 1; 336 | const git_oid *sub_head; 337 | struct TreeWalkData child_twdata = { FALSE, parent_twdata->evtag, NULL, NULL, 338 | parent_twdata->cancellable, 339 | parent_twdata->error }; 340 | 341 | parent_twdata->evtag->n_submodules++; 342 | 343 | r = git_submodule_open (&child_twdata.repo, sub); 344 | if (!handle_libgit_ret (r, child_twdata.error)) 345 | { 346 | g_prefix_error (child_twdata.error, "Missing `git submodule update --init`? "); 347 | goto out; 348 | } 349 | 350 | r = git_repository_odb (&child_twdata.odb, child_twdata.repo); 351 | if (!handle_libgit_ret (r, child_twdata.error)) 352 | goto out; 353 | 354 | sub_head = git_submodule_wd_id (sub); 355 | 356 | if (!checksum_commit_contents (&child_twdata, sub_head, 357 | child_twdata.cancellable, child_twdata.error)) 358 | goto out; 359 | 360 | r = 0; 361 | out: 362 | if (r < 0) 363 | parent_twdata->caught_error = TRUE; 364 | if (child_twdata.repo) 365 | git_repository_free (child_twdata.repo); 366 | if (child_twdata.odb) 367 | git_odb_free (child_twdata.odb); 368 | return r; 369 | } 370 | 371 | static gboolean 372 | verify_line (const char *expected_checksum, 373 | const char *line, 374 | const char *rev, 375 | GError **error) 376 | { 377 | gboolean ret = FALSE; 378 | const char *provided_checksum; 379 | 380 | g_assert (g_str_has_prefix (line, EVTAG_SHA512)); 381 | 382 | provided_checksum = line + strlen (EVTAG_SHA512); 383 | provided_checksum += strspn (provided_checksum, " \t"); 384 | if (strcmp (provided_checksum, expected_checksum) != 0) 385 | { 386 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, 387 | "Invalid %s, actual checksum of %s is %s", 388 | line, rev, expected_checksum); 389 | goto out; 390 | } 391 | 392 | ret = TRUE; 393 | out: 394 | return ret; 395 | } 396 | 397 | static char * 398 | get_stats (struct EvTag *self) 399 | { 400 | return g_strdup_printf ("# git-evtag comment: submodules=%u " 401 | "commits=%u (%" G_GUINT64_FORMAT ") " 402 | "trees=%u (%" G_GUINT64_FORMAT ") " 403 | "blobs=%u (%" G_GUINT64_FORMAT ")", 404 | self->n_submodules, 405 | self->n_commits, 406 | self->commit_bytes, 407 | self->n_trees, 408 | self->tree_bytes, 409 | self->n_blobs, 410 | self->blob_bytes); 411 | } 412 | 413 | static gboolean 414 | check_file_has_evtag (const char *path, 415 | gboolean *out_have_evtag, 416 | GError **error) 417 | { 418 | gboolean ret = FALSE; 419 | char *message = NULL; 420 | const char *p; 421 | const char *nl; 422 | gboolean found = FALSE; 423 | 424 | if (!g_file_get_contents (path, &message, NULL, error)) 425 | goto out; 426 | 427 | p = message; 428 | while (TRUE) 429 | { 430 | nl = strchr (p, '\n'); 431 | 432 | if (g_str_has_prefix (p, EVTAG_SHA512)) 433 | { 434 | found = TRUE; 435 | break; 436 | } 437 | 438 | if (!nl) 439 | break; 440 | p = nl + 1; 441 | } 442 | 443 | ret = TRUE; 444 | *out_have_evtag = found; 445 | out: 446 | g_free (message); 447 | return ret; 448 | } 449 | 450 | static int 451 | status_cb (const char *path, unsigned int status_flags, void *payload) 452 | { 453 | int r = 1; 454 | struct TreeWalkData *twdata = payload; 455 | 456 | if (status_flags != 0) 457 | { 458 | g_assert (!twdata->caught_error); 459 | twdata->caught_error = TRUE; 460 | g_set_error (twdata->error, G_IO_ERROR, G_IO_ERROR_FAILED, 461 | "Attempting to tag or verify dirty tree (%s); use --force-unclean to override", 462 | path); 463 | goto out; 464 | } 465 | 466 | r = 0; 467 | out: 468 | return r; 469 | } 470 | 471 | static gboolean 472 | compute_and_append_legacy_archive_checksum (const char *commit, 473 | GString *buf, 474 | GCancellable *cancellable, 475 | GError **error) 476 | { 477 | gboolean ret = FALSE; 478 | const char *archive_argv[] = {"git", "archive", "--format=tar", commit, NULL}; 479 | GSubprocess *gitarchive_proc = NULL; 480 | GInputStream *gitarchive_output = NULL; 481 | guint64 legacy_checksum_start; 482 | guint64 legacy_checksum_end; 483 | GChecksum *legacy_archive_sha256 = g_checksum_new (G_CHECKSUM_SHA256); 484 | gssize bytes_read; 485 | char readbuf[4096]; 486 | char *gitversion = NULL; 487 | int wait_status; 488 | char *nl; 489 | 490 | legacy_checksum_start = g_get_monotonic_time (); 491 | 492 | gitarchive_proc = g_subprocess_newv (archive_argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); 493 | 494 | if (!gitarchive_proc) 495 | goto out; 496 | 497 | gitarchive_output = g_subprocess_get_stdout_pipe (gitarchive_proc); 498 | 499 | while ((bytes_read = g_input_stream_read (gitarchive_output, readbuf, sizeof (readbuf), 500 | cancellable, error)) > 0) 501 | g_checksum_update (legacy_archive_sha256, (guint8*)readbuf, bytes_read); 502 | if (bytes_read < 0) 503 | goto out; 504 | legacy_checksum_end = g_get_monotonic_time (); 505 | 506 | g_string_append_printf (buf, "# git-evtag comment: Computed legacy checksum in %0.1fs\n", 507 | (double)(legacy_checksum_end - legacy_checksum_start) / (double) G_USEC_PER_SEC); 508 | 509 | g_string_append (buf, LEGACY_EVTAG_ARCHIVE_TAR); 510 | g_string_append_c (buf, ' '); 511 | g_string_append (buf, g_checksum_get_string (legacy_archive_sha256)); 512 | g_string_append_c (buf, '\n'); 513 | 514 | if (!g_spawn_command_line_sync ("git --version", &gitversion, NULL, &wait_status, error)) 515 | goto out; 516 | if (!g_spawn_check_wait_status (wait_status, error)) 517 | goto out; 518 | 519 | nl = strchr (gitversion, '\n'); 520 | if (!nl) 521 | { 522 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 523 | "git --version returned invalid content without a newline"); 524 | goto out; 525 | } 526 | 527 | *nl = '\0'; 528 | g_strchomp (gitversion); 529 | 530 | g_string_append (buf, LEGACY_EVTAG_ARCHIVE_TAR_GITVERSION); 531 | g_string_append_c (buf, ' '); 532 | g_string_append (buf, gitversion); 533 | g_string_append_c (buf, '\n'); 534 | 535 | ret = TRUE; 536 | out: 537 | return ret; 538 | } 539 | 540 | static gboolean 541 | validate_at_head (struct EvTag *self, 542 | git_oid *specified_oid, 543 | GError **error) 544 | { 545 | gboolean ret = FALSE; 546 | git_oid head_oid; 547 | int r; 548 | 549 | r = git_reference_name_to_id (&head_oid, self->top_repo, "HEAD"); 550 | if (!handle_libgit_ret (r, error)) 551 | goto out; 552 | 553 | /* We have this restriction due to submodules; we require them to be 554 | * checked out and initialized. 555 | */ 556 | if (git_oid_cmp (&head_oid, specified_oid) != 0) 557 | { 558 | char head_oid_hexstr[GIT_OID_HEXSZ+1]; 559 | char specified_oid_hexstr[GIT_OID_HEXSZ+1]; 560 | 561 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, 562 | "Target %s is not HEAD (%s); currently git-evtag can only tag or verify HEAD in a pristine checkout", 563 | git_oid_tostr (specified_oid_hexstr, sizeof (specified_oid_hexstr), specified_oid), 564 | git_oid_tostr (head_oid_hexstr, sizeof (head_oid_hexstr), &head_oid)); 565 | goto out; 566 | } 567 | 568 | ret = TRUE; 569 | out: 570 | return ret; 571 | } 572 | 573 | static gboolean 574 | checksum_commit_recurse (struct EvTag *self, 575 | git_oid *specified_oid, 576 | guint64 *out_elapsed_time, 577 | GCancellable *cancellable, 578 | GError **error) 579 | { 580 | gboolean ret = FALSE; 581 | int r; 582 | guint64 checksum_start_time; 583 | guint64 checksum_end_time; 584 | 585 | checksum_start_time = g_get_monotonic_time (); 586 | { 587 | struct TreeWalkData twdata = { FALSE, self, self->top_repo, NULL, cancellable, error }; 588 | 589 | r = git_repository_odb (&twdata.odb, self->top_repo); 590 | if (!handle_libgit_ret (r, error)) 591 | goto out; 592 | 593 | if (!checksum_commit_contents (&twdata, specified_oid, cancellable, error)) 594 | goto out; 595 | } 596 | checksum_end_time = g_get_monotonic_time (); 597 | 598 | ret = TRUE; 599 | if (out_elapsed_time) 600 | *out_elapsed_time = checksum_end_time - checksum_start_time; 601 | out: 602 | return ret; 603 | } 604 | 605 | static gboolean 606 | git_evtag_builtin_sign (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) 607 | { 608 | gboolean ret = FALSE; 609 | int r; 610 | const char *tagname; 611 | git_object *obj = NULL; 612 | git_oid specified_oid; 613 | GOptionContext *optcontext; 614 | guint64 elapsed_ns; 615 | char commit_oid_hexstr[GIT_OID_HEXSZ+1]; 616 | 617 | optcontext = g_option_context_new ("TAGNAME - Create a new GPG signed tag"); 618 | 619 | if (!option_context_parse (optcontext, sign_options, &argc, &argv, 620 | cancellable, error)) 621 | goto out; 622 | 623 | if (argc < 2) 624 | { 625 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A TAGNAME argument is required"); 626 | goto out; 627 | } 628 | else if (argc > 2) 629 | { 630 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Too many arguments"); 631 | goto out; 632 | } 633 | tagname = argv[1]; 634 | 635 | r = git_revparse_single (&obj, self->top_repo, "HEAD"); 636 | if (!handle_libgit_ret (r, error)) 637 | goto out; 638 | specified_oid = *git_object_id (obj); 639 | git_oid_fmt (commit_oid_hexstr, &specified_oid); 640 | commit_oid_hexstr[sizeof(commit_oid_hexstr)-1] = '\0'; 641 | 642 | if (!validate_at_head (self, &specified_oid, error)) 643 | goto out; 644 | 645 | if (!checksum_commit_recurse (self, &specified_oid, &elapsed_ns, 646 | cancellable, error)) 647 | goto out; 648 | 649 | if (opt_print_only) 650 | { 651 | char *stats = get_stats (self); 652 | g_print ("%s\n", stats); 653 | g_free (stats); 654 | g_print ("%s %s\n", EVTAG_SHA512, g_checksum_get_string (self->checksum)); 655 | } 656 | else 657 | { 658 | const char *editor; 659 | int tmpfd; 660 | char *temppath; 661 | char *editor_child_argv[] = { NULL, NULL, NULL }; 662 | GPtrArray *gittag_child_argv = g_ptr_array_new (); 663 | GString *buf = g_string_new ("\n\n"); 664 | gboolean have_evtag; 665 | 666 | tmpfd = g_file_open_tmp ("git-evtag-XXXXXX.md", &temppath, error); 667 | if (tmpfd < 0) 668 | goto out; 669 | (void) close (tmpfd); 670 | 671 | g_string_append_printf (buf, "# git-evtag comment: Computed checksum in %0.1fs\n", 672 | (double)(elapsed_ns) / (double) G_USEC_PER_SEC); 673 | 674 | { 675 | char *stats = get_stats (self); 676 | g_string_append (buf, stats); 677 | g_string_append_c (buf, '\n'); 678 | g_free (stats); 679 | } 680 | g_string_append (buf, EVTAG_SHA512); 681 | g_string_append_c (buf, ' '); 682 | g_string_append (buf, g_checksum_get_string (self->checksum)); 683 | g_string_append_c (buf, '\n'); 684 | 685 | if (opt_with_legacy_archive_tag) 686 | { 687 | if (!compute_and_append_legacy_archive_checksum (commit_oid_hexstr, buf, 688 | cancellable, error)) 689 | goto out; 690 | } 691 | 692 | if (!g_file_set_contents (temppath, buf->str, -1, error)) 693 | goto out; 694 | g_string_free (buf, TRUE); 695 | 696 | editor = getenv ("EDITOR"); 697 | if (!editor) 698 | editor = "vi"; 699 | 700 | editor_child_argv[0] = (char*)editor; 701 | editor_child_argv[1] = (char*)temppath; 702 | if (!spawn_sync_require_success (editor_child_argv, 703 | G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, 704 | error)) 705 | goto out; 706 | 707 | if (!check_file_has_evtag (temppath, &have_evtag, error)) 708 | goto out; 709 | 710 | if (!have_evtag) 711 | { 712 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 713 | "Aborting tag due to deleted Git-EVTag line"); 714 | goto out; 715 | } 716 | 717 | g_ptr_array_add (gittag_child_argv, "git"); 718 | g_ptr_array_add (gittag_child_argv, "tag"); 719 | if (!opt_no_signature) 720 | g_ptr_array_add (gittag_child_argv, "-s"); 721 | if (opt_keyid) 722 | { 723 | g_ptr_array_add (gittag_child_argv, "--local-user"); 724 | g_ptr_array_add (gittag_child_argv, opt_keyid); 725 | } 726 | g_ptr_array_add (gittag_child_argv, "-F"); 727 | g_ptr_array_add (gittag_child_argv, temppath); 728 | g_ptr_array_add (gittag_child_argv, (char*)tagname); 729 | g_ptr_array_add (gittag_child_argv, (char*)commit_oid_hexstr); 730 | g_ptr_array_add (gittag_child_argv, NULL); 731 | if (!spawn_sync_require_success ((char**)gittag_child_argv->pdata, 732 | G_SPAWN_SEARCH_PATH, 733 | error)) 734 | { 735 | g_printerr ("Saved tag message in: %s\n", temppath); 736 | goto out; 737 | } 738 | (void) unlink (temppath); 739 | g_ptr_array_free (gittag_child_argv, TRUE); 740 | } 741 | 742 | ret = TRUE; 743 | out: 744 | return ret; 745 | } 746 | 747 | static gboolean 748 | git_evtag_builtin_verify (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) 749 | { 750 | gboolean ret = FALSE; 751 | int r; 752 | gboolean verified = FALSE; 753 | GOptionContext *optcontext; 754 | git_oid tag_oid; 755 | git_object *obj = NULL; 756 | char *long_tagname = NULL; 757 | git_tag *tag; 758 | const char *tagname; 759 | const char *message; 760 | git_oid specified_oid; 761 | const char *nl; 762 | guint64 elapsed_ns; 763 | const char *expected_checksum; 764 | char commit_oid_hexstr[GIT_OID_HEXSZ+1]; 765 | char tag_oid_hexstr[GIT_OID_HEXSZ+1]; 766 | char *git_verify_tag_argv[] = {"git", "verify-tag", NULL, NULL }; 767 | 768 | optcontext = g_option_context_new ("TAGNAME - Verify a signed tag"); 769 | 770 | if (!option_context_parse (optcontext, verify_options, &argc, &argv, 771 | cancellable, error)) 772 | goto out; 773 | 774 | if (argc < 2) 775 | { 776 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A TAGNAME argument is required"); 777 | goto out; 778 | } 779 | else if (argc > 2) 780 | { 781 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Too many arguments"); 782 | goto out; 783 | } 784 | 785 | tagname = argv[1]; 786 | 787 | long_tagname = g_strconcat ("refs/tags/", tagname, NULL); 788 | 789 | r = git_reference_name_to_id (&tag_oid, self->top_repo, long_tagname); 790 | if (!handle_libgit_ret (r, error)) 791 | goto out; 792 | r = git_tag_lookup (&tag, self->top_repo, &tag_oid); 793 | if (!handle_libgit_ret (r, error)) 794 | goto out; 795 | r = git_tag_target (&obj, tag); 796 | if (!handle_libgit_ret (r, error)) 797 | goto out; 798 | specified_oid = *git_object_id (obj); 799 | 800 | git_oid_fmt (commit_oid_hexstr, &specified_oid); 801 | commit_oid_hexstr[sizeof(commit_oid_hexstr)-1] = '\0'; 802 | 803 | if (!validate_at_head (self, &specified_oid, error)) 804 | goto out; 805 | 806 | message = git_tag_message (tag); 807 | 808 | if (!git_oid_tostr (tag_oid_hexstr, sizeof (tag_oid_hexstr), git_tag_id (tag))) 809 | g_assert_not_reached (); 810 | 811 | if (!opt_no_signature) 812 | { 813 | git_verify_tag_argv[2] = tag_oid_hexstr; 814 | if (!spawn_sync_require_success (git_verify_tag_argv, G_SPAWN_SEARCH_PATH, error)) 815 | goto out; 816 | } 817 | 818 | if (!checksum_commit_recurse (self, &specified_oid, &elapsed_ns, 819 | cancellable, error)) 820 | goto out; 821 | 822 | expected_checksum = g_checksum_get_string (self->checksum); 823 | 824 | while (TRUE) 825 | { 826 | nl = strchr (message, '\n'); 827 | 828 | if (g_str_has_prefix (message, EVTAG_SHA512)) 829 | { 830 | char *line; 831 | if (nl) 832 | line = g_strndup (message, nl - message); 833 | else 834 | line = g_strdup (message); 835 | 836 | g_strchomp (line); 837 | 838 | if (!verify_line (expected_checksum, line, commit_oid_hexstr, error)) 839 | goto out; 840 | 841 | { 842 | char *stats = get_stats (self); 843 | g_print ("%s\n", stats); 844 | g_free (stats); 845 | } 846 | g_print ("Successfully verified: %s\n", line); 847 | verified = TRUE; 848 | break; 849 | } 850 | 851 | if (!nl) 852 | break; 853 | message = nl + 1; 854 | } 855 | 856 | if (!verified) 857 | { 858 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, 859 | "Failed to find %s in tag message", 860 | EVTAG_SHA512); 861 | goto out; 862 | } 863 | 864 | ret = TRUE; 865 | out: 866 | return ret; 867 | } 868 | 869 | static GOptionContext * 870 | option_context_new_with_commands (Subcommand *commands) 871 | { 872 | GOptionContext *context; 873 | GString *summary; 874 | 875 | context = g_option_context_new ("COMMAND"); 876 | 877 | summary = g_string_new ("Builtin Commands:"); 878 | 879 | while (commands->name != NULL) 880 | { 881 | g_string_append_printf (summary, "\n %s", commands->name); 882 | commands++; 883 | } 884 | 885 | g_option_context_set_summary (context, summary->str); 886 | 887 | g_string_free (summary, TRUE); 888 | 889 | return context; 890 | } 891 | 892 | static gboolean 893 | submain (struct EvTag *self, 894 | int argc, 895 | char **argv, 896 | GError **error) 897 | { 898 | gboolean ret = FALSE; 899 | git_status_options statusopts = GIT_STATUS_OPTIONS_INIT; 900 | GCancellable *cancellable = NULL; 901 | const char *command_name = NULL; 902 | Subcommand *command; 903 | char *prgname = NULL; 904 | int in, out; 905 | int r; 906 | 907 | /* 908 | * Parse the global options. We rearrange the options as 909 | * necessary, in order to pass relevant options through 910 | * to the commands, but also have them take effect globally. 911 | */ 912 | 913 | for (in = 1, out = 1; in < argc; in++, out++) 914 | { 915 | /* The non-option is the command, take it out of the arguments */ 916 | if (argv[in][0] != '-') 917 | { 918 | if (command_name == NULL) 919 | { 920 | command_name = argv[in]; 921 | out--; 922 | continue; 923 | } 924 | } 925 | 926 | else if (g_str_equal (argv[in], "--")) 927 | { 928 | break; 929 | } 930 | 931 | argv[out] = argv[in]; 932 | } 933 | 934 | argc = out; 935 | 936 | command = commands; 937 | while (command->name) 938 | { 939 | if (g_strcmp0 (command_name, command->name) == 0) 940 | break; 941 | command++; 942 | } 943 | 944 | if (!command->fn) 945 | { 946 | GOptionContext *context; 947 | char *help; 948 | 949 | context = option_context_new_with_commands (commands); 950 | 951 | /* This will not return for some options (e.g. --version). */ 952 | if (option_context_parse (context, NULL, &argc, &argv, cancellable, error)) 953 | { 954 | if (command_name == NULL) 955 | { 956 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 957 | "No command specified"); 958 | } 959 | else 960 | { 961 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, 962 | "Unknown command '%s'", command_name); 963 | } 964 | } 965 | 966 | help = g_option_context_get_help (context, FALSE, NULL); 967 | g_printerr ("%s", help); 968 | 969 | g_option_context_free (context); 970 | 971 | goto out; 972 | } 973 | 974 | prgname = g_strdup_printf ("%s %s", g_get_prgname (), command_name); 975 | g_set_prgname (prgname); 976 | 977 | r = git_repository_open_ext (&self->top_repo, ".", 0, NULL); 978 | if (!handle_libgit_ret (r, error)) 979 | goto out; 980 | 981 | r = git_status_init_options (&statusopts, GIT_STATUS_OPTIONS_VERSION); 982 | if (!handle_libgit_ret (r, error)) 983 | goto out; 984 | 985 | { 986 | struct TreeWalkData twdata = { FALSE, self, self->top_repo, NULL, cancellable, error }; 987 | r = git_status_foreach_ext (self->top_repo, &statusopts, status_cb, &twdata); 988 | if (twdata.caught_error) 989 | goto out; 990 | if (!handle_libgit_ret (r, error)) 991 | goto out; 992 | } 993 | 994 | self->checksum = g_checksum_new (G_CHECKSUM_SHA512); 995 | 996 | if (!command->fn (self, argc, argv, cancellable, error)) 997 | goto out; 998 | 999 | ret = TRUE; 1000 | out: 1001 | return ret; 1002 | } 1003 | 1004 | int 1005 | main (int argc, 1006 | char **argv) 1007 | { 1008 | GError *local_error = NULL; 1009 | GError **error = &local_error; 1010 | struct EvTag self = { NULL, }; 1011 | 1012 | /* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */ 1013 | g_setenv ("GIO_USE_VFS", "local", TRUE); 1014 | 1015 | #ifdef HAVE_GIT_LIBGIT2_INIT 1016 | git_libgit2_init (); 1017 | #else 1018 | git_threads_init (); 1019 | #endif 1020 | 1021 | if (!submain (&self, argc, argv, error)) 1022 | goto out; 1023 | 1024 | out: 1025 | if (self.top_repo) 1026 | git_repository_free (self.top_repo); 1027 | if (self.checksum) 1028 | g_checksum_free (self.checksum); 1029 | if (local_error) 1030 | { 1031 | int is_tty = isatty (1); 1032 | const char *prefix = ""; 1033 | const char *suffix = ""; 1034 | if (is_tty) 1035 | { 1036 | prefix = "\x1b[31m\x1b[1m"; /* red, bold */ 1037 | suffix = "\x1b[22m\x1b[0m"; /* bold off, color reset */ 1038 | } 1039 | g_printerr ("%serror: %s%s\n", prefix, suffix, local_error->message); 1040 | g_error_free (local_error); 1041 | return 1; 1042 | } 1043 | return 0; 1044 | } 1045 | --------------------------------------------------------------------------------