sign-launcher.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. #!/usr/bin/env python3
  2. # Copyright (C) 2020 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Helper tool for signing repo launcher scripts correctly.
  16. This is intended to be run only by the official Repo release managers.
  17. """
  18. import argparse
  19. import os
  20. import subprocess
  21. import sys
  22. import util
  23. def sign(opts):
  24. """Sign the launcher!"""
  25. output = ''
  26. for key in opts.keys:
  27. # We use ! at the end of the key so that gpg uses this specific key.
  28. # Otherwise it uses the key as a lookup into the overall key and uses the
  29. # default signing key. i.e. It will see that KEYID_RSA is a subkey of
  30. # another key, and use the primary key to sign instead of the subkey.
  31. cmd = ['gpg', '--homedir', opts.gpgdir, '-u', f'{key}!', '--batch', '--yes',
  32. '--armor', '--detach-sign', '--output', '-', opts.launcher]
  33. ret = util.run(opts, cmd, encoding='utf-8', stdout=subprocess.PIPE)
  34. output += ret.stdout
  35. # Save the combined signatures into one file.
  36. with open(f'{opts.launcher}.asc', 'w', encoding='utf-8') as fp:
  37. fp.write(output)
  38. def check(opts):
  39. """Check the signature."""
  40. util.run(opts, ['gpg', '--verify', f'{opts.launcher}.asc'])
  41. def postmsg(opts):
  42. """Helpful info to show at the end for release manager."""
  43. print(f"""
  44. Repo launcher bucket:
  45. gs://git-repo-downloads/
  46. To upload this launcher directly:
  47. gsutil cp -a public-read {opts.launcher} {opts.launcher}.asc gs://git-repo-downloads/
  48. NB: You probably want to upload it with a specific version first, e.g.:
  49. gsutil cp -a public-read {opts.launcher} gs://git-repo-downloads/repo-3.0
  50. gsutil cp -a public-read {opts.launcher}.asc gs://git-repo-downloads/repo-3.0.asc
  51. """)
  52. def get_parser():
  53. """Get a CLI parser."""
  54. parser = argparse.ArgumentParser(description=__doc__)
  55. parser.add_argument('-n', '--dry-run',
  56. dest='dryrun', action='store_true',
  57. help='show everything that would be done')
  58. parser.add_argument('--gpgdir',
  59. default=os.path.join(util.HOMEDIR, '.gnupg', 'repo'),
  60. help='path to dedicated gpg dir with release keys '
  61. '(default: ~/.gnupg/repo/)')
  62. parser.add_argument('--keyid', dest='keys', default=[], action='append',
  63. help='alternative signing keys to use')
  64. parser.add_argument('launcher',
  65. default=os.path.join(util.TOPDIR, 'repo'), nargs='?',
  66. help='the launcher script to sign')
  67. return parser
  68. def main(argv):
  69. """The main func!"""
  70. parser = get_parser()
  71. opts = parser.parse_args(argv)
  72. if not os.path.exists(opts.gpgdir):
  73. parser.error(f'--gpgdir does not exist: {opts.gpgdir}')
  74. if not os.path.exists(opts.launcher):
  75. parser.error(f'launcher does not exist: {opts.launcher}')
  76. opts.launcher = os.path.relpath(opts.launcher)
  77. print(f'Signing "{opts.launcher}" launcher script and saving to '
  78. f'"{opts.launcher}.asc"')
  79. if opts.keys:
  80. print(f'Using custom keys to sign: {" ".join(opts.keys)}')
  81. else:
  82. print('Using official Repo release keys to sign')
  83. opts.keys = [util.KEYID_DSA, util.KEYID_RSA, util.KEYID_ECC]
  84. util.import_release_key(opts)
  85. sign(opts)
  86. check(opts)
  87. postmsg(opts)
  88. return 0
  89. if __name__ == '__main__':
  90. sys.exit(main(sys.argv[1:]))