You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by cp...@apache.org on 2018/07/29 22:32:47 UTC

[arrow] branch master updated: ARROW-2934: [Packaging] Add checksums creation to sign subcommand

This is an automated email from the ASF dual-hosted git repository.

cpcloud pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 7ffb6c5  ARROW-2934: [Packaging] Add checksums creation to sign subcommand
7ffb6c5 is described below

commit 7ffb6c5c346016b54884f58cbf79341fce8f1d13
Author: Phillip Cloud <cp...@gmail.com>
AuthorDate: Sun Jul 29 18:32:41 2018 -0400

    ARROW-2934: [Packaging] Add checksums creation to sign subcommand
    
    ![cs](https://user-images.githubusercontent.com/417981/43369024-8c5ff300-9334-11e8-9709-953bb7620acd.png)
    
    Author: Phillip Cloud <cp...@gmail.com>
    
    Closes #2338 from cpcloud/ARROW-2934 and squashes the following commits:
    
    92c004c0 <Phillip Cloud> Only generate sha1 and sha256 by default
    fb1dca0b <Phillip Cloud> Signatures must end in .asc according to ASF policy
    01045ca0 <Phillip Cloud> Remove duplicate code
    2ff820f0 <Phillip Cloud> Add some pieces of flair
    de7f1c4a <Phillip Cloud> Clean up argument passing
    84d3461c <Phillip Cloud> Fix click argument
    9acc696a <Phillip Cloud> ARROW-2934:  Add checksums creation to sign subcommand
---
 dev/tasks/crossbow.py | 114 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 88 insertions(+), 26 deletions(-)

diff --git a/dev/tasks/crossbow.py b/dev/tasks/crossbow.py
index 7a36cc7..2423237 100755
--- a/dev/tasks/crossbow.py
+++ b/dev/tasks/crossbow.py
@@ -17,6 +17,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import hashlib
 import os
 import re
 import sys
@@ -24,6 +25,7 @@ import time
 import click
 import pygit2
 import github3
+import gnupg
 
 from io import StringIO
 from pathlib import Path
@@ -41,7 +43,7 @@ class GitRemoteCallbacks(pygit2.RemoteCallbacks):
     def __init__(self, token):
         self.token = token
         self.attempts = 0
-        super(GitRemoteCallbacks, self).__init__()
+        super().__init__()
 
     def push_update_reference(self, refname, message):
         pass
@@ -126,7 +128,8 @@ class Repo:
         If an SSH github url is set, it will be replaced by the https
         equivalent.
         """
-        return self.remote.url.replace('git@github.com:', 'https://github.com/')
+        return self.remote.url.replace(
+            'git@github.com:', 'https://github.com/')
 
     @property
     def email(self):
@@ -522,17 +525,46 @@ def status(ctx, job_name):
             click.echo(filename + click.style(statemsg, fg=COLORS[state]))
 
 
+def hashbytes(bytes, algoname):
+    """Hash `bytes` using the algorithm named `algoname`.
+
+    Parameters
+    ----------
+    bytes : bytes
+        The bytes to hash
+    algoname : str
+        The name of class in the hashlib standard library module
+
+    Returns
+    -------
+    str
+        Hexadecimal digest of `bytes` hashed using `algoname`
+    """
+    algo = getattr(hashlib, algoname)()
+    algo.update(bytes)
+    result = algo.hexdigest()
+    return result
+
+
 @crossbow.command()
 @click.argument('job-name', required=True)
-@click.option('--gpg-homedir', default=None,
+@click.option('-g', '--gpg-homedir', default=None,
+              type=click.Path(exists=True, file_okay=False, dir_okay=True),
               help=('Full pathname to directory containing the public and '
                     'private keyrings. Default is whatever GnuPG defaults to'))
-@click.option('--target-dir', default=DEFAULT_ARROW_PATH / 'packages',
+@click.option('-t', '--target-dir', default=DEFAULT_ARROW_PATH / 'packages',
+              type=click.Path(file_okay=False, dir_okay=True),
               help='Directory to download the build artifacts')
+@click.option('-a', '--algorithm',
+              default=['sha1', 'sha256'],
+              show_default=True,
+              type=click.Choice(sorted(hashlib.algorithms_guaranteed)),
+              multiple=True,
+              help=('Algorithm(s) used to generate checksums. Pass multiple '
+                    'algorithms by passing -a/--algorithm multiple times'))
 @click.pass_context
-def sign(ctx, job_name, gpg_homedir, target_dir):
+def sign(ctx, job_name, gpg_homedir, target_dir, algorithm):
     """Download and sign build artifacts from github releases"""
-    import gnupg
     gpg = gnupg.GPG(gnupghome=gpg_homedir)
 
     # fetch the queue repository
@@ -546,37 +578,67 @@ def sign(ctx, job_name, gpg_homedir, target_dir):
     target_dir.mkdir(parents=True, exist_ok=True)
     click.echo('Download {}\'s artifacts to {}'.format(job_name, target_dir))
 
-    tpl = '{:<10} {:>68}'
-    for task_name, task in sorted(job.tasks.items()):
+    tpl = '{:<10} {:>73}'
+
+    task_items = sorted(job.tasks.items())
+    ntasks = len(task_items)
+
+    for i, (task_name, task) in enumerate(task_items, start=1):
         assets = queue.github_assets(task)
         artifact_dir = target_dir / task_name
         artifact_dir.mkdir(exist_ok=True)
 
-        click.echo('\nDownloading and signing assets for task {}'
-                    .format(task_name))
-        click.echo('-' * 79)
+        basemsg = 'Downloading and signing assets for task {}'.format(
+            click.style(task_name, bold=True)
+        )
+        click.echo(
+            '\n{} {:>{size}}' .format(
+                basemsg,
+                click.style('{}/{}'.format(i, ntasks), bold=True),
+                size=89 - (len(basemsg) + 1) + 2 * len(
+                    click.style('', bold=True))
+            )
+        )
+        click.echo('-' * 89)
 
         for artifact in task.artifacts:
             try:
                 asset = assets[artifact]
             except KeyError:
-                msg = click.style('[{:>8}]'.format('MISSING'),
+                msg = click.style('[{:>13}]'.format('MISSING'),
                                   fg=COLORS['missing'])
                 click.echo(tpl.format(msg, artifact))
-                continue
-
-            # download artifact
-            artifact_path = artifact_dir / asset.name
-            asset.download(artifact_path)
-
-            # sign the artifact
-            with artifact_path.open('rb') as fp:
-                signature_path = Path(str(artifact_path) + '.sig')
-                gpg.sign_file(fp, detach=True, clearsign=False,
-                              output=str(signature_path))
-
-            msg = click.style('[{:>8}]'.format('SIGNED'), fg=COLORS['ok'])
-            click.echo(tpl.format(msg, asset.name))
+            else:
+                click.echo(click.style(artifact, bold=True))
+
+                # download artifact
+                artifact_path = artifact_dir / asset.name
+                asset.download(artifact_path)
+
+                # sign the artifact
+                signature_path = Path(str(artifact_path) + '.asc')
+                with artifact_path.open('rb') as fp:
+                    gpg.sign_file(fp, detach=True, clearsign=False,
+                                  binary=False,
+                                  output=str(signature_path))
+
+                # compute checksums for the artifact
+                artifact_bytes = artifact_path.read_bytes()
+                for algo in algorithm:
+                    suffix = '.{}'.format(algo)
+                    checksum_path = Path(str(artifact_path) + suffix)
+                    checksum = '{}  {}'.format(
+                        hashbytes(artifact_bytes, algo), artifact_path.name
+                    )
+                    checksum_path.write_text(checksum)
+                    msg = click.style(
+                        '[{:>13}]'.format('{} HASHED'.format(algo)),
+                        fg='blue'
+                    )
+                    click.echo(tpl.format(msg, checksum_path.name))
+
+                msg = click.style('[{:>13}]'.format('SIGNED'), fg=COLORS['ok'])
+                click.echo(tpl.format(msg, str(signature_path.name)))
 
 
 if __name__ == '__main__':