@nordicsemiconductor/pc-nrfconnect-shared 197.0.0 → 199.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 +26 -0
- package/ipc/MetaFiles.ts +1 -0
- package/package.json +4 -1
- package/scripts/create-source.ts +78 -0
- package/scripts/nordic-publish.js +17 -18
- package/scripts/nordic-publish.ts +267 -159
- 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/ipc/MetaFiles.d.ts +3 -0
- package/typings/generated/ipc/MetaFiles.d.ts.map +1 -1
- package/typings/generated/ipc/schema/packageJson.d.ts +6 -6
- package/typings/generated/scripts/create-source.d.ts +3 -0
- package/typings/generated/scripts/create-source.d.ts.map +1 -0
- 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/packageJson.d.ts +2 -2
- 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,201 @@ 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
|
+
changeWorkingDirectory = () =>
|
|
111
|
+
new Promise<void>((resolve, reject) => {
|
|
112
|
+
console.log(`Changing to directory ${this.sourceDir}`);
|
|
113
|
+
this.ftpClient.cwd(this.sourceDir, err => {
|
|
114
|
+
if (err) {
|
|
115
|
+
reject(
|
|
116
|
+
new Error(
|
|
117
|
+
'\nError: Failed to change to directory. ' +
|
|
118
|
+
'Check whether it exists on the FTP server.'
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
resolve();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
initialise = async () => {
|
|
128
|
+
await this.connect();
|
|
129
|
+
await this.changeWorkingDirectory();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
download = (filename: string) =>
|
|
133
|
+
new Promise<string>((resolve, reject) => {
|
|
134
|
+
console.log(`Downloading file ${filename}`);
|
|
135
|
+
let data = '';
|
|
136
|
+
this.ftpClient.get(filename, (err, stream) => {
|
|
137
|
+
if (err) return reject(err);
|
|
138
|
+
stream.once('close', () => resolve(data));
|
|
139
|
+
stream.on('data', chunk => {
|
|
140
|
+
data += chunk;
|
|
141
|
+
});
|
|
142
|
+
return undefined;
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
upload = (
|
|
147
|
+
contentOrLocalFilename: string | Buffer,
|
|
148
|
+
remoteFilename: string
|
|
149
|
+
) =>
|
|
150
|
+
new Promise<void>((resolve, reject) => {
|
|
151
|
+
console.log(`Uploading file ${remoteFilename}`);
|
|
152
|
+
this.ftpClient.put(contentOrLocalFilename, remoteFilename, err =>
|
|
153
|
+
err ? reject(err) : resolve()
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
uploadContent = this.upload;
|
|
158
|
+
uploadLocalFile = this.upload;
|
|
159
|
+
|
|
160
|
+
end = () => this.ftpClient.end();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class ArtifactoryClient extends Client {
|
|
164
|
+
token = process.env.ARTIFACTORY_TOKEN;
|
|
165
|
+
|
|
166
|
+
sourceUrl: string;
|
|
167
|
+
|
|
168
|
+
constructor(private readonly options: Options) {
|
|
169
|
+
super();
|
|
170
|
+
|
|
171
|
+
if (this.token == null) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'The environment variable ARTIFACTORY_TOKEN must be set.'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.sourceUrl = `https://files.nordicsemi.com/artifactory/swtools/${this.getAccessLevel()}/ncd/apps/${
|
|
178
|
+
options.source
|
|
179
|
+
}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getAccessLevel = () => {
|
|
183
|
+
if (this.options.accessLevel != null) return this.options.accessLevel;
|
|
184
|
+
|
|
185
|
+
return this.options.deployOfficial ? 'external' : 'internal';
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
download = async (filename: string) => {
|
|
189
|
+
console.log(`Downloading ${filename}`);
|
|
190
|
+
|
|
191
|
+
const url = `${this.sourceUrl}/${filename}`;
|
|
192
|
+
const res = await fetch(url, {
|
|
193
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!res.ok) {
|
|
197
|
+
throw new Error(`Failed to download ${url}: ${res.statusText}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return res.text();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
upload = async (content: Buffer, remoteFilename: string) => {
|
|
204
|
+
const url = `${this.sourceUrl}/${remoteFilename}`;
|
|
205
|
+
const res = await fetch(url, {
|
|
206
|
+
method: 'PUT',
|
|
207
|
+
body: content,
|
|
208
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (!res.ok) {
|
|
212
|
+
throw new Error(`Failed to upload ${url}: ${res.statusText}`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
uploadContent = (content: Buffer, remoteFilename: string) => {
|
|
217
|
+
console.log(`Uploading content for ${remoteFilename}`);
|
|
218
|
+
|
|
219
|
+
return this.upload(content, remoteFilename);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
uploadLocalFile = (localFilename: string, remoteFilename: string) => {
|
|
223
|
+
console.log(
|
|
224
|
+
`Uploading local file ${localFilename} as ${remoteFilename}`
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return this.upload(fs.readFileSync(localFilename), remoteFilename);
|
|
228
|
+
};
|
|
229
|
+
}
|
|
34
230
|
|
|
35
231
|
const hasMessage = (error: unknown): error is { message: unknown } =>
|
|
36
232
|
error != null && typeof error === 'object' && 'message' in error;
|
|
@@ -38,64 +234,84 @@ const hasMessage = (error: unknown): error is { message: unknown } =>
|
|
|
38
234
|
const errorAsString = (error: unknown) =>
|
|
39
235
|
hasMessage(error) ? error.message : String(error);
|
|
40
236
|
|
|
41
|
-
const
|
|
42
|
-
|
|
237
|
+
const validAccessLevels = [
|
|
238
|
+
'external',
|
|
239
|
+
'external-confidential',
|
|
240
|
+
'internal',
|
|
241
|
+
'internal-confidential',
|
|
242
|
+
] as const;
|
|
43
243
|
|
|
44
|
-
|
|
45
|
-
if (deployOfficial) return repoDirOfficial;
|
|
244
|
+
type AccessLevel = (typeof validAccessLevels)[number];
|
|
46
245
|
|
|
47
|
-
|
|
48
|
-
|
|
246
|
+
const isAccessLevel = (value: string): value is AccessLevel =>
|
|
247
|
+
(validAccessLevels as readonly string[]).includes(value);
|
|
248
|
+
|
|
249
|
+
const splitSourceAndAccessLevel = (sourceAndMaybeAccessLevel: string) => {
|
|
250
|
+
const match = sourceAndMaybeAccessLevel.match(
|
|
251
|
+
/(?<source>.*?)\s*\((?<accessLevel>.*)\)/
|
|
252
|
+
);
|
|
49
253
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
254
|
+
if (match == null) {
|
|
255
|
+
return { source: sourceAndMaybeAccessLevel };
|
|
256
|
+
}
|
|
53
257
|
|
|
54
|
-
|
|
55
|
-
if (deployOfficial) return repoUrlOfficial;
|
|
258
|
+
const { source, accessLevel } = match.groups!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Can never be null because of the regex
|
|
56
259
|
|
|
57
|
-
|
|
260
|
+
if (!isAccessLevel(accessLevel)) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`The specified access level "${accessLevel}" must be one of ${validAccessLevels.join(
|
|
263
|
+
', '
|
|
264
|
+
)}.`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { source, accessLevel };
|
|
58
269
|
};
|
|
59
270
|
|
|
60
271
|
interface Options {
|
|
61
272
|
doPack: boolean;
|
|
62
|
-
doCreateSource: boolean;
|
|
63
273
|
deployOfficial: boolean;
|
|
64
|
-
|
|
65
|
-
sourceUrl: string;
|
|
274
|
+
source: string;
|
|
66
275
|
sourceName?: string;
|
|
276
|
+
destination: 'ftp' | 'artifactory';
|
|
277
|
+
accessLevel?: AccessLevel;
|
|
67
278
|
}
|
|
68
279
|
|
|
69
280
|
const parseOptions = (): Options => {
|
|
70
281
|
program
|
|
71
|
-
.description('Publish
|
|
282
|
+
.description('Publish an nRF Connect for Desktop app')
|
|
72
283
|
.requiredOption(
|
|
73
284
|
'-s, --source <source>',
|
|
74
|
-
'Specify the source to publish (e.g. official).'
|
|
285
|
+
'Specify the source to publish (e.g. "official" or "releast-test"). ' +
|
|
286
|
+
'When publishing to Artifactory, an access level can be ' +
|
|
287
|
+
'specified at the end in parantheses (e.g. "official (external)").'
|
|
288
|
+
)
|
|
289
|
+
.addOption(
|
|
290
|
+
new Option(
|
|
291
|
+
'-d, --destination <ftp|artifactory>',
|
|
292
|
+
'Specify where to publish.'
|
|
293
|
+
)
|
|
294
|
+
.choices(['ftp', 'artifactory'])
|
|
295
|
+
.makeOptionMandatory()
|
|
75
296
|
)
|
|
76
297
|
.option(
|
|
77
298
|
'-n, --no-pack',
|
|
78
299
|
'Publish existing .tgz file at the root directory without npm pack.'
|
|
79
300
|
)
|
|
80
|
-
.option(
|
|
81
|
-
'--create-source <source name>',
|
|
82
|
-
'Do not fail if the source specifiec with --source does not yet ' +
|
|
83
|
-
'exist but instead create a new source with this name ' +
|
|
84
|
-
'(e.g. "Release Test").'
|
|
85
|
-
)
|
|
86
301
|
.parse();
|
|
87
302
|
|
|
88
303
|
const options = program.opts();
|
|
89
304
|
|
|
90
|
-
const
|
|
305
|
+
const { source, accessLevel } = splitSourceAndAccessLevel(options.source);
|
|
306
|
+
|
|
307
|
+
const deployOfficial = source === 'official';
|
|
91
308
|
|
|
92
309
|
return {
|
|
93
310
|
doPack: options.pack,
|
|
94
|
-
|
|
95
|
-
sourceName: options.createSource,
|
|
311
|
+
source,
|
|
96
312
|
deployOfficial,
|
|
97
|
-
|
|
98
|
-
|
|
313
|
+
destination: options.destination,
|
|
314
|
+
accessLevel,
|
|
99
315
|
};
|
|
100
316
|
};
|
|
101
317
|
|
|
@@ -144,7 +360,7 @@ const packOrReadPackage = (options: Options): App => {
|
|
|
144
360
|
version,
|
|
145
361
|
filename,
|
|
146
362
|
shasum,
|
|
147
|
-
sourceUrl:
|
|
363
|
+
sourceUrl: client.sourceUrl,
|
|
148
364
|
isOfficial: options.deployOfficial,
|
|
149
365
|
appInfoName: `${name}.json`,
|
|
150
366
|
releaseNotesFilename: `${name}-Changelog.md`,
|
|
@@ -153,72 +369,6 @@ const packOrReadPackage = (options: Options): App => {
|
|
|
153
369
|
};
|
|
154
370
|
};
|
|
155
371
|
|
|
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
372
|
const assertAppVersionIsValid = (
|
|
223
373
|
latestAppVersion: string | undefined,
|
|
224
374
|
app: App
|
|
@@ -234,39 +384,10 @@ const assertAppVersionIsValid = (
|
|
|
234
384
|
}
|
|
235
385
|
};
|
|
236
386
|
|
|
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
|
-
const createBlankSourceJson = async (name: string) => {
|
|
250
|
-
try {
|
|
251
|
-
await downloadFileContent('source.json');
|
|
252
|
-
} catch {
|
|
253
|
-
// Expected that the download throws an exception,
|
|
254
|
-
// because the file is supposed to not exist yet
|
|
255
|
-
return {
|
|
256
|
-
name,
|
|
257
|
-
apps: [],
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
throw new Error(
|
|
262
|
-
'`--create-source` given, but a `source.json` already exists on the server.'
|
|
263
|
-
);
|
|
264
|
-
};
|
|
265
|
-
|
|
266
387
|
const downloadSourceJson = async () => {
|
|
267
388
|
let sourceJsonContent;
|
|
268
389
|
try {
|
|
269
|
-
sourceJsonContent = await
|
|
390
|
+
sourceJsonContent = await client.download('source.json');
|
|
270
391
|
const sourceJson = <SourceJson>JSON.parse(sourceJsonContent);
|
|
271
392
|
if (
|
|
272
393
|
sourceJson == null ||
|
|
@@ -292,15 +413,10 @@ const downloadSourceJson = async () => {
|
|
|
292
413
|
}
|
|
293
414
|
};
|
|
294
415
|
|
|
295
|
-
const getUpdatedSourceJson = async (
|
|
296
|
-
|
|
297
|
-
options: Options
|
|
298
|
-
): Promise<SourceJson> => {
|
|
299
|
-
const sourceJson = await (options.doCreateSource
|
|
300
|
-
? createBlankSourceJson(options.sourceName!) // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Can never be null because of the control flow
|
|
301
|
-
: downloadSourceJson());
|
|
416
|
+
const getUpdatedSourceJson = async (app: App): Promise<SourceJson> => {
|
|
417
|
+
const sourceJson = await downloadSourceJson();
|
|
302
418
|
return {
|
|
303
|
-
|
|
419
|
+
...sourceJson,
|
|
304
420
|
apps: [
|
|
305
421
|
...new Set(sourceJson.apps).add(
|
|
306
422
|
`${app.sourceUrl}/${app.appInfoName}`
|
|
@@ -313,7 +429,7 @@ const downloadExistingAppInfo = async (
|
|
|
313
429
|
app: App
|
|
314
430
|
): Promise<Partial<Pick<AppInfo, 'latestVersion' | 'versions'>>> => {
|
|
315
431
|
try {
|
|
316
|
-
const appInfoContent = await
|
|
432
|
+
const appInfoContent = await client.download(app.appInfoName);
|
|
317
433
|
return JSON.parse(appInfoContent) as AppInfo;
|
|
318
434
|
} catch (error) {
|
|
319
435
|
console.log(
|
|
@@ -367,18 +483,19 @@ const getUpdatedAppInfo = async (app: App): Promise<AppInfo> => {
|
|
|
367
483
|
};
|
|
368
484
|
|
|
369
485
|
const uploadSourceJson = (sourceJson: SourceJson) =>
|
|
370
|
-
|
|
486
|
+
client.uploadContent(
|
|
371
487
|
Buffer.from(JSON.stringify(sourceJson, undefined, 2)),
|
|
372
488
|
'source.json'
|
|
373
489
|
);
|
|
374
490
|
|
|
375
491
|
const uploadAppInfo = (app: App, appInfo: AppInfo) =>
|
|
376
|
-
|
|
492
|
+
client.uploadContent(
|
|
377
493
|
Buffer.from(JSON.stringify(appInfo, undefined, 2)),
|
|
378
494
|
app.appInfoName
|
|
379
495
|
);
|
|
380
496
|
|
|
381
|
-
const uploadPackage = (app: App) =>
|
|
497
|
+
const uploadPackage = (app: App) =>
|
|
498
|
+
client.uploadLocalFile(app.filename, app.filename);
|
|
382
499
|
|
|
383
500
|
const uploadChangelog = (app: App) => {
|
|
384
501
|
const changelogFilename = 'Changelog.md';
|
|
@@ -387,7 +504,7 @@ const uploadChangelog = (app: App) => {
|
|
|
387
504
|
return Promise.reject(new Error(errorMsg));
|
|
388
505
|
}
|
|
389
506
|
|
|
390
|
-
return
|
|
507
|
+
return client.uploadLocalFile(changelogFilename, app.releaseNotesFilename);
|
|
391
508
|
};
|
|
392
509
|
|
|
393
510
|
const uploadIcon = (app: App) => {
|
|
@@ -397,36 +514,27 @@ const uploadIcon = (app: App) => {
|
|
|
397
514
|
return Promise.reject(new Error(errorMsg));
|
|
398
515
|
}
|
|
399
516
|
|
|
400
|
-
return
|
|
517
|
+
return client.uploadLocalFile(localIconFilename, app.iconFilename);
|
|
401
518
|
};
|
|
402
519
|
|
|
403
520
|
const main = async () => {
|
|
404
521
|
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
522
|
const options = parseOptions();
|
|
416
523
|
|
|
524
|
+
client =
|
|
525
|
+
options.destination === 'ftp'
|
|
526
|
+
? new FtpClient(options)
|
|
527
|
+
: new ArtifactoryClient(options);
|
|
528
|
+
|
|
417
529
|
checkAppProperties({
|
|
418
530
|
checkChangelogHasCurrentEntry: options.deployOfficial,
|
|
419
531
|
});
|
|
420
532
|
|
|
421
533
|
const app = packOrReadPackage(options);
|
|
422
534
|
|
|
423
|
-
await
|
|
424
|
-
if (options.doCreateSource) {
|
|
425
|
-
await createSourceDirectory(options.sourceDir);
|
|
426
|
-
}
|
|
427
|
-
await changeWorkingDirectory(options.sourceDir);
|
|
535
|
+
await client.initialise(options);
|
|
428
536
|
|
|
429
|
-
const sourceJson = await getUpdatedSourceJson(app
|
|
537
|
+
const sourceJson = await getUpdatedSourceJson(app);
|
|
430
538
|
const appInfo = await getUpdatedAppInfo(app);
|
|
431
539
|
|
|
432
540
|
await uploadChangelog(app);
|
|
@@ -441,7 +549,7 @@ const main = async () => {
|
|
|
441
549
|
process.exitCode = 1;
|
|
442
550
|
}
|
|
443
551
|
|
|
444
|
-
client
|
|
552
|
+
client?.end();
|
|
445
553
|
};
|
|
446
554
|
|
|
447
555
|
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>
|