@nordicsemiconductor/pc-nrfconnect-shared 197.0.0 → 198.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog.md +20 -0
- package/package.json +1 -1
- package/scripts/nordic-publish.js +17 -17
- package/scripts/nordic-publish.ts +291 -129
- package/scripts/prepare-shared-release.ts +2 -2
- package/src/About/DeviceCard.tsx +4 -8
- package/src/Device/deviceInfo/deviceInfo.ts +79 -60
- package/src/utils/systemReport.ts +2 -5
- package/typings/generated/src/About/DeviceCard.d.ts.map +1 -1
- package/typings/generated/src/Device/deviceInfo/deviceInfo.d.ts +2 -4
- package/typings/generated/src/Device/deviceInfo/deviceInfo.d.ts.map +1 -1
- package/typings/generated/src/utils/systemReport.d.ts.map +1 -1
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/* eslint-disable max-classes-per-file, class-methods-use-this */
|
|
10
|
+
|
|
9
11
|
import { execSync } from 'child_process';
|
|
10
|
-
import { program } from 'commander';
|
|
12
|
+
import { Option, program } from 'commander';
|
|
11
13
|
import fs from 'fs';
|
|
12
|
-
import
|
|
14
|
+
import LowlevelFtpClient from 'ftp';
|
|
13
15
|
import semver from 'semver';
|
|
14
16
|
import calculateShasum from 'shasum';
|
|
15
17
|
|
|
@@ -30,7 +32,218 @@ interface App {
|
|
|
30
32
|
packageJson: PackageJsonApp;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
let client: Client;
|
|
36
|
+
|
|
37
|
+
abstract class Client {
|
|
38
|
+
abstract sourceUrl: string;
|
|
39
|
+
|
|
40
|
+
initialise(options: Options): Promise<void> | void {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
41
|
+
end(): void {}
|
|
42
|
+
|
|
43
|
+
abstract download(filename: string): Promise<string>;
|
|
44
|
+
abstract uploadContent(
|
|
45
|
+
content: Buffer,
|
|
46
|
+
remoteFilename: string
|
|
47
|
+
): Promise<void>;
|
|
48
|
+
abstract uploadLocalFile(
|
|
49
|
+
localFilename: string,
|
|
50
|
+
remoteFilename: string
|
|
51
|
+
): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class FtpClient extends Client {
|
|
55
|
+
/*
|
|
56
|
+
* To use this script with an FTP server REPO_HOST, REPO_USER and REPO_PASS need to be set
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
ftpClient = new LowlevelFtpClient();
|
|
60
|
+
|
|
61
|
+
host = process.env.REPO_HOST || 'localhost';
|
|
62
|
+
port = Number(process.env.REPO_PORT) || 21;
|
|
63
|
+
user = process.env.REPO_USER || 'anonymous';
|
|
64
|
+
password = process.env.REPO_PASS || 'anonymous@';
|
|
65
|
+
|
|
66
|
+
sourceDir: string;
|
|
67
|
+
sourceUrl: string;
|
|
68
|
+
|
|
69
|
+
constructor(private readonly options: Options) {
|
|
70
|
+
super();
|
|
71
|
+
this.sourceDir = this.getSourceDir();
|
|
72
|
+
this.sourceUrl = this.getSourceUrl();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getSourceDir = () => {
|
|
76
|
+
const repoDirOfficial = '/.pc-tools/nrfconnect-apps';
|
|
77
|
+
|
|
78
|
+
if (process.env.REPO_DIR) return process.env.REPO_DIR;
|
|
79
|
+
if (this.options.deployOfficial) return repoDirOfficial;
|
|
80
|
+
|
|
81
|
+
return `${repoDirOfficial}/${this.options.source}`;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
getSourceUrl = () => {
|
|
85
|
+
if (process.env.REPO_URL) return process.env.REPO_URL;
|
|
86
|
+
return `https://developer.nordicsemi.com${this.sourceDir}`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
connect = () =>
|
|
90
|
+
new Promise<void>((resolve, reject) => {
|
|
91
|
+
console.log(
|
|
92
|
+
`Connecting to ftp://${this.user}@${this.host}:${this.port}`
|
|
93
|
+
);
|
|
94
|
+
this.ftpClient.once('error', err => {
|
|
95
|
+
this.ftpClient.removeAllListeners('ready');
|
|
96
|
+
reject(err);
|
|
97
|
+
});
|
|
98
|
+
this.ftpClient.once('ready', () => {
|
|
99
|
+
this.ftpClient.removeAllListeners('error');
|
|
100
|
+
resolve();
|
|
101
|
+
});
|
|
102
|
+
this.ftpClient.connect({
|
|
103
|
+
host: this.host,
|
|
104
|
+
port: this.port,
|
|
105
|
+
user: this.user,
|
|
106
|
+
password: this.password,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
createSourceDirectory = () =>
|
|
111
|
+
new Promise<void>((resolve, reject) => {
|
|
112
|
+
console.log(`Creating source directory ${this.sourceDir}`);
|
|
113
|
+
this.ftpClient.mkdir(this.sourceDir, true, err => {
|
|
114
|
+
if (err) {
|
|
115
|
+
reject(new Error(`Failed to create source directory.`));
|
|
116
|
+
} else {
|
|
117
|
+
resolve();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
changeWorkingDirectory = () =>
|
|
123
|
+
new Promise<void>((resolve, reject) => {
|
|
124
|
+
console.log(`Changing to directory ${this.sourceDir}`);
|
|
125
|
+
this.ftpClient.cwd(this.sourceDir, err => {
|
|
126
|
+
if (err) {
|
|
127
|
+
reject(
|
|
128
|
+
new Error(
|
|
129
|
+
'\nError: Failed to change to directory. ' +
|
|
130
|
+
'Check whether it exists on the FTP server.\n' +
|
|
131
|
+
'If you want to create a new source, use the ' +
|
|
132
|
+
'--create-source option.'
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
resolve();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
initialise = async () => {
|
|
142
|
+
await this.connect();
|
|
143
|
+
if (this.options.doCreateSource) {
|
|
144
|
+
await this.createSourceDirectory();
|
|
145
|
+
}
|
|
146
|
+
await this.changeWorkingDirectory();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
download = (filename: string) =>
|
|
150
|
+
new Promise<string>((resolve, reject) => {
|
|
151
|
+
console.log(`Downloading file ${filename}`);
|
|
152
|
+
let data = '';
|
|
153
|
+
this.ftpClient.get(filename, (err, stream) => {
|
|
154
|
+
if (err) return reject(err);
|
|
155
|
+
stream.once('close', () => resolve(data));
|
|
156
|
+
stream.on('data', chunk => {
|
|
157
|
+
data += chunk;
|
|
158
|
+
});
|
|
159
|
+
return undefined;
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
upload = (
|
|
164
|
+
contentOrLocalFilename: string | Buffer,
|
|
165
|
+
remoteFilename: string
|
|
166
|
+
) =>
|
|
167
|
+
new Promise<void>((resolve, reject) => {
|
|
168
|
+
console.log(`Uploading file ${remoteFilename}`);
|
|
169
|
+
this.ftpClient.put(contentOrLocalFilename, remoteFilename, err =>
|
|
170
|
+
err ? reject(err) : resolve()
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
uploadContent = this.upload;
|
|
175
|
+
uploadLocalFile = this.upload;
|
|
176
|
+
|
|
177
|
+
end = () => this.ftpClient.end();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
class ArtifactoryClient extends Client {
|
|
181
|
+
token = process.env.ARTIFACTORY_TOKEN;
|
|
182
|
+
|
|
183
|
+
sourceUrl: string;
|
|
184
|
+
|
|
185
|
+
constructor(private readonly options: Options) {
|
|
186
|
+
super();
|
|
187
|
+
|
|
188
|
+
if (this.token == null) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
'The environment variable ARTIFACTORY_TOKEN must be set.'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.sourceUrl = `https://files.nordicsemi.com/artifactory/swtools/${this.getAccessLevel()}/ncd/apps/${
|
|
195
|
+
options.source
|
|
196
|
+
}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getAccessLevel = () => {
|
|
200
|
+
if (this.options.accessLevel != null) return this.options.accessLevel;
|
|
201
|
+
|
|
202
|
+
return this.options.deployOfficial ? 'external' : 'internal';
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
download = async (filename: string) => {
|
|
206
|
+
console.log(`Downloading ${filename}`);
|
|
207
|
+
|
|
208
|
+
const url = `${this.sourceUrl}/${filename}`;
|
|
209
|
+
const res = await fetch(url, {
|
|
210
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (!res.ok) {
|
|
214
|
+
throw new Error(`Failed to download ${url}: ${res.statusText}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return res.text();
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
upload = async (content: Buffer, remoteFilename: string) => {
|
|
221
|
+
const url = `${this.sourceUrl}/${remoteFilename}`;
|
|
222
|
+
const res = await fetch(url, {
|
|
223
|
+
method: 'PUT',
|
|
224
|
+
body: content,
|
|
225
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (!res.ok) {
|
|
229
|
+
throw new Error(`Failed to upload ${url}: ${res.statusText}`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
uploadContent = (content: Buffer, remoteFilename: string) => {
|
|
234
|
+
console.log(`Uploading content for ${remoteFilename}`);
|
|
235
|
+
|
|
236
|
+
return this.upload(content, remoteFilename);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
uploadLocalFile = (localFilename: string, remoteFilename: string) => {
|
|
240
|
+
console.log(
|
|
241
|
+
`Uploading local file ${localFilename} as ${remoteFilename}`
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
return this.upload(fs.readFileSync(localFilename), remoteFilename);
|
|
245
|
+
};
|
|
246
|
+
}
|
|
34
247
|
|
|
35
248
|
const hasMessage = (error: unknown): error is { message: unknown } =>
|
|
36
249
|
error != null && typeof error === 'object' && 'message' in error;
|
|
@@ -38,40 +251,66 @@ const hasMessage = (error: unknown): error is { message: unknown } =>
|
|
|
38
251
|
const errorAsString = (error: unknown) =>
|
|
39
252
|
hasMessage(error) ? error.message : String(error);
|
|
40
253
|
|
|
41
|
-
const
|
|
42
|
-
|
|
254
|
+
const validAccessLevels = [
|
|
255
|
+
'external',
|
|
256
|
+
'external-confidential',
|
|
257
|
+
'internal',
|
|
258
|
+
'internal-confidential',
|
|
259
|
+
] as const;
|
|
43
260
|
|
|
44
|
-
|
|
45
|
-
if (deployOfficial) return repoDirOfficial;
|
|
261
|
+
type AccessLevel = (typeof validAccessLevels)[number];
|
|
46
262
|
|
|
47
|
-
|
|
48
|
-
|
|
263
|
+
const isAccessLevel = (value: string): value is AccessLevel =>
|
|
264
|
+
(validAccessLevels as readonly string[]).includes(value);
|
|
265
|
+
|
|
266
|
+
const splitSourceAndAccessLevel = (sourceAndMaybeAccessLevel: string) => {
|
|
267
|
+
const match = sourceAndMaybeAccessLevel.match(
|
|
268
|
+
/(?<source>.*?)\s*\((?<accessLevel>.*)\)/
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
if (match == null) {
|
|
272
|
+
return { source: sourceAndMaybeAccessLevel };
|
|
273
|
+
}
|
|
49
274
|
|
|
50
|
-
const
|
|
51
|
-
const repoUrlOfficial =
|
|
52
|
-
'https://developer.nordicsemi.com/.pc-tools/nrfconnect-apps';
|
|
275
|
+
const { source, accessLevel } = match.groups!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Can never be null because of the regex
|
|
53
276
|
|
|
54
|
-
if (
|
|
55
|
-
|
|
277
|
+
if (!isAccessLevel(accessLevel)) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`The specified access level "${accessLevel}" must be one of ${validAccessLevels.join(
|
|
280
|
+
', '
|
|
281
|
+
)}.`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
56
284
|
|
|
57
|
-
return
|
|
285
|
+
return { source, accessLevel };
|
|
58
286
|
};
|
|
59
287
|
|
|
60
288
|
interface Options {
|
|
61
289
|
doPack: boolean;
|
|
62
290
|
doCreateSource: boolean;
|
|
63
291
|
deployOfficial: boolean;
|
|
64
|
-
|
|
65
|
-
sourceUrl: string;
|
|
292
|
+
source: string;
|
|
66
293
|
sourceName?: string;
|
|
294
|
+
destination: 'ftp' | 'artifactory';
|
|
295
|
+
accessLevel?: AccessLevel;
|
|
67
296
|
}
|
|
68
297
|
|
|
69
298
|
const parseOptions = (): Options => {
|
|
70
299
|
program
|
|
71
|
-
.description('Publish
|
|
300
|
+
.description('Publish an nRF Connect for Desktop app')
|
|
72
301
|
.requiredOption(
|
|
73
302
|
'-s, --source <source>',
|
|
74
|
-
'Specify the source to publish (e.g. official).'
|
|
303
|
+
'Specify the source to publish (e.g. "official" or "releast-test"). ' +
|
|
304
|
+
'When publishing to Artifactory, an access level can be ' +
|
|
305
|
+
'specified at the end in parantheses (e.g. "official (external)").'
|
|
306
|
+
)
|
|
307
|
+
.addOption(
|
|
308
|
+
new Option(
|
|
309
|
+
'-d, --destination <ftp|artifactory>',
|
|
310
|
+
'Specify where to publish.'
|
|
311
|
+
)
|
|
312
|
+
.choices(['ftp', 'artifactory'])
|
|
313
|
+
.makeOptionMandatory()
|
|
75
314
|
)
|
|
76
315
|
.option(
|
|
77
316
|
'-n, --no-pack',
|
|
@@ -87,15 +326,18 @@ const parseOptions = (): Options => {
|
|
|
87
326
|
|
|
88
327
|
const options = program.opts();
|
|
89
328
|
|
|
90
|
-
const
|
|
329
|
+
const { source, accessLevel } = splitSourceAndAccessLevel(options.source);
|
|
330
|
+
|
|
331
|
+
const deployOfficial = source === 'official';
|
|
91
332
|
|
|
92
333
|
return {
|
|
93
334
|
doPack: options.pack,
|
|
94
335
|
doCreateSource: options.createSource != null,
|
|
336
|
+
source,
|
|
95
337
|
sourceName: options.createSource,
|
|
96
338
|
deployOfficial,
|
|
97
|
-
|
|
98
|
-
|
|
339
|
+
destination: options.destination,
|
|
340
|
+
accessLevel,
|
|
99
341
|
};
|
|
100
342
|
};
|
|
101
343
|
|
|
@@ -144,7 +386,7 @@ const packOrReadPackage = (options: Options): App => {
|
|
|
144
386
|
version,
|
|
145
387
|
filename,
|
|
146
388
|
shasum,
|
|
147
|
-
sourceUrl:
|
|
389
|
+
sourceUrl: client.sourceUrl,
|
|
148
390
|
isOfficial: options.deployOfficial,
|
|
149
391
|
appInfoName: `${name}.json`,
|
|
150
392
|
releaseNotesFilename: `${name}-Changelog.md`,
|
|
@@ -153,72 +395,6 @@ const packOrReadPackage = (options: Options): App => {
|
|
|
153
395
|
};
|
|
154
396
|
};
|
|
155
397
|
|
|
156
|
-
const connect = (config: {
|
|
157
|
-
host: string;
|
|
158
|
-
port: number;
|
|
159
|
-
user: string;
|
|
160
|
-
password: string;
|
|
161
|
-
}) =>
|
|
162
|
-
new Promise<void>((resolve, reject) => {
|
|
163
|
-
console.log(
|
|
164
|
-
`Connecting to ftp://${config.user}@${config.host}:${config.port}`
|
|
165
|
-
);
|
|
166
|
-
client.once('error', err => {
|
|
167
|
-
client.removeAllListeners('ready');
|
|
168
|
-
reject(err);
|
|
169
|
-
});
|
|
170
|
-
client.once('ready', () => {
|
|
171
|
-
client.removeAllListeners('error');
|
|
172
|
-
resolve();
|
|
173
|
-
});
|
|
174
|
-
client.connect(config);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
const createSourceDirectory = (dir: string) =>
|
|
178
|
-
new Promise<void>((resolve, reject) => {
|
|
179
|
-
console.log(`Creating source directory ${dir}`);
|
|
180
|
-
client.mkdir(dir, true, err => {
|
|
181
|
-
if (err) {
|
|
182
|
-
reject(new Error(`Failed to create source directory.`));
|
|
183
|
-
} else {
|
|
184
|
-
resolve();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const changeWorkingDirectory = (dir: string) =>
|
|
190
|
-
new Promise<void>((resolve, reject) => {
|
|
191
|
-
console.log(`Changing to directory ${dir}`);
|
|
192
|
-
client.cwd(dir, err => {
|
|
193
|
-
if (err) {
|
|
194
|
-
reject(
|
|
195
|
-
new Error(
|
|
196
|
-
'\nError: Failed to change to directory. ' +
|
|
197
|
-
'Check whether it exists on the FTP server.\n' +
|
|
198
|
-
'If you want to create a new source, use the ' +
|
|
199
|
-
'--create-source option.'
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
} else {
|
|
203
|
-
resolve();
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const downloadFileContent = (filename: string) =>
|
|
209
|
-
new Promise<string>((resolve, reject) => {
|
|
210
|
-
console.log(`Downloading file ${filename}`);
|
|
211
|
-
let data = '';
|
|
212
|
-
client.get(filename, (err, stream) => {
|
|
213
|
-
if (err) return reject(err);
|
|
214
|
-
stream.once('close', () => resolve(data));
|
|
215
|
-
stream.on('data', chunk => {
|
|
216
|
-
data += chunk;
|
|
217
|
-
});
|
|
218
|
-
return undefined;
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
398
|
const assertAppVersionIsValid = (
|
|
223
399
|
latestAppVersion: string | undefined,
|
|
224
400
|
app: App
|
|
@@ -234,21 +410,9 @@ const assertAppVersionIsValid = (
|
|
|
234
410
|
}
|
|
235
411
|
};
|
|
236
412
|
|
|
237
|
-
type UploadLocalFile = (localFileName: string, remote: string) => Promise<void>;
|
|
238
|
-
type UploadBufferContent = (content: Buffer, remote: string) => Promise<void>;
|
|
239
|
-
|
|
240
|
-
const uploadFile: UploadLocalFile & UploadBufferContent = (
|
|
241
|
-
local: string | Buffer,
|
|
242
|
-
remote: string
|
|
243
|
-
) =>
|
|
244
|
-
new Promise<void>((resolve, reject) => {
|
|
245
|
-
console.log(`Uploading file ${remote}`);
|
|
246
|
-
client.put(local, remote, err => (err ? reject(err) : resolve()));
|
|
247
|
-
});
|
|
248
|
-
|
|
249
413
|
const createBlankSourceJson = async (name: string) => {
|
|
250
414
|
try {
|
|
251
|
-
await
|
|
415
|
+
await client.download('source.json');
|
|
252
416
|
} catch {
|
|
253
417
|
// Expected that the download throws an exception,
|
|
254
418
|
// because the file is supposed to not exist yet
|
|
@@ -266,7 +430,7 @@ const createBlankSourceJson = async (name: string) => {
|
|
|
266
430
|
const downloadSourceJson = async () => {
|
|
267
431
|
let sourceJsonContent;
|
|
268
432
|
try {
|
|
269
|
-
sourceJsonContent = await
|
|
433
|
+
sourceJsonContent = await client.download('source.json');
|
|
270
434
|
const sourceJson = <SourceJson>JSON.parse(sourceJsonContent);
|
|
271
435
|
if (
|
|
272
436
|
sourceJson == null ||
|
|
@@ -281,7 +445,8 @@ const downloadSourceJson = async () => {
|
|
|
281
445
|
|
|
282
446
|
return sourceJson;
|
|
283
447
|
} catch (error) {
|
|
284
|
-
const message =
|
|
448
|
+
const message =
|
|
449
|
+
'Unable to read `source.json` on the server. If you want to create a new source, use the option --create-source.\nError: ';
|
|
285
450
|
const caughtError = errorAsString(error);
|
|
286
451
|
const maybeSourceJsonContent =
|
|
287
452
|
sourceJsonContent == null
|
|
@@ -313,7 +478,7 @@ const downloadExistingAppInfo = async (
|
|
|
313
478
|
app: App
|
|
314
479
|
): Promise<Partial<Pick<AppInfo, 'latestVersion' | 'versions'>>> => {
|
|
315
480
|
try {
|
|
316
|
-
const appInfoContent = await
|
|
481
|
+
const appInfoContent = await client.download(app.appInfoName);
|
|
317
482
|
return JSON.parse(appInfoContent) as AppInfo;
|
|
318
483
|
} catch (error) {
|
|
319
484
|
console.log(
|
|
@@ -330,8 +495,13 @@ const failBecauseOfMissingProperty = () => {
|
|
|
330
495
|
);
|
|
331
496
|
};
|
|
332
497
|
|
|
333
|
-
const getUpdatedAppInfo = async (
|
|
334
|
-
|
|
498
|
+
const getUpdatedAppInfo = async (
|
|
499
|
+
app: App,
|
|
500
|
+
options: Options
|
|
501
|
+
): Promise<AppInfo> => {
|
|
502
|
+
const oldAppInfo = options.doCreateSource
|
|
503
|
+
? {}
|
|
504
|
+
: await downloadExistingAppInfo(app);
|
|
335
505
|
|
|
336
506
|
assertAppVersionIsValid(oldAppInfo.latestVersion, app);
|
|
337
507
|
|
|
@@ -367,18 +537,19 @@ const getUpdatedAppInfo = async (app: App): Promise<AppInfo> => {
|
|
|
367
537
|
};
|
|
368
538
|
|
|
369
539
|
const uploadSourceJson = (sourceJson: SourceJson) =>
|
|
370
|
-
|
|
540
|
+
client.uploadContent(
|
|
371
541
|
Buffer.from(JSON.stringify(sourceJson, undefined, 2)),
|
|
372
542
|
'source.json'
|
|
373
543
|
);
|
|
374
544
|
|
|
375
545
|
const uploadAppInfo = (app: App, appInfo: AppInfo) =>
|
|
376
|
-
|
|
546
|
+
client.uploadContent(
|
|
377
547
|
Buffer.from(JSON.stringify(appInfo, undefined, 2)),
|
|
378
548
|
app.appInfoName
|
|
379
549
|
);
|
|
380
550
|
|
|
381
|
-
const uploadPackage = (app: App) =>
|
|
551
|
+
const uploadPackage = (app: App) =>
|
|
552
|
+
client.uploadLocalFile(app.filename, app.filename);
|
|
382
553
|
|
|
383
554
|
const uploadChangelog = (app: App) => {
|
|
384
555
|
const changelogFilename = 'Changelog.md';
|
|
@@ -387,7 +558,7 @@ const uploadChangelog = (app: App) => {
|
|
|
387
558
|
return Promise.reject(new Error(errorMsg));
|
|
388
559
|
}
|
|
389
560
|
|
|
390
|
-
return
|
|
561
|
+
return client.uploadLocalFile(changelogFilename, app.releaseNotesFilename);
|
|
391
562
|
};
|
|
392
563
|
|
|
393
564
|
const uploadIcon = (app: App) => {
|
|
@@ -397,37 +568,28 @@ const uploadIcon = (app: App) => {
|
|
|
397
568
|
return Promise.reject(new Error(errorMsg));
|
|
398
569
|
}
|
|
399
570
|
|
|
400
|
-
return
|
|
571
|
+
return client.uploadLocalFile(localIconFilename, app.iconFilename);
|
|
401
572
|
};
|
|
402
573
|
|
|
403
574
|
const main = async () => {
|
|
404
575
|
try {
|
|
405
|
-
/*
|
|
406
|
-
* To use this script REPO_HOST, REPO_USER and REPO_PASS will need to be set
|
|
407
|
-
*/
|
|
408
|
-
const config = {
|
|
409
|
-
host: process.env.REPO_HOST || 'localhost',
|
|
410
|
-
port: Number(process.env.REPO_PORT) || 21,
|
|
411
|
-
user: process.env.REPO_USER || 'anonymous',
|
|
412
|
-
password: process.env.REPO_PASS || 'anonymous@',
|
|
413
|
-
};
|
|
414
|
-
|
|
415
576
|
const options = parseOptions();
|
|
416
577
|
|
|
578
|
+
client =
|
|
579
|
+
options.destination === 'ftp'
|
|
580
|
+
? new FtpClient(options)
|
|
581
|
+
: new ArtifactoryClient(options);
|
|
582
|
+
|
|
417
583
|
checkAppProperties({
|
|
418
584
|
checkChangelogHasCurrentEntry: options.deployOfficial,
|
|
419
585
|
});
|
|
420
586
|
|
|
421
587
|
const app = packOrReadPackage(options);
|
|
422
588
|
|
|
423
|
-
await
|
|
424
|
-
if (options.doCreateSource) {
|
|
425
|
-
await createSourceDirectory(options.sourceDir);
|
|
426
|
-
}
|
|
427
|
-
await changeWorkingDirectory(options.sourceDir);
|
|
589
|
+
await client.initialise(options);
|
|
428
590
|
|
|
429
591
|
const sourceJson = await getUpdatedSourceJson(app, options);
|
|
430
|
-
const appInfo = await getUpdatedAppInfo(app);
|
|
592
|
+
const appInfo = await getUpdatedAppInfo(app, options);
|
|
431
593
|
|
|
432
594
|
await uploadChangelog(app);
|
|
433
595
|
await uploadIcon(app);
|
|
@@ -441,7 +603,7 @@ const main = async () => {
|
|
|
441
603
|
process.exitCode = 1;
|
|
442
604
|
}
|
|
443
605
|
|
|
444
|
-
client
|
|
606
|
+
client?.end();
|
|
445
607
|
};
|
|
446
608
|
|
|
447
609
|
main();
|
|
@@ -78,8 +78,8 @@ const updateChangelog = (nextReleaseNumber: string) => {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (
|
|
81
|
-
header === '
|
|
82
|
-
header === `${nextReleaseNumber} -
|
|
81
|
+
header.toLowerCase() === 'unreleased' ||
|
|
82
|
+
header.toLowerCase() === `${nextReleaseNumber} - unreleased`
|
|
83
83
|
) {
|
|
84
84
|
writeFileSync(
|
|
85
85
|
'Changelog.md',
|
package/src/About/DeviceCard.tsx
CHANGED
|
@@ -8,11 +8,7 @@ import React from 'react';
|
|
|
8
8
|
import { useSelector } from 'react-redux';
|
|
9
9
|
|
|
10
10
|
import Card from '../Card/Card';
|
|
11
|
-
import {
|
|
12
|
-
buyOnlineUrl,
|
|
13
|
-
deviceInfo,
|
|
14
|
-
productPageUrl,
|
|
15
|
-
} from '../Device/deviceInfo/deviceInfo';
|
|
11
|
+
import { deviceInfo } from '../Device/deviceInfo/deviceInfo';
|
|
16
12
|
import { selectedDevice, selectedDeviceInfo } from '../Device/deviceSlice';
|
|
17
13
|
import AboutButton from './AboutButton';
|
|
18
14
|
import Section from './Section';
|
|
@@ -38,7 +34,7 @@ export default () => {
|
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
const pca = device.devkit?.boardVersion;
|
|
41
|
-
const { name, cores } = deviceInfo(device);
|
|
37
|
+
const { name, cores, website } = deviceInfo(device);
|
|
42
38
|
|
|
43
39
|
return (
|
|
44
40
|
<Card title="Device">
|
|
@@ -61,13 +57,13 @@ export default () => {
|
|
|
61
57
|
|
|
62
58
|
<Section>
|
|
63
59
|
<AboutButton
|
|
64
|
-
url={
|
|
60
|
+
url={website.buyOnline}
|
|
65
61
|
label="Find distributor"
|
|
66
62
|
/>
|
|
67
63
|
</Section>
|
|
68
64
|
<Section>
|
|
69
65
|
<AboutButton
|
|
70
|
-
url={
|
|
66
|
+
url={website.productPage}
|
|
71
67
|
label="Go to product page"
|
|
72
68
|
/>
|
|
73
69
|
</Section>
|