mirror of
https://github.com/google/pebble.git
synced 2025-03-15 00:31:21 +00:00
253 lines
9.6 KiB
Python
253 lines
9.6 KiB
Python
#!/usr/bin/env python
|
|
# Copyright 2024 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import argparse
|
|
import boto
|
|
import boto.s3
|
|
import boto.s3.key
|
|
import hashlib
|
|
import json
|
|
import os.path
|
|
from sh import git, head
|
|
import sys
|
|
import zipfile
|
|
|
|
class PebbleBundle(object):
|
|
MANIFEST_FILENAME = 'manifest.json'
|
|
|
|
def __init__(self, bundle_path):
|
|
bundle_abs_path = os.path.abspath(bundle_path)
|
|
if not os.path.exists(bundle_abs_path):
|
|
raise "Bundle does not exist: " + bundle_path
|
|
|
|
self.zip = zipfile.ZipFile(bundle_abs_path)
|
|
self.path = bundle_abs_path
|
|
self.manifest = None
|
|
|
|
def get_manifest(self):
|
|
if (self.manifest):
|
|
return self.manifest
|
|
|
|
if self.MANIFEST_FILENAME not in self.zip.namelist():
|
|
raise "Could not find {}; are you sure this is a PebbleBundle?".format(self.MANIFEST_FILENAME)
|
|
|
|
self.manifest = json.loads(self.zip.read(self.MANIFEST_FILENAME))
|
|
return self.manifest
|
|
|
|
def is_firmware_bundle(self):
|
|
return 'firmware' in self.get_manifest()
|
|
|
|
def get_firmware_info(self):
|
|
if not self.is_firmware_bundle():
|
|
return None
|
|
|
|
return self.get_manifest()['firmware']
|
|
|
|
|
|
S3_PUBLIC_URL = 'https://pebblefw.s3.amazonaws.com/'
|
|
S3_BUCKET = 'pebblefw'
|
|
LATEST_MANIFEST = 'latest.json'
|
|
BUNDLE_DIRECTORY = 'pbz'
|
|
LAYOUTS_DIRECTORY = 'layouts'
|
|
|
|
RECOVERY_MANIFESTS = {
|
|
"bigboard" : {
|
|
"friendlyVersion" : "v1.4",
|
|
"timestamp" : 1354827516,
|
|
"sha-256" : "61f59c29b7e1c72c2e4de6848affb56ddd1f4c79512bfb9325aa99a650105d19",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/bigboard/release/pbz/recovery_bigboard_v1.4.pbz",
|
|
"notes" : "Bigboard Recovery Firmware"
|
|
},
|
|
"ev2_4" : {
|
|
"friendlyVersion" : "v1.5.2",
|
|
"timestamp" : 1356919500,
|
|
"sha-256" : "1ca285d65d80b48b90bab85c5f9e54c907414adffa6f1168beec8aac5d6f32a2",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/ev2_4/release/pbz/recovery_ev2_4_v1.5.2.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"bb2" : {
|
|
"friendlyVersion" : "v1.5.5",
|
|
"timestamp" : 1377981535,
|
|
"sha-256" : "620cfcadc8d28240048ffa01eb6984b06774a584349f178564e1548ecc813903",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/bb2/release/pbz/recovery_bb2_v1.5.5.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"v1_5" : {
|
|
"friendlyVersion" : "v1.5.5",
|
|
"timestamp" : 1377981535,
|
|
"sha-256" : "d952ffc4b46d1db965ac8ff0cffca645c1f9c9a8f296cf5da452f698b5bc15ce",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/v1_5/release/pbz/recovery_v1_5_v1.5.5.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"v2_0" : {
|
|
"friendlyVersion" : "v1.5.5",
|
|
"timestamp" : 1377981535,
|
|
"sha-256" : "07fd7c6ef5a1fbf51b8122bca3e72772015b5f210e905f30e853c65ae02ee09b",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/v2_0/release/pbz/recovery_v2_0_v1.5.5.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"snowy_evt2" : {
|
|
"friendlyVersion" : "v3.0.2-prf",
|
|
"timestamp" : 1430231686,
|
|
"sha-256" : "5579978e73db5d57831d4e1b8e3416b66fe94f5f19d3eb3ac49fcd54270935f3",
|
|
"url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_evt2/release-v3/pbz/recovery_snowy_evt2_v3.0.2-prf.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"snowy_dvt" : {
|
|
"friendlyVersion" : "v3.0.2-prf",
|
|
"timestamp" : 1430231686,
|
|
"sha-256" : "3338d94af5b97e94fe0e0dee5eddae7ea9c8f299b64070d1d1a692948c651a02",
|
|
"url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_dvt/release-v3/pbz/recovery_snowy_dvt_v3.0.2-prf.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"snowy_s3" : {
|
|
"friendlyVersion" : "v3.0.3-prf",
|
|
"timestamp" : 1435167301,
|
|
"sha-256" : "0e5bd3d327c5180506b20ef33aed1601037df0030683b27d68c16e4836c4def0",
|
|
"url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_s3/release-v3/pbz/recovery_snowy_s3_v3.0.3-prf.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"spalding" : {
|
|
"friendlyVersion" : "v3.2-prf5",
|
|
"timestamp" : 1440968436,
|
|
"sha-256" : "f6d3616b423d32618a1a3615ae8f8d5526941c7b6311be45e4387045c7eb7602",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/spalding/release-v3/pbz/recovery_s4_pvt_prf5.pbz",
|
|
"notes" : "Official Recovery Firmware"
|
|
},
|
|
"spalding_evt" : {
|
|
"friendlyVersion" : "v3.2-prf5",
|
|
"sha-256": "f9c2bb857952c034c7122b67f5178a25fcdf9ef8cce3d3bd8c2c8671676958c2",
|
|
"url" : "https://pebblefw.s3.amazonaws.com/pebble/spalding/release-v3/pbz/recovery_s4_evt_prf5.pbz",
|
|
"timestamp": 1440968436,
|
|
"notes" : "Official Recovery Firmware"
|
|
}
|
|
}
|
|
|
|
def build_s3_path(*args):
|
|
return 'pebble/' + '/'.join(args)
|
|
|
|
def __git_tag():
|
|
return str(git.describe()).strip()
|
|
|
|
def generate_update_manifest(bundle, bundle_s3_key, layouts_s3_key, notes):
|
|
bundle_path = os.path.abspath(bundle.path)
|
|
board = bundle.get_firmware_info()['hwrev']
|
|
|
|
timestamp = bundle.get_firmware_info()['timestamp']
|
|
|
|
with open(bundle_path, 'rb') as normal_bundle:
|
|
bundle_data = normal_bundle.read()
|
|
sha256 = hashlib.sha256(bundle_data).hexdigest()
|
|
|
|
bundle_url = S3_PUBLIC_URL + bundle_s3_key
|
|
layouts_url = S3_PUBLIC_URL + layouts_s3_key
|
|
|
|
manifest = {
|
|
'normal' : {
|
|
'friendlyVersion' : __git_tag(),
|
|
'timestamp' : timestamp,
|
|
'sha-256' : sha256,
|
|
'url' : bundle_url,
|
|
'layouts' : layouts_url,
|
|
'notes' : notes
|
|
},
|
|
'recovery' : RECOVERY_MANIFESTS[board]
|
|
}
|
|
|
|
return json.dumps(manifest)
|
|
|
|
def push_to_s3(bundle_path, layouts_path, stage, notes, update_latest=False, dry_run=False):
|
|
conn = boto.connect_s3()
|
|
|
|
bundle = PebbleBundle(bundle_path)
|
|
bundle_path = bundle.path
|
|
bundle_filename = os.path.basename(bundle_path)
|
|
|
|
git_tag = str(git.describe()).strip()
|
|
board = bundle.get_firmware_info()['hwrev']
|
|
bundle_key = build_s3_path(board, stage, BUNDLE_DIRECTORY, bundle_filename)
|
|
layouts_key = build_s3_path(board, stage, LAYOUTS_DIRECTORY, __git_tag() + '_layouts.json')
|
|
manifest_key = build_s3_path(board, stage, __git_tag() + '.json')
|
|
with open(notes, 'r') as f:
|
|
notes_txt = f.read().strip()
|
|
|
|
manifest_contents = generate_update_manifest(bundle, bundle_key, layouts_key, notes_txt)
|
|
|
|
if dry_run:
|
|
print manifest_contents
|
|
return
|
|
|
|
fw_bucket = boto.s3.bucket.Bucket(conn, S3_BUCKET)
|
|
|
|
# Upload the bundle to S3
|
|
bundle_obj = boto.s3.key.Key(fw_bucket, bundle_key)
|
|
bundle_obj.set_contents_from_filename(bundle_path)
|
|
bundle_obj.set_acl('public-read')
|
|
print 'PUT ' + bundle_key
|
|
|
|
bundle_obj = boto.s3.key.Key(fw_bucket, layouts_key)
|
|
bundle_obj.set_contents_from_filename(layouts_path)
|
|
bundle_obj.set_acl('public-read')
|
|
print 'PUT ' + layouts_key
|
|
|
|
manifest_obj = boto.s3.key.Key(fw_bucket, manifest_key)
|
|
manifest_obj.set_contents_from_string(manifest_contents)
|
|
manifest_obj.set_acl('public-read')
|
|
print 'PUT ' + manifest_key
|
|
|
|
if update_latest:
|
|
latest_key = build_s3_path(board, stage, LATEST_MANIFEST)
|
|
latest_obj = boto.s3.key.Key(fw_bucket, latest_key)
|
|
# prevent caching of the latest file
|
|
headers = {'Cache-Control': 'max-age=0'}
|
|
latest_obj.set_contents_from_string(manifest_contents, headers=headers)
|
|
latest_obj.set_acl('public-read')
|
|
print 'PUT ' + latest_key
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--notes', metavar='NOTES', required=True,
|
|
help='path to very brief release notes')
|
|
parser.add_argument('--bundle', metavar='BUNDLE_PATH', required=True,
|
|
help='path to the bundle')
|
|
parser.add_argument('--layouts', metavar='LAYOUTS_PATH', required=True,
|
|
help='path to the layouts json')
|
|
stage_choices = [
|
|
'release', 'test', 'release-v2',
|
|
'release-v3', 'beta', 'nightly',
|
|
'porksmoothie', 'garlicsoda', 'anchovietea'
|
|
]
|
|
parser.add_argument('--stage', metavar='STAGE', choices=stage_choices,
|
|
required=True, help='the target release stage')
|
|
parser.add_argument("--latest", action="store_true",
|
|
help="update the release stage's 'latest.json' manifest")
|
|
parser.add_argument("-d", "--dry_run", action="store_true", help="don't modify s3")
|
|
|
|
args = parser.parse_args()
|
|
|
|
print args
|
|
|
|
print 'HI'
|
|
push_to_s3(bundle_path=args.bundle,
|
|
layouts_path=args.layouts,
|
|
stage=args.stage,
|
|
notes=args.notes,
|
|
update_latest=args.latest,
|
|
dry_run=args.dry_run)
|
|
print 'BYE'
|
|
|
|
if (__name__ == '__main__'):
|
|
main()
|