/ test / get_previous_releases.py
get_previous_releases.py
  1  #!/usr/bin/env python3
  2  #
  3  # Copyright (c) 2018-present The Bitcoin Core developers
  4  # Distributed under the MIT software license, see the accompanying
  5  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  6  
  7  import argparse
  8  import contextlib
  9  from fnmatch import fnmatch
 10  import hashlib
 11  import os
 12  from pathlib import Path
 13  import platform
 14  import re
 15  import shutil
 16  import subprocess
 17  import sys
 18  import time
 19  import zipfile
 20  
 21  sys.path.append(str(Path(__file__).resolve().parent))
 22  from download_utils import download_from_url
 23  
 24  TAR = os.getenv('TAR', 'tar')
 25  
 26  SHA256_SUMS = {
 27      "0e2819135366f150d9906e294b61dff58fd1996ebd26c2f8e979d6c0b7a79580": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-aarch64-linux-gnu.tar.gz"},
 28      "d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz"},
 29      "1b0a7408c050e3d09a8be8e21e183ef7ee570385dc41216698cc3ab392a484e7": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-osx64.tar.gz"},
 30      "706e0472dbc933ed2757650d54cbcd780fd3829ebf8f609b32780c7eedebdbc9": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-x86_64-linux-gnu.tar.gz"},
 31      "5ea84b6a4c1b03d5f4d1a718fbed215e2fd0e66ca7d59ca6141218d163ce1301": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-win64.zip"},
 32  
 33      "60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-aarch64-linux-gnu.tar.gz"},
 34      "55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz"},
 35      "b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-osx64.tar.gz"},
 36      "fa71cb52ee5e0459cbf5248cdec72df27995840c796f58b304607a1ed4c165af": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-riscv64-linux-gnu.tar.gz"},
 37      "376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-x86_64-linux-gnu.tar.gz"},
 38      "e59fba67afce011d32b5d723a3a0be12da1b8a34f5d7966e504520c48d64716d": {"tag": "v0.20.1", "archive": "bitcoin-0.20.1-win64.zip"},
 39  
 40      "43416854330914992bbba2d0e9adf2a6fff4130be9af8ae2ef1186e743d9a3fe": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-aarch64-linux-gnu.tar.gz"},
 41      "f028af308eda45a3c4c90f9332f96b075bf21e3495c945ebce48597151808176": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-arm-linux-gnueabihf.tar.gz"},
 42      "695fb624fa6423f5da4f443b60763dd1d77488bfe5ef63760904a7b54e91298d": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-osx64.tar.gz"},
 43      "f8b2adfeae021a672effbc7bd40d5c48d6b94e53b2dd660f787340bf1a52e4e9": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-riscv64-linux-gnu.tar.gz"},
 44      "da7766775e3f9c98d7a9145429f2be8297c2672fe5b118fd3dc2411fb48e0032": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-x86_64-linux-gnu.tar.gz"},
 45      "1d0052c4ce80227fb6d0bc1c4e673ba21033e219c1f935d25f130ef7f43360d4": {"tag": "v0.21.0", "archive": "bitcoin-0.21.0-win64.zip"},
 46  
 47      "ac718fed08570a81b3587587872ad85a25173afa5f9fbbd0c03ba4d1714cfa3e": {"tag": "v22.0", "archive": "bitcoin-22.0-aarch64-linux-gnu.tar.gz"},
 48      "b8713c6c5f03f5258b54e9f436e2ed6d85449aa24c2c9972f91963d413e86311": {"tag": "v22.0", "archive": "bitcoin-22.0-arm-linux-gnueabihf.tar.gz"},
 49      "2744d199c3343b2d94faffdfb2c94d75a630ba27301a70e47b0ad30a7e0155e9": {"tag": "v22.0", "archive": "bitcoin-22.0-osx64.tar.gz"},
 50      "2cca5f99007d060aca9d8c7cbd035dfe2f040dd8200b210ce32cdf858479f70d": {"tag": "v22.0", "archive": "bitcoin-22.0-powerpc64-linux-gnu.tar.gz"},
 51      "91b1e012975c5a363b5b5fcc81b5b7495e86ff703ec8262d4b9afcfec633c30d": {"tag": "v22.0", "archive": "bitcoin-22.0-powerpc64le-linux-gnu.tar.gz"},
 52      "9cc3a62c469fe57e11485fdd32c916f10ce7a2899299855a2e479256ff49ff3c": {"tag": "v22.0", "archive": "bitcoin-22.0-riscv64-linux-gnu.tar.gz"},
 53      "59ebd25dd82a51638b7a6bb914586201e67db67b919b2a1ff08925a7936d1b16": {"tag": "v22.0", "archive": "bitcoin-22.0-x86_64-linux-gnu.tar.gz"},
 54      "9485e4b52ed6cebfe474ab4d7d0c1be6d0bb879ba7246a8239326b2230a77eb1": {"tag": "v22.0", "archive": "bitcoin-22.0-win64.zip"},
 55  
 56      "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb": {"tag": "v23.0", "archive": "bitcoin-23.0-aarch64-linux-gnu.tar.gz"},
 57      "952c574366aff76f6d6ad1c9ee45a361d64fa04155e973e926dfe7e26f9703a3": {"tag": "v23.0", "archive": "bitcoin-23.0-arm-linux-gnueabihf.tar.gz"},
 58      "7c8bc63731aa872b7b334a8a7d96e33536ad77d49029bad179b09dca32cd77ac": {"tag": "v23.0", "archive": "bitcoin-23.0-arm64-apple-darwin.tar.gz"},
 59      "2caa5898399e415f61d9af80a366a3008e5856efa15aaff74b88acf429674c99": {"tag": "v23.0", "archive": "bitcoin-23.0-powerpc64-linux-gnu.tar.gz"},
 60      "217dd0469d0f4962d22818c368358575f6a0abcba8804807bb75325eb2f28b19": {"tag": "v23.0", "archive": "bitcoin-23.0-powerpc64le-linux-gnu.tar.gz"},
 61      "078f96b1e92895009c798ab827fb3fde5f6719eee886bd0c0e93acab18ea4865": {"tag": "v23.0", "archive": "bitcoin-23.0-riscv64-linux-gnu.tar.gz"},
 62      "c816780583009a9dad426dc0c183c89be9da98906e1e2c7ebae91041c1aaaaf3": {"tag": "v23.0", "archive": "bitcoin-23.0-x86_64-apple-darwin.tar.gz"},
 63      "2cca490c1f2842884a3c5b0606f179f9f937177da4eadd628e3f7fd7e25d26d0": {"tag": "v23.0", "archive": "bitcoin-23.0-x86_64-linux-gnu.tar.gz"},
 64      "004b2e25b21e0f14cbcce6acec37f221447abbb3ea7931c689e508054bfc6cf6": {"tag": "v23.0", "archive": "bitcoin-23.0-win64.zip"},
 65  
 66      "0b48b9e69b30037b41a1e6b78fb7cbcc48c7ad627908c99686e81f3802454609": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-aarch64-linux-gnu.tar.gz"},
 67      "37d7660f0277301744e96426bbb001d2206b8d4505385dfdeedf50c09aaaef60": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-arm-linux-gnueabihf.tar.gz"},
 68      "90ed59e86bfda1256f4b4cad8cc1dd77ee0efec2492bcb5af61402709288b62c": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-arm64-apple-darwin.tar.gz"},
 69      "7590645e8676f8b5fda62dc20174474c4ac8fd0defc83a19ed908ebf2e94dc11": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-powerpc64-linux-gnu.tar.gz"},
 70      "79e89a101f23ff87816675b98769cd1ee91059f95c5277f38f48f21a9f7f8509": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-powerpc64le-linux-gnu.tar.gz"},
 71      "6b163cef7de4beb07b8cb3347095e0d76a584019b1891135cd1268a1f05b9d88": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-riscv64-linux-gnu.tar.gz"},
 72      "e2f751512f3c0f00eb68ba946d9c829e6cf99422a61e8f5e0a7c109c318674d0": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-x86_64-apple-darwin.tar.gz"},
 73      "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-x86_64-linux-gnu.tar.gz"},
 74      "8784ce304f22c495392d3adfd7fc2c645d093db9bd4d42666c41adf540539fff": {"tag": "v24.0.1", "archive": "bitcoin-24.0.1-win64.zip"},
 75  
 76      "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b": {"tag": "v25.0", "archive": "bitcoin-25.0-aarch64-linux-gnu.tar.gz"},
 77      "e537c8630b05e63242d979c3004f851fd73c2a10b5b4fdbb161788427c7b3c0f": {"tag": "v25.0", "archive": "bitcoin-25.0-arm-linux-gnueabihf.tar.gz"},
 78      "3b35075d6c1209743611c705a13575be2668bc069bc6301ce78a2e1e53ebe7cc": {"tag": "v25.0", "archive": "bitcoin-25.0-arm64-apple-darwin.tar.gz"},
 79      "0c8e135a6fd297270d3b65196042d761453493a022b5ff7fb847fc911e938214": {"tag": "v25.0", "archive": "bitcoin-25.0-powerpc64-linux-gnu.tar.gz"},
 80      "fa8af160782f5adfcea570f72b947073c1663b3e9c3cd0f82b216b609fe47573": {"tag": "v25.0", "archive": "bitcoin-25.0-powerpc64le-linux-gnu.tar.gz"},
 81      "fe6e347a66043946920c72c9c4afca301968101e6b82fb90a63d7885ebcceb32": {"tag": "v25.0", "archive": "bitcoin-25.0-riscv64-linux-gnu.tar.gz"},
 82      "5708fc639cdfc27347cccfd50db9b73b53647b36fb5f3a4a93537cbe8828c27f": {"tag": "v25.0", "archive": "bitcoin-25.0-x86_64-apple-darwin.tar.gz"},
 83      "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec": {"tag": "v25.0", "archive": "bitcoin-25.0-x86_64-linux-gnu.tar.gz"},
 84      "7154b35ecc8247589070ae739b7c73c4dee4794bea49eb18dc66faed65b819e7": {"tag": "v25.0", "archive": "bitcoin-25.0-win64.zip"},
 85  
 86      "10581b6d4aeb091c08c9e69eb4e4ced000038c009286dc1edb8a876656ccf6df": {"tag": "v28.2", "archive": "bitcoin-28.2-aarch64-linux-gnu.tar.gz"},
 87      "c08135c249688438b1ed6f48a2f192ffaae7c7111ffee5cfad59fdae355bb47f": {"tag": "v28.2", "archive": "bitcoin-28.2-arm-linux-gnueabihf.tar.gz"},
 88      "c0270ed50effc174f7ff3332dba5183a8693999dac2ba78b37d8c8797b3ea2b2": {"tag": "v28.2", "archive": "bitcoin-28.2-arm64-apple-darwin.tar.gz"},
 89      "21c54bc7520e873c8b52c817af257a8a4aee15d81bb2492e5bd51af055ae4469": {"tag": "v28.2", "archive": "bitcoin-28.2-powerpc64-linux-gnu.tar.gz"},
 90      "8ad2bf5dd0a7fd04afa9cd2b1309621f8662f00c799f1971cd44ba9eb9d18ef1": {"tag": "v28.2", "archive": "bitcoin-28.2-riscv64-linux-gnu.tar.gz"},
 91      "866a4b703a2095301151c17dcc753e19e4dba61ec68d19709ec4f81ff4320103": {"tag": "v28.2", "archive": "bitcoin-28.2-x86_64-apple-darwin.tar.gz"},
 92      "98add5f220c01b387343b70edeb6273403fe081e22cd85fda132704cdcaa98aa": {"tag": "v28.2", "archive": "bitcoin-28.2-x86_64-linux-gnu.tar.gz"},
 93      "da0869639c323bbf6f264f1829083b9514e10179b90c34b09d8cbcab8a1897e3": {"tag": "v28.2", "archive": "bitcoin-28.2-win64.zip"},
 94  }
 95  
 96  
 97  @contextlib.contextmanager
 98  def pushd(new_dir) -> None:
 99      previous_dir = os.getcwd()
