Skip to content

Commit 6128e30

Browse files
Merge pull request #1000 from NordicSemiconductor/feat/specify_nrfutil_core_version
Specify core version of nrfutil
2 parents a1fcdd9 + 8f8cb21 commit 6128e30

File tree

9 files changed

+139
-55
lines changed

9 files changed

+139
-55
lines changed

Changelog.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@ This project does _not_ adhere to
77
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) but contrary to it
88
every new version is a new major version.
99

10+
## 212.0.0 - 2025-06-05
11+
12+
### Added
13+
14+
- Specify what core version of nrfutil an app depends on.
15+
16+
### Steps to upgrade when using this package
17+
18+
- In apps in `package.json` set the field `nrfConnectForDesktop.nrfutilCore`
19+
to the core version of nrfutil that the nrfutil commands will use.
20+
1021
## 211.0.0 - 2025-05-29
1122

1223
### Fixed
1324

14-
- It was observed that on mac we get multiple arrive event and the only chnage
15-
is the number if serial ports. Now the selected device in redux will also
16-
update to reflect these chnages
25+
- It was observed on macOS that we get multiple arrive events and the only
26+
change is the number of serial ports. Now the selected device in redux is
27+
also updated to reflect these changes.
1728

1829
## 210.0.0 - 2025-05-15
1930

ipc/MetaFiles.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ export type AppVersions = {
2222
[version: string]: AppVersion;
2323
};
2424

