Skip to content

Commit 35a877a

Browse files
Merge pull request #11 from SimformSolutionsPvtLtd/develop
Release v1.0.2
2 parents 5632e1f + 3c71c18 commit 35a877a

7 files changed

Lines changed: 128 additions & 60 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ npm install -g simform-pocket-cli
1111
## Distribution
1212

1313
```bash
14-
pocket distribute --applicationId "yourApplicationID" --buildPath "yourBuildPath" --appToken "YourAppToken" --baseUrl "base url of the server" --releaseNotes "formatted release notes"
14+
pocket distribute --applicationId "yourApplicationID" --buildPath "yourBuildPath" --appToken "YourAppToken" --releaseNotes "formatted release notes"
1515
```
1616

1717
## Example
1818

1919
```bash
20-
pocket distribute --applicationId "<yourApplicationId>" --buildPath "<yourPath>/Downloads/app-release.apk" --appToken "<yourAppToken>" --baseUrl "<yourBaseUrl>/functions/v1/" --releaseNotes "formatted release notes"
20+
pocket distribute --applicationId "<yourApplicationId>" --buildPath "<yourPath>/Downloads/app-release.apk" --appToken "<yourAppToken>" --releaseNotes "formatted release notes"
2121
```
2222

2323
## How to get Application ID and App Token

