@@ -7,11 +7,25 @@ import * as fs from '../../util/fs';
7
7
import { HtkConfig } from '../../config' ;
8
8
import { logError } from '../../error-tracking' ;
9
9
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
+ > {
11
17
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
+
15
29
const release = await response . json ( ) ;
16
30
const apkAsset = release . assets . filter ( ( a : any ) => a . name === "httptoolkit.apk" ) [ 0 ] ;
17
31
const releaseName = release . name || release . tag_name ;
@@ -119,8 +133,18 @@ export async function streamLatestApk(config: HtkConfig): Promise<stream.Readabl
119
133
if ( ! latestApkRelease ) {
120
134
throw new Error ( "Couldn't find an Android APK locally or remotely" ) ;
121
135
} else {
136
+ // No APK locally, but we can get one remotely:
137
+
122
138
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 ;
124
148
125
149
// We buffer output into two passthrough streams, so both file & install
126
150
// 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
130
154
const apkOutputStream = new stream . PassThrough ( { highWaterMark : 10485760 } ) ;
131
155
apkStream . pipe ( apkOutputStream ) ;
132
156
133
- updateLocalApk ( latestApkRelease . version , apkFileStream , config ) . catch ( logError ) ;
157
+ if ( latestApkRelease !== 'unknown-latest' ) {
158
+ updateLocalApk ( latestApkRelease . version , apkFileStream , config ) . catch ( logError ) ;
159
+ }
160
+
134
161
return apkOutputStream ;
135
162
}
136
163
}
137
164
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
+ ) {
139
172
console . log ( 'Streaming local APK' ) ;
140
173
// If we have an APK locally and it's up to date, or we can't tell, just use it
141
174
return fs . createReadStream ( localApk . path ) ;
0 commit comments