25-
export type AppVersion = {
25+
type AppVersion = {
2626
shasum?: string;
2727
publishTimestamp?: string;
2828
tarballUrl: UrlString;
2929
nrfutilModules?: NrfutilModules;
30+
nrfutilCore?: NrfutilModuleVersion;
3031
};
3132

3233
export interface AppInfo {
@@ -57,6 +58,9 @@ const nrfutilModuleVersion = semver;
5758
export type NrfutilModuleName = z.infer<typeof nrfutilModuleName>;
5859
export type NrfutilModuleVersion = z.infer<typeof nrfutilModuleVersion>;
5960

60-
export const nrfModules = z.record(nrfutilModuleName, z.tuple([semver]));
61+
export const nrfModules = z.record(
62+
nrfutilModuleName,
63+
z.tuple([nrfutilModuleVersion])
64+
);
6165

6266
export type NrfutilModules = z.infer<typeof nrfModules>;

ipc/apps.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
*/
66

77
import { handle, invoke } from './infrastructure/rendererToMain';
8-
import type { AppVersions, NrfutilModules, UrlString } from './MetaFiles';
8+
import type {
9+
AppVersions,
10+
NrfutilModules,
11+
NrfutilModuleVersion,
12+
UrlString,
13+
} from './MetaFiles';
914
import { LOCAL, Source, SourceName } from './sources';
1015

1116
export interface AppSpec {
@@ -34,6 +39,7 @@ interface Installed {
3439
repositoryUrl?: UrlString;
3540
html?: string;
3641
nrfutil?: NrfutilModules;
42+
nrfutilCore?: NrfutilModuleVersion;
3743
installed: {
3844
publishTimestamp?: string;
3945
path: string;

ipc/schema/packageJson.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const parsePackageJson = parseWithPrettifiedErrorMessage(packageJson);
2626
const nrfConnectForDesktop = z.object({
2727
supportedDevices: z.enum(knownDevicePcas).array().nonempty().optional(),
2828
nrfutil: nrfModules.optional(),
29+
nrfutilCore: semver,
2930
html: z.string(),
3031
});
3132

@@ -59,11 +60,11 @@ export const parsePackageJsonApp =
5960
parseWithPrettifiedErrorMessage(packageJsonApp);
6061

6162
// In the launcher we want to handle that the whole nrfConnectForDesktop may be missing
62-
// and the html in it can also be undefined, so there we need to use this legacy variant
63+
// and html or nrfutilCore in it can also be undefined, so there we need to use this legacy variant
6364
const packageJsonLegacyApp = packageJsonApp.extend({
6465
nrfConnectForDesktop: nrfConnectForDesktop
6566
.extend({ supportedDevices: z.array(z.string()).nonempty().optional() })
66-
.partial({ html: true })
67+
.partial({ html: true, nrfutilCore: true })
6768
.optional(),
6869
});
6970

nrfutil/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
export { default as prepareSandbox } from './sandbox';
8-
export { NrfutilSandbox } from './sandbox';
8+
export type { NrfutilSandbox } from './sandbox';
99
export type { Progress } from './sandboxTypes';
1010
export { getNrfutilLogger, setNrfutilLogger } from './nrfutilLogger';
1111
export {

nrfutil/moduleVersion.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
55
*/
66

7-
import { packageJsonApp } from '../src/utils/packageJson';
7+
import { isLauncher, packageJsonApp } from '../src/utils/packageJson';
88
import {
99
type Dependency,
1010
hasVersion,
@@ -78,11 +78,20 @@ const versionFromPackageJson = (module: string) =>
7878
packageJsonApp().nrfConnectForDesktop.nrfutil?.[module][0];
7979

8080
const failToDetermineVersion = (module: string) => {
81-
throw new Error(`No version specified for the bundled nrfutil ${module}`);
81+
throw new Error(`No version specified for nrfutil ${module}`);
8282
};
8383

8484
export const versionToInstall = (module: string, version?: string) =>
8585
version ??
8686
overriddenVersion(module) ??
8787
versionFromPackageJson(module) ??
8888
failToDetermineVersion(module);
89+
90+
const coreVersionFromPackageJson = () =>
91+
isLauncher()
92+
? undefined // Will lead to using CORE_VERSION_FOR_LEGACY_APPS
93+
: packageJsonApp().nrfConnectForDesktop.nrfutilCore ??
94+
failToDetermineVersion('core');
95+
96+
export const coreVersionsToInstall = (coreVersion?: string) =>
97+
coreVersion ?? overriddenVersion('core') ?? coreVersionFromPackageJson();

nrfutil/sandbox.ts

Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import treeKill from 'tree-kill';
1313
import describeError from '../src/logging/describeError';
1414
import telemetry from '../src/telemetry/telemetry';
1515
import { isDevelopment } from '../src/utils/environment';
16-
import { versionToInstall } from './moduleVersion';
16+
import { coreVersionsToInstall, versionToInstall } from './moduleVersion';
1717
import { getNrfutilLogger } from './nrfutilLogger';
1818
import {
1919
BackgroundTask,
@@ -40,18 +40,31 @@ const parseJsonBuffers = <T>(data: Buffer): T[] | undefined => {
4040
}
4141
};
4242

43-
const nrfutilSandboxFolder =
44-
process.platform === 'darwin' && process.arch !== 'x64'
45-
? path.join('nrfutil-sandboxes', process.arch)
46-
: 'nrfutil-sandboxes';
47-
48-
const prepareEnv = (baseDir: string, module: string, version: string) => {
43+
const nrfutilSandboxPathSegments = (
44+
baseDir: string,
45+
module: string,
46+
version: string,
47+
coreVersion?: string
48+
) => [
49+
baseDir,
50+
'nrfutil-sandboxes',
51+
...(process.platform === 'darwin' && process.arch !== 'x64'
52+
? [process.arch]
53+
: []),
54+
...(coreVersion != null ? [coreVersion] : []),
55+
module,
56+
version,
57+
];
58+
59+
const prepareEnv = (
60+
baseDir: string,
61+
module: string,
62+
version: string,
63+
coreVersion?: string
64+
) => {
4965
const env = { ...process.env };
5066
env.NRFUTIL_HOME = path.join(
51-
baseDir,
52-
nrfutilSandboxFolder,
53-
module,
54-
version
67+
...nrfutilSandboxPathSegments(baseDir, module, version, coreVersion)
5568
);
5669
fs.mkdirSync(env.NRFUTIL_HOME, { recursive: true });
5770

@@ -124,16 +137,25 @@ export class NrfutilSandbox {
124137
baseDir: string;
125138
module: string;
126139
version: string;
140+
coreVersion: string | undefined; // Must only be undefined when the launcher creates a sandbox for a legacy app, which does not specify the required core version
127141
onLoggingHandlers: ((logging: LogMessage, pid?: number) => void)[] = [];
128142
logLevel: LogLevel = isDevelopment ? 'error' : 'off';
129143
env: ReturnType<typeof prepareEnv>;
130144

131-
constructor(baseDir: string, module: string, version: string) {
145+
readonly CORE_VERSION_FOR_LEGACY_APPS = '8.0.0';
146+
147+
constructor(
148+
baseDir: string,
149+
module: string,
150+
version: string,
151+
coreVersion?: string
152+
) {
132153
this.baseDir = baseDir;
133154
this.module = module;
134155
this.version = version;
156+
this.coreVersion = coreVersion;
135157

136-
this.env = prepareEnv(baseDir, module, version);
158+
this.env = prepareEnv(baseDir, module, version, coreVersion);
137159
}
138160

139161
private processLoggingData = (data: NrfutilJson, pid?: number) => {
@@ -173,10 +195,12 @@ export class NrfutilSandbox {
173195
if (
174196
fs.existsSync(
175197
path.join(
176-
this.baseDir,
177-
nrfutilSandboxFolder,
178-
this.module,
179-
this.version,
198+
...nrfutilSandboxPathSegments(
199+
this.baseDir,
200+
this.module,
201+
this.version,
202+
this.coreVersion
203+
),
180204
'bin',
181205
`nrfutil-${this.module}${
182206
os.platform() === 'win32' ? '.exe' : ''
@@ -201,15 +225,8 @@ export class NrfutilSandbox {
201225
force: true,
202226
});
203227
}
204-
await this.updateNrfUtilCore();
205-
await this.spawnNrfutil(
206-
'install',
207-
[`${this.module}=${this.version}`, '--force'],
208-
onProgress
209-
);
210-
getNrfutilLogger()?.info(
211-
`Successfully installed nrfutil ${this.module} version: ${this.version}`
212-
);
228+
await this.installNrfUtilCore(onProgress);
229+
await this.installNrfUtilCommand(onProgress);
213230
} catch (error) {
214231
if (this.env.NRFUTIL_HOME && fs.existsSync(this.env.NRFUTIL_HOME)) {
215232
fs.rmSync(this.env.NRFUTIL_HOME, {
@@ -218,25 +235,61 @@ export class NrfutilSandbox {
218235
});
219236
}
220237

221-
getNrfutilLogger()?.error(
222-
`Error while installing nrfutil ${this.module} version: ${
223-
this.version
224-
}. describeError: ${describeError(error)}`
225-
);
226238
throw error;
227239
}
228240
};
229241

230-
public updateNrfUtilCore = async (
242+
private installNrfUtilCore = async (
243+
onProgress?: (progress: Progress, task?: Task) => void
244+
) => {
245+
const currentCoreVersion = await this.getCoreVersion();
246+
const requestedCoreVersion =
247+
this.coreVersion ?? this.CORE_VERSION_FOR_LEGACY_APPS;
248+
if (currentCoreVersion.version === requestedCoreVersion) {
249+
getNrfutilLogger()?.debug(
250+
`Requested nrfutil core version ${requestedCoreVersion} is already installed.`
251+
);
252+
253+
return;
254+
}
255+
256+
await this.install(
257+
'core',
258+
requestedCoreVersion,
259+
'self-upgrade',
260+
['--to-version', requestedCoreVersion],
261+
onProgress
262+
);
263+
};
264+
265+
public installNrfUtilCommand = (
231266
onProgress?: (progress: Progress, task?: Task) => void
267+
) =>
268+
this.install(
269+
this.module,
270+
this.version,
271+
'install',
272+
[`${this.module}=${this.version}`, '--force'],
273+
onProgress
274+
);
275+
276+
public install = async (
277+
module: string,
278+
version: string,
279+
...args: Parameters<typeof this.spawnNrfutil>
232280
) => {
233281
try {
234-
await this.spawnNrfutil('self-upgrade', [], onProgress);
235-
} catch (error) {
236-
// User might not have internet hance fail silently
237-
getNrfutilLogger()?.error(
238-
`Error while updating the bundled core for nrfutil ${this.module}.`
282+
await this.spawnNrfutil(...args);
283+
getNrfutilLogger()?.info(
284+
`Successfully installed nrfutil ${module} version: ${version}`
239285
);
286+
} catch (error) {
287+
const errorMessage = `Error while installing nrfutil ${module} version ${version}: ${describeError(
288+
error
289+
)}`;
290+
291+
getNrfutilLogger()?.error(errorMessage);
292+
throw new Error(errorMessage);
240293
}
241294
};
242295

@@ -765,22 +818,20 @@ export default async (
765818
baseDir: string,
766819
module: string,
767820
version?: string,
821+
coreVersion?: string,
768822
onProgress?: (progress: Progress, task?: Task) => void
769823
) => {
770824
const sandbox = new NrfutilSandbox(
771825
baseDir,
772826
module,
773-
versionToInstall(module, version)
827+
versionToInstall(module, version),
828+
coreVersionsToInstall(coreVersion)
774829
);
775830

776831
onProgress?.(convertNrfutilProgress({ progressPercentage: 0 }));
777-
const result = await sandbox.isSandboxInstalled();
778832

779-
if (!result) {
833+
if (!(await sandbox.isSandboxInstalled())) {
780834
await sandbox.prepareSandbox(onProgress);
781-
} else {
782-
// update nrfutil core
783-
await sandbox.updateNrfUtilCore(onProgress);
784835
}
785836

786837
onProgress?.(convertNrfutilProgress({ progressPercentage: 100 }));

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nordicsemiconductor/pc-nrfconnect-shared",
3-
"version": "211.0.0",
3+
"version": "212.0.0",
44
"description": "Shared commodities for developing pc-nrfconnect-* packages",
55
"repository": {
66
"type": "git",

scripts/nordic-publish.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ const getUpdatedAppInfo = async (app: App): Promise<AppInfo> => {
466466
} = app.packageJson;
467467

468468
const nrfutilModules = nrfConnectForDesktop?.nrfutil;
469+
const nrfutilCore = nrfConnectForDesktop?.nrfutilCore;
469470

470471
return {
471472
name,
@@ -482,6 +483,7 @@ const getUpdatedAppInfo = async (app: App): Promise<AppInfo> => {
482483
publishTimestamp: new Date().toISOString(),
483484
shasum: app.shasum,
484485
nrfutilModules,
486+
nrfutilCore,
485487
},
486488
},
487489
};

0 commit comments

Comments
 (0)