100      os.chdir(new_dir)
101      try:
102          yield
103      finally:
104          os.chdir(previous_dir)
105  
106  
107  def download_binary(tag, args) -> int:
108      if Path(tag).is_dir():
109          if not args.remove_dir:
110              print('Using cached {}'.format(tag))
111              return 0
112          shutil.rmtree(tag)
113  
114      bin_path = 'bin/bitcoin-core-{}'.format(tag[1:])
115  
116      match = re.compile('v(.*)(rc[0-9]+)$').search(tag)
117      if match:
118          bin_path = 'bin/bitcoin-core-{}/test.{}'.format(
119              match.group(1), match.group(2))
120  
121      host = args.host
122      if tag < "v23" and host in ["x86_64-apple-darwin", "arm64-apple-darwin"]:
123          host = "osx64"
124  
125      archive_format = 'tar.gz'
126      if host == 'win64':
127          archive_format = 'zip'
128  
129      archive = f'bitcoin-{tag[1:]}-{host}.{archive_format}'
130      archive_url = f'https://bitcoincore.org/{bin_path}/{archive}'
131  
132      try:
133          download_from_url(archive_url, archive)
134      except Exception as e:
135          print(f"\nDownload failed: {e}", file=sys.stderr)
136          print("Retrying download after failure ...", file=sys.stderr)
137          time.sleep(12)
138          try:
139              download_from_url(archive_url, archive)
140          except Exception as e2:
141              print(f"\nDownload failed a second time: {e2}", file=sys.stderr)
142              return 1
143  
144      hasher = hashlib.sha256()
145      with open(archive, "rb") as afile:
146          hasher.update(afile.read())
147      archiveHash = hasher.hexdigest()
148  
149      if archiveHash not in SHA256_SUMS or SHA256_SUMS[archiveHash]['archive'] != archive:
150          if archive in [v['archive'] for v in SHA256_SUMS.values()]:
151              print(f"Checksum {archiveHash} did not match", file=sys.stderr)
152          else:
153              print("Checksum for given version doesn't exist", file=sys.stderr)
154          return 1
155  
156      print("Checksum matched")
157      Path(tag).mkdir()
158  
159      # Extract archive
160      if host == 'win64':
161          try:
162              with zipfile.ZipFile(archive, 'r') as zip:
163                  zip.extractall(tag)
164              # Remove the top level directory to match tar's --strip-components=1
165              extracted_items = os.listdir(tag)
166              top_level_dir = os.path.join(tag, extracted_items[0])
167              # Move all files & subdirectories up one level
168              for item in os.listdir(top_level_dir):
169                  shutil.move(os.path.join(top_level_dir, item), tag)
170              # Remove the now-empty top-level directory
171              os.rmdir(top_level_dir)
172          except Exception as e:
173              print(f"Zip extraction failed: {e}", file=sys.stderr)
174              return 1
175      else:
176          ret = subprocess.run([TAR, '-zxf', archive, '-C', tag,
177                                '--strip-components=1',
178                                'bitcoin-{tag}'.format(tag=tag[1:])]).returncode
179          if ret != 0:
180              print(f"Failed to extract the {tag} tarball", file=sys.stderr)
181              return ret
182  
183      Path(archive).unlink()
184  
185      if tag >= "v23" and tag < "v28.2" and args.host == "arm64-apple-darwin":
186          # Starting with v23 there are arm64 binaries for ARM (e.g. M1, M2) mac.
187          # Until v28.2 they had to be signed to run.
188          binary_path = f'{os.getcwd()}/{tag}/bin/'
189  
190          for arm_binary in os.listdir(binary_path):
191              # Is it already signed?
192              ret = subprocess.run(
193                  ['codesign', '-v', binary_path + arm_binary],
194                  stderr=subprocess.DEVNULL,  # Suppress expected stderr output
195              ).returncode
196              if ret == 1:
197                  # Have to self-sign the binary
198                  ret = subprocess.run(
199                      ['codesign', '-s', '-', binary_path + arm_binary]
200                  ).returncode
201                  if ret != 0:
202                      print(f"Failed to self-sign {tag} {arm_binary} arm64 binary", file=sys.stderr)
203                      return 1
204  
205                  # Confirm success
206                  ret = subprocess.run(
207                      ['codesign', '-v', binary_path + arm_binary]
208                  ).returncode
209                  if ret != 0:
210                      print(f"Failed to verify the self-signed {tag} {arm_binary} arm64 binary", file=sys.stderr)
211                      return 1
212  
213      return 0
214  
215  
216  def set_host(args) -> int:
217      if platform.system().lower() == 'windows':
218          if platform.machine() != 'AMD64':
219              print('Only 64bit Windows supported', file=sys.stderr)
220              return 1
221          args.host = 'win64'
222          return 0
223      host = os.environ.get('HOST', subprocess.check_output(
224          './depends/config.guess').decode())
225      platforms = {
226          'aarch64-*-linux*': 'aarch64-linux-gnu',
227          'powerpc64le-*-linux-*': 'powerpc64le-linux-gnu',
228          'riscv64-*-linux*': 'riscv64-linux-gnu',
229          'x86_64-*-linux*': 'x86_64-linux-gnu',
230          'x86_64-apple-darwin*': 'x86_64-apple-darwin',
231          'aarch64-apple-darwin*': 'arm64-apple-darwin',
232      }
233      args.host = ''
234      for pattern, target in platforms.items():
235          if fnmatch(host, pattern):
236              args.host = target
237      if not args.host:
238          print('Not sure which binary to download for {}'.format(host), file=sys.stderr)
239          return 1
240      return 0
241  
242  
243  def main(args) -> int:
244      Path(args.target_dir).mkdir(exist_ok=True, parents=True)
245      print("Releases directory: {}".format(args.target_dir))
246      ret = set_host(args)
247      if ret:
248          return ret
249      with pushd(args.target_dir):
250          for tag in args.tags:
251              ret = download_binary(tag, args)
252              if ret:
253                  return ret
254      return 0
255  
256  
257  if __name__ == '__main__':
258      parser = argparse.ArgumentParser(
259          formatter_class=argparse.ArgumentDefaultsHelpFormatter,
260          epilog='''
261          HOST can be set to any of the `host-platform-triplet`s from
262          depends/README.md for which a release exists.
263          ''',
264      )
265      parser.add_argument('-r', '--remove-dir', action='store_true',
266                          help='remove existing directory.')
267      parser.add_argument('-t', '--target-dir', action='store',
268                          help='target directory.', default='releases')
269      all_tags = sorted([*set([v['tag'] for v in SHA256_SUMS.values()])])
270      parser.add_argument('tags', nargs='*', default=all_tags,
271                          help='release tags. e.g.: v0.18.1 v0.20.0rc2 '
272                          '(if not specified, the full list needed for '
273                          'backwards compatibility tests will be used)'
274                          )
275      args = parser.parse_args()
276      sys.exit(main(args))