package.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simform-pocket-cli",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "Pocket Deploy command-line utility",
55
"homepage": "https://github.com/SimformSolutionsPvtLtd/pocket-cli/#readme",
66
"author": "Simform Solutions",
@@ -23,16 +23,10 @@
2323
"/dist"
2424
],
2525
"dependencies": {
26-
"@plist/parse": "1.1.0",
2726
"axios": "^1.7.9",
28-
"bplist-parser": "0.3.2",
2927
"build-info-parser": "1.0.0",
30-
"bytebuffer": "5.0.1",
31-
"cgbi-to-png": "1.0.7",
3228
"form-data": "^4.0.1",
33-
"isomorphic-unzip": "1.1.5",
3429
"jsonwebtoken": "9.0.2",
35-
"save": "^2.4.0",
3630
"yargs": "^17.7.2"
3731
},
3832
"devDependencies": {

src/ApiConst.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export default {
2-
deployAppToPocketDeploy: 'deployAppToPocketDeploy'
2+
deployAppToPocketDeploy: 'deployAppToPocketDeploy',
3+
buildsExist: 'buildsExist'
34
};

src/AppConst.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ export default {
33
appDefaultPackage: 'com.default',
44
appDefaultVersion: '1.0',
55
appDefaultVersionCode: 1,
6-
maximumFileSize: 100 // 100MB
6+
chunkSize: 100 // In Bytes
77
};

src/deployTheApp.ts

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ import FormData from 'form-data';
55
import ApiConst from './ApiConst';
66
import AppConst from './AppConst';
77
import { getApkName } from './CommonUtils';
8-
9-
const FileType = {
10-
Apk: 'apk',
11-
Ipa: 'ipa'
12-
};
13-
14-
const FileContentType = {
15-
Ipa: 'application/octet-stream',
16-
Apk: 'application/vnd.android.package-archive'
17-
};
8+
import jwt from 'jsonwebtoken';
9+
import {
10+
AppPlatform,
11+
DecodedToken,
12+
FileContentType,
13+
FileType
14+
} from './types/CommonTypes';
1815

1916
/**
2017
* Extract the build data from the file
@@ -83,7 +80,7 @@ const deployApp = async (
8380
applicationId: string,
8481
buildPath: string,
8582
appToken: any,
86-
baseUrl: string,
83+
baseUrl?: string,
8784
releaseNotes?: string
8885
) => {
8986
// Use applicationId and buildPath in your deployment logic
@@ -102,73 +99,127 @@ const deployApp = async (
10299
console.error('App token is missing');
103100
process.exit(1);
104101
}
105-
if (!baseUrl) {
106-
console.error('Base URL is missing');
102+
103+
const decodedToken = jwt.decode(appToken) as DecodedToken;
104+
105+
const decodedApplicationId = decodedToken ? decodedToken.app : '';
106+
107+
if (applicationId !== decodedApplicationId) {
108+
console.error('Application ID does not match the application token.');
107109
process.exit(1);
108110
}
109111

112+
const decodedBaseUrl = decodedToken?.baseUrl;
113+
const currentUrl = baseUrl || decodedBaseUrl;
114+
115+
if (!currentUrl) {
116+
console.error('Base URL is missing');
117+
process.exit(1);
118+
}
110119
const fileType = buildPath?.split('.')?.pop() ?? FileType.Apk;
111120

112121
const currentFileContentType =
113122
fileType === FileType.Ipa ? FileContentType.Ipa : FileContentType.Apk;
114123

115124
const fileBuffer = fs.readFileSync(buildPath);
116125

117-
const formData = new FormData();
126+
const chunkSize = AppConst.chunkSize * 1024 * 1024; // 100MB
127+
128+
const fileSize = fs.statSync(buildPath).size;
129+
130+
const totalChunks = Math.ceil(fileSize / chunkSize);
131+
132+
const fileStream = fs.createReadStream(buildPath, {
133+
highWaterMark: chunkSize
134+
});
135+
136+
let chunkNumber = 1;
118137

119138
const blobData = new Blob([fileBuffer], {
120139
type: currentFileContentType
121140
});
122141

123-
if (blobData?.size > AppConst.maximumFileSize * 1024 * 1024) {
124-
throw new Error(
125-
`File size exceeds the allowed limit of ${AppConst.maximumFileSize} MB.`
126-
);
127-
}
128-
129142
try {
130143
console.log('Processing build...');
131144
const buildInfo = await fileDataExtract(buildPath, fileType, blobData);
132145

133-
formData.append('appToken', appToken);
134-
formData.append('currentFileContentType', currentFileContentType);
135-
formData.append('baseUrl', baseUrl);
136-
formData.append('applicationId', applicationId);
137-
formData.append('buildInfo', JSON.stringify(buildInfo));
146+
// Check if the build already exists
147+
const buildExistResponse = await axios.post(
148+
`${currentUrl}/${ApiConst.buildsExist}`,
149+
{
150+
application_id: applicationId,
151+
version_code: buildInfo.versionCode?.toString(),
152+
version_name: buildInfo.versionName?.toString(),
153+
platform:
154+
buildInfo.fileType === FileType.Ipa
155+
? AppPlatform.IOS
156+
: AppPlatform.Android
157+
},
158+
{
159+
headers: {
160+
'Content-Type': 'application/json',
161+
Authorization: `Bearer ${appToken}`
162+
}
163+
}
164+
);
138165

139-
if (releaseNotes) {
140-
releaseNotes = `<pre>${releaseNotes}</pre>`;
141-
formData.append('releaseNotes', releaseNotes);
166+
if (buildExistResponse.status === 409) {
167+
return buildExistResponse;
142168
}
143169

144-
const fileStream = fs.createReadStream(buildPath);
170+
for await (const chunk of fileStream) {
171+
const formData = new FormData();
145172

146-
fileStream.on('error', err => {
147-
console.error('Error reading the file:', err);
148-
});
173+
formData.append('applicationId', applicationId);
174+
formData.append('buildInfo', JSON.stringify(buildInfo));
175+
formData.append('currentFileContentType', currentFileContentType);
176+
formData.append('baseUrl', currentUrl);
177+
formData.append('appToken', appToken);
149178

150-
formData.append('file', fileStream, {
151-
contentType: currentFileContentType,
152-
filename: `${AppConst.appDefaultName}.${fileType}`
153-
});
179+
if (releaseNotes) {
180+
formData.append('releaseNotes', `<pre>${releaseNotes}</pre>`);
181+
}
154182

155-
console.log('deployAppToPocketDeploy');
156-
const response = await axios.post(
157-
`${baseUrl}${ApiConst.deployAppToPocketDeploy}`,
158-
formData,
159-
{
160-
headers: {
161-
...formData.getHeaders(),
162-
Authorization: `Bearer ${appToken}`
183+
fileStream.on('error', err => {
184+
console.error('Error reading the file:', err);
185+
});
186+
// Add current chunk
187+
formData.append('file', chunk, {
188+
contentType: currentFileContentType,
189+
filename: `chunk-${chunkNumber}`
190+
});
191+
192+
// Add chunk metadata
193+
formData.append('chunkNumber', chunkNumber);
194+
formData.append('totalChunks', totalChunks);
195+
196+
const response = await axios.post(
197+
`${currentUrl}/${ApiConst.deployAppToPocketDeploy}`,
198+
formData,
199+
{
200+
headers: {
201+
...formData.getHeaders(),
202+
Authorization: `Bearer ${appToken}`
203+
}
163204
}
205+
);
206+
207+
if (response.status === 200) {
208+
const percentage = Math.floor((chunkNumber / totalChunks) * 100);
209+
console.log(`Upload Progress: ${percentage}%`);
210+
211+
// Increment the chunk number
212+
chunkNumber++;
213+
} else {
214+
throw new Error(`Failed to upload build`);
164215
}
165-
);
216+
}
166217

167218
console.log('Build Uploaded successfully');
168-
return response;
219+
return;
169220
} catch (error) {
170221
console.error(
171-
'Error during post-build process:',
222+
'Error during uploading the build:',
172223
error.response?.data ?? error
173224
);
174225
}

src/distribute.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ yargs(hideBin(process.argv))
2828
},
2929
baseUrl: {
3030
type: 'string',
31-
demandOption: true,
31+
demandOption: false,
3232
describe: 'The base URL of the server.'
3333
},
3434
releaseNotes: {
@@ -37,7 +37,7 @@ yargs(hideBin(process.argv))
3737
describe: 'Enter your Release Notes for this version.'
3838
}
3939
},
40-
async (argv) => {
40+
async argv => {
4141
await deployApp(
4242
argv.applicationId,
4343
argv.buildPath,

src/types/CommonTypes.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export interface DecodedToken {
2+
exp?: number; // Expiration time
3+
sub?: string;
4+
email?: string;
5+
app?: string;
6+
baseUrl?: string;
7+
}
8+
9+
export enum FileType {
10+
Apk = 'apk',
11+
Ipa = 'ipa'
12+
}
13+
14+
export enum AppPlatform {
15+
Android = 'android',
16+
IOS = 'ios'
17+
}
18+
19+
export enum FileContentType {
20+
Ipa = 'application/octet-stream',
21+
Apk = 'application/vnd.android.package-archive'
22+
}

0 commit comments

Comments
 (0)