@@ -7,11 +7,25 @@ import * as fs from '../../util/fs';
77import { HtkConfig } from '../../config' ;
88import { logError } from '../../error-tracking' ;
99
10- async function getLatestRelease ( ) : Promise < { version : string , url : string } | undefined > {
10+ const APK_REPO_RELEASE = 'httptoolkit/httptoolkit-android/releases/latest' ;
11+
12+ async function getLatestRelease ( ) : Promise <
13+ | { version : string , url : string }
14+ | 'unknown-latest'
15+ | undefined
16+ > {
1117 try {
12- const response = await fetch (
13- "https://api.github.com/repos/httptoolkit/httptoolkit-android/releases/latest"
14- ) ;
18+ const response = await fetch ( `https://api.github.com/repos/${ APK_REPO_RELEASE } ` ) ;
19+
20+ if ( response . status === 403 && response . headers . get ( 'x-ratelimit-remaining' ) === '0' ) {
21+ // Likely connectable but we can't check the version (API rate limit). Used only if
22+ // we don't have any other APK available.
23+ console . log ( "Can't check latest APK version due to GitHub rate limit" ) ;
24+ return 'unknown-latest' ;
25+ } else if ( ! response . ok ) {
26+ throw new Error ( `Checking latest Android app release failed with ${ response . status } ` ) ;
27+ }
28+
1529 const release = await response . json ( ) ;
1630 const apkAsset = release . assets . filter ( ( a : any ) => a . name === "httptoolkit.apk" ) [ 0 ] ;
1731 const releaseName = release . name || release . tag_name ;
@@ -119,8 +133,18 @@ export async function streamLatestApk(config: HtkConfig): Promise<stream.Readabl
119133 if ( ! latestApkRelease ) {
120134 throw new Error ( "Couldn't find an Android APK locally or remotely" ) ;
121135 } else {
136+ // No APK locally, but we can get one remotely:
137+
122138 console . log ( 'Streaming remote APK directly' ) ;
123- const apkStream = ( await fetch ( latestApkRelease . url ) ) . body ;
139+ const apkUrl = latestApkRelease === 'unknown-latest'
140+ ? `https://github.com/${ APK_REPO_RELEASE } /download/httptoolkit.apk`
141+ : latestApkRelease . url ;
142+
143+ const apkResponse = await fetch ( apkUrl ) ;
144+ if ( ! apkResponse . ok ) {
145+ throw new Error ( `APK download failed with ${ apkResponse . status } ` ) ;
146+ }
147+ const apkStream = apkResponse . body ;
124148
125149 // We buffer output into two passthrough streams, so both file & install
126150 // stream usage can be set up async independently. Buffers are 10MB, to
@@ -130,12 +154,21 @@ export async function streamLatestApk(config: HtkConfig): Promise<stream.Readabl
130154 const apkOutputStream = new stream . PassThrough ( { highWaterMark : 10485760 } ) ;
131155 apkStream . pipe ( apkOutputStream ) ;
132156
133- updateLocalApk ( latestApkRelease . version , apkFileStream , config ) . catch ( logError ) ;
157+ if ( latestApkRelease !== 'unknown-latest' ) {
158+ updateLocalApk ( latestApkRelease . version , apkFileStream , config ) . catch ( logError ) ;
159+ }
160+
134161 return apkOutputStream ;
135162 }
136163 }
137164
138- if ( ! latestApkRelease || semver . gte ( localApk . version , latestApkRelease . version , true ) ) {
165+ // So, we now have a local APK. Do we want to pull the remote one anyway?
166+
167+ if (
168+ ! latestApkRelease || // Can't get releases at all
169+ latestApkRelease === 'unknown-latest' || // Can get, but don't know the version
170+ semver . gte ( localApk . version , latestApkRelease . version , true ) // Already up to date
171+ ) {
139172 console . log ( 'Streaming local APK' ) ;
140173 // If we have an APK locally and it's up to date, or we can't tell, just use it
141174 return fs . createReadStream ( localApk . path ) ;
0 commit comments