Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: librem5-flash-im...@packages.debian.org
Control: affects -1 + src:librem5-flash-image

Please unblock package librem5-flash-image

The tool is used to flash images to Librem 5 phones.

[ Reason ]
This adds support for stable image downloads (rather than
always fetching the latest image to flash to the phone)
hence it seems appropriate to have that in a stable relase.
It also makes downloading a bit more robust by allowing for longer
timeouts.

[ Impact ]
Users will have to manually go out and search for stable images.

[ Tests ]
- Tested flashing manually
- CI tests (that download images) pass upstream in a Debian container

[ Risks ]
Risks should be low as the package is in use on other distros since some
time.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
I apologize for being late here, I simply missed that the version
is outdated. I could have backported the patch but just using the
upstream version (which didn't bring any other features) seemed more
reasonable here.

unblock librem5-flash-image/0.0.3-1
diff --git a/debian/changelog b/debian/changelog
index 2c0f47f..d94dbf9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+librem5-flash-image (0.0.3-1) unstable; urgency=medium
+
+  * New upstream release
+
+ -- Guido Günther <a...@sigxcpu.org>  Fri, 24 Feb 2023 17:53:10 +0100
+
 librem5-flash-image (0.0.2-1) unstable; urgency=medium
 
   * New upstream release 0.0.2
diff --git a/debian/gbp.conf b/debian/gbp.conf
index 3d0bb65..b2eeae8 100644
--- a/debian/gbp.conf
+++ b/debian/gbp.conf
@@ -1,4 +1,17 @@
 [DEFAULT]
-debian-branch=debian/master
+debian-branch = debian/master
+debian-tag = debian/%(version)s
+upstream-branch = upstream/latest
+upstream-tag = upstream/%(version)s
+upstream-vcs-tag = v%(version)s
 pristine-tar = True
-upstream-tag = v%(version)s
+
+[tag]
+sign-tags = true
+
+[dch]
+multimaint-merge = True
+
+[import-orig]
+postimport = dch -v%(version)s New upstream release; git add debian/changelog; debcommit
+upstream-vcs-tag = v%(version%~%_)s
diff --git a/scripts/librem5-flash-image b/scripts/librem5-flash-image
index be28869..3896d8c 100755
--- a/scripts/librem5-flash-image
+++ b/scripts/librem5-flash-image
@@ -35,6 +35,12 @@ except ImportError:
 
 from urllib.parse import urljoin
 
+IMAGES = {
+    'stable': {
+        'url': 'https://storage.puri.sm/librem5/images/',
+    }
+}
+
 JENKINS = 'https://arm01.puri.sm'
 BOARD_TYPE = 'librem5r4'
 VALID_PHONE_BOARD_TYPES = ['librem5r2', 'librem5r3', 'librem5r4']
@@ -130,7 +136,8 @@ def resuming_stream(url, expected_size, max_attempts):
         try:
             resp = requests.get(url,
                                 stream=True,
-                                headers={'Range': 'bytes={}-'.format(position)}
+                                headers={'Range': 'bytes={}-'.format(position)},
+                                timeout=10
                                 )
             resp.raise_for_status()
 
@@ -145,7 +152,9 @@ def resuming_stream(url, expected_size, max_attempts):
             if position < expected_size:
                 raise PrematureEndException()
             return
-        except (requests.exceptions.ConnectionError, PrematureEndException):
+        except (requests.exceptions.ConnectionError,
+                requests.exceptions.Timeout,
+                PrematureEndException):
             if i == max_attempts - 1:
                 logging.error("Max connection errors reached, aborting")
                 raise
@@ -207,7 +216,7 @@ def download_image(url, target, attempts):
     verify_image(target, meta)
 
 
-def find_image(jobname, type, variant, dist):
+def find_image_jenkins(jobname, type, variant, dist):
     server = jenkins.Jenkins(JENKINS)
     logging.info("Looking for {} {} {} image".format(type, variant, dist))
     try:
@@ -219,6 +228,8 @@ def find_image(jobname, type, variant, dist):
         resp = requests.get(build['url'] + '/api/json')
         resp.raise_for_status()
         json = resp.json()
+        if json['description'] is None:
+            continue
         if (json['description'].startswith(variant + ' ' + type) and
                 dist in json['description'] and
                 json['result'] == 'SUCCESS'):
@@ -229,6 +240,40 @@ def find_image(jobname, type, variant, dist):
     return found
 
 
+def find_image_stable(board, variant, dist):
+    remote = IMAGES['stable']
+    logging.info("Looking for {} {} {} image".format(board, variant, dist))
+    found = None
+
+    path = f"{dist}/latest/{board}/{variant}/"
+    url = urljoin(remote['url'], f"{path}/artifact/{IMAGE.format(board)}.xz")
+    try:
+        resp = requests.head(url, timeout=10)
+        if resp.ok:
+            d = datetime.datetime.strptime(resp.headers['Last-Modified'],
+                                           '%a, %d %b %Y %H:%M:%S %Z')
+            return {
+                'url': urljoin(remote['url'], path),
+                'timestamp': d.timestamp() * 1000,
+                'id': '"stable"',
+                'description': f"Last stable {board} build",
+            }
+        resp.raise_for_status()
+
+    except (requests.exceptions.ConnectionError,
+            requests.exceptions.Timeout):
+        logging.error("Failed to find image at {} - connection failed".format(url))
+        found = None
+    return found
+
+
+def find_image(jobname, board, variant, dist, stable):
+    if stable:
+        return find_image_stable(board, variant, dist)
+    else:
+        return find_image_jenkins(jobname, board, variant, dist)
+
+
 def find_uboot(jobname):
     server = jenkins.Jenkins(JENKINS)
 
@@ -378,6 +423,8 @@ def main():
     parser.add_argument('--board', choices=VALID_DEVKIT_BOARD_TYPES + VALID_PHONE_BOARD_TYPES, default=None,
                         help="Type of the board to download ( devkit, librem5r2, librem5r3, librem5r4 ) "
                         "default is 'auto'")
+    parser.add_argument('--stable', action='store_true', default=False,
+                        help="Whether to grab the latest stable image, otherwise the dev snapshot is fetched")
 
     group = parser.add_argument_group(title='Testing and debugging options')
     group.add_argument('--debug', action="store_true", default=False,
@@ -405,7 +452,7 @@ def main():
 
     # Check available downloads upfront so it's less likely we fail
     # later:
-    image_ref = find_image(args.image_job, board, args.variant, args.dist)
+    image_ref = find_image(args.image_job, board, args.variant, args.dist, args.stable)
     if image_ref:
         image_ref['ts'] = datetime.datetime.fromtimestamp(image_ref['timestamp'] / 1000).strftime('%c')
         logging.info("Found disk image Build {id} '{description}' from {ts}".format(**image_ref))
@@ -431,7 +478,8 @@ def main():
     if board in ["librem5r3", 'librem5r4']:
         uuu_mods = "CFG: SDP: -chip MX8MQ -compatible MX8MQ -vid 0x316d -pid 0x4c05"
 
-    outdir = args.dir if args.dir is not None else tempfile.mkdtemp(prefix='devkit_image_', dir='.')
+    dirprefix = 'tmp_' + os.path.basename(sys.argv[0]) + '_'
+    outdir = args.dir if args.dir is not None else tempfile.mkdtemp(prefix=dirprefix, dir='.')
     try:
         logging.info("Downloading to {}".format(outdir))
         if args.dir == outdir:

Reply via email to