Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/builder/lib/processors/versionInfoGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ const processLibraryInfo = async (libraryInfo) => {
* }
* ```
*
* @param {object} [parameters.options.framework] Framework info embedded as the top-level
* `framework` object in sap-ui-version.json. Defaults to `{name: "", version: ""}`
* when omitted.
* @param {string} parameters.options.framework.name Framework name, e.g. "OpenUI5" or "SAPUI5"
* @param {string} parameters.options.framework.version Framework version, e.g. "1.136.0";
* may be an empty string if the version is unknown
* @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with an array containing the versionInfo resource
*/

Expand Down Expand Up @@ -463,6 +469,7 @@ export default async function({options}) {
buildTimestamp: buildTimestamp,
scmRevision: "", // TODO: insert current application scm revision here
// gav: "", // TODO: insert current application id + version here
framework: options.framework ?? {name: "", version: ""},
libraries,
components
};
Expand Down
42 changes: 42 additions & 0 deletions packages/builder/lib/tasks/generateVersionInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,45 @@ const MANIFEST_JSON = "manifest.json";
* @module @ui5/builder/tasks/generateVersionInfo
*/

/**
* Determines the UI5 framework (name and version) for the version info.
*
* Resolution order:
* 1. Explicit framework configuration from the root project's ui5.yaml
* 2. Dependency npm-scope scan (@sapui5/ → SAPUI5, @openui5/ → OpenUI5)
* 3. <code>undefined</code> if no framework signal is found
*
* @private
* @param {object} rootProject The root project instance
* @param {module:@ui5/fs/Resource[]} resources Filtered library dependency resources
* @returns {{name: string, version: string}|undefined} Framework object or undefined
*/
function resolveFramework(rootProject, resources) {
const frameworkName = rootProject.getFrameworkName?.();
if (frameworkName === "OpenUI5" || frameworkName === "SAPUI5") {
return {name: frameworkName, version: rootProject.getFrameworkVersion()};
}

let openui5Detected = false;
let openui5CoreVersion = "";
for (const res of resources) {
const id = res.getProject().getId?.();
if (id?.startsWith("@sapui5/")) {
return {name: "SAPUI5", version: ""};
}
if (id?.startsWith("@openui5/")) {
openui5Detected = true;
if (res.getProject().getName() === "sap.ui.core") {
openui5CoreVersion = res.getProject().getVersion();
}
}
}
if (openui5Detected) {
return {name: "OpenUI5", version: openui5CoreVersion};
}
return undefined;
}

/**
* Task to create sap-ui-version.json
*
Expand Down Expand Up @@ -47,10 +86,13 @@ export default async ({workspace, dependencies, options: {rootProject, pattern}}
});
const libraryInfos = await Promise.all(libraryInfosPromises);

const framework = resolveFramework(rootProject, resources);

const [versionInfoResource] = await versionInfoGenerator({
options: {
rootProjectName: rootProject.getName(),
rootProjectVersion: rootProject.getVersion(),
framework,
libraryInfos
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"version": "1.0.0",
"buildTimestamp": "202008120917",
"scmRevision": "",
"framework": {
"name": "",
"version": ""
},
"libraries": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"version": "1.0.0",
"buildTimestamp": "WILL_BE_IGNORED_BY_TESTS",
"scmRevision": "",
"framework": {
"name": "",
"version": ""
},
"libraries": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"version": "1.0.0",
"buildTimestamp": "WILL_BE_IGNORED_BY_TESTS",
"scmRevision": "",
"framework": {
"name": "",
"version": ""
},
"libraries": []
}
19 changes: 19 additions & 0 deletions packages/builder/test/lib/processors/versionInfoGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ const assertVersionInfoContent = (t, oExpectedVersionInfo, sActualContent) => {
delete lib.buildTimestamp; // removing to allow deep comparison
});

// The "framework" object is always emitted. When the expectation does not assert it
// explicitly, verify it defaults to empty strings and remove it to allow deep comparison.
if (!("framework" in oExpectedVersionInfo)) {
t.deepEqual(currentVersionInfo.framework, {name: "", version: ""},
"framework defaults to empty strings when no framework option is passed");
delete currentVersionInfo.framework;
}

t.deepEqual(currentVersionInfo, oExpectedVersionInfo, "Correct content");
};
Expand All @@ -59,6 +66,18 @@ test.serial("versionInfoGenerator empty libraryInfos parameter", async (t) => {
});


test.serial("versionInfoGenerator passes through framework option", async (t) => {
const {versionInfoGenerator} = t.context;
const versionInfos = await versionInfoGenerator({options: {
rootProjectName: "myname", rootProjectVersion: "1.33.7", libraryInfos: [],
framework: {name: "OpenUI5", version: "1.136.0"}}});

const result = JSON.parse(await versionInfos[0].getString());
t.deepEqual(result.framework, {name: "OpenUI5", version: "1.136.0"},
"framework object is emitted from the provided option");
});


test.serial("versionInfoGenerator simple library infos", async (t) => {
const {versionInfoGenerator} = t.context;
const options = {
Expand Down
185 changes: 184 additions & 1 deletion packages/builder/test/lib/tasks/generateVersionInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ const createProjectMetadata = (names, version, type) => {
getName: () => key,
getNamespace: () => names.join("/"),
getVersion: () => version || "3.0.0-" + key,
getType: () => type || "library"
getType: () => type || "library",
getId: () => key,
getFrameworkName: () => undefined,
getFrameworkVersion: () => undefined
};
};

Expand Down Expand Up @@ -91,6 +94,14 @@ async function assertCreatedVersionInfo(t, oExpectedVersionInfo, oOptions) {
return libraryA.name.localeCompare(libraryB.name);
});

// The "framework" object is always emitted. When the expectation does not assert it
// explicitly, verify it defaults to empty strings and remove it to allow deep comparison.
if (!("framework" in oExpectedVersionInfo)) {
t.deepEqual(currentVersionInfo.framework, {name: "", version: ""},
"framework defaults to empty strings when no framework is resolved");
delete currentVersionInfo.framework;
}

t.deepEqual(currentVersionInfo, oExpectedVersionInfo, "Correct content");
}

Expand Down Expand Up @@ -1184,3 +1195,175 @@ test.serial("integration: Library of type 'module'", async (t) => {

t.is(infoLogStub.callCount, 0);
});

test.serial("task: explicit OpenUI5 framework config is forwarded", async (t) => {
const workspace = createWorkspace();
const rootProject = {
getName: () => "myname",
getVersion: () => "1.33.7",
getFrameworkName: () => "OpenUI5",
getFrameworkVersion: () => "1.136.0"
};
const oOptions = {
workspace,
dependencies: resourceFactory.createAdapter({virBasePath: "/resources/"}),
options: {
pattern: "/resources/**/.library",
rootProject
}
};
await assertCreatedVersionInfo(t, {
"name": "myname",
"scmRevision": "",
"version": "1.33.7",
"framework": {"name": "OpenUI5", "version": "1.136.0"},
"libraries": []
}, oOptions);
});

test.serial("task: explicit SAPUI5 framework config is forwarded", async (t) => {
const workspace = createWorkspace();
const rootProject = {
getName: () => "myname",
getVersion: () => "1.33.7",
getFrameworkName: () => "SAPUI5",
getFrameworkVersion: () => "1.136.0"
};
const oOptions = {
workspace,
dependencies: resourceFactory.createAdapter({virBasePath: "/resources/"}),
options: {
pattern: "/resources/**/.library",
rootProject
}
};
await assertCreatedVersionInfo(t, {
"name": "myname",
"scmRevision": "",
"version": "1.33.7",
"framework": {"name": "SAPUI5", "version": "1.136.0"},
"libraries": []
}, oOptions);
});

test.serial("task: unrecognized framework name yields empty framework", async (t) => {
const workspace = createWorkspace();
const rootProject = {
getName: () => "myname",
getVersion: () => "1.33.7",
getFrameworkName: () => "CustomFramework",
getFrameworkVersion: () => "1.0.0"
};
const oOptions = {
workspace,
dependencies: resourceFactory.createAdapter({virBasePath: "/resources/"}),
options: {
pattern: "/resources/**/.library",
rootProject
}
};
// framework not in expected → assertCreatedVersionInfo verifies it defaults to {name:"",version:""}
await assertCreatedVersionInfo(t, {
"name": "myname",
"scmRevision": "",
"version": "1.33.7",
"libraries": []
}, oOptions);
});

function createScopedDepProject(names, {id, version = "1.136.0"} = {}) {
const key = names.join(".");
return {
getName: () => key,
getNamespace: () => names.join("/"),
getVersion: () => version,
getType: () => "library",
getId: () => id ?? key,
getFrameworkName: () => undefined,
getFrameworkVersion: () => undefined
};
}

test.serial("task: scans @openui5/sap.ui.core dep and resolves OpenUI5 with core version", async (t) => {
const workspace = createWorkspace();
const dep = resourceFactory.createAdapter({
virBasePath: "/",
project: createScopedDepProject(["sap", "ui", "core"], {id: "@openui5/sap.ui.core", version: "1.136.0"})
});
await dep.write(resourceFactory.createResource({
path: "/resources/sap/ui/core/.library",
string: `<library><name>sap.ui.core</name></library>`
}));
const rootProject = {
getName: () => "myapp", getVersion: () => "1.0.0",
getFrameworkName: () => undefined, getFrameworkVersion: () => undefined
};
const oOptions = {
workspace,
dependencies: dep,
options: {pattern: "/resources/**/.library", rootProject}
};
await assertCreatedVersionInfo(t, {
"name": "myapp",
"scmRevision": "",
"version": "1.0.0",
"framework": {"name": "OpenUI5", "version": "1.136.0"},
"libraries": [{"name": "sap.ui.core", "scmRevision": "", "version": "1.136.0"}]
}, oOptions);
});

test.serial("task: scans @sapui5/ dep and resolves SAPUI5 with empty version (Scenario 5)", async (t) => {
const workspace = createWorkspace();
const dep = resourceFactory.createAdapter({
virBasePath: "/",
project: createScopedDepProject(["sap", "m"], {id: "@sapui5/sap.m", version: "1.136.0"})
});
await dep.write(resourceFactory.createResource({
path: "/resources/sap/m/.library",
string: `<library><name>sap.m</name></library>`
}));
const rootProject = {
getName: () => "myapp", getVersion: () => "1.0.0",
getFrameworkName: () => undefined, getFrameworkVersion: () => undefined
};
const oOptions = {
workspace,
dependencies: dep,
options: {pattern: "/resources/**/.library", rootProject}
};
await assertCreatedVersionInfo(t, {
"name": "myapp",
"scmRevision": "",
"version": "1.0.0",
"framework": {"name": "SAPUI5", "version": ""},
"libraries": [{"name": "sap.m", "scmRevision": "", "version": "1.136.0"}]
}, oOptions);
});

test.serial("task: explicit ui5.yaml config wins over @sapui5/ scan result", async (t) => {
const workspace = createWorkspace();
const dep = resourceFactory.createAdapter({
virBasePath: "/",
project: createScopedDepProject(["sap", "m"], {id: "@sapui5/sap.m", version: "1.136.0"})
});
await dep.write(resourceFactory.createResource({
path: "/resources/sap/m/.library",
string: `<library><name>sap.m</name></library>`
}));
const rootProject = {
getName: () => "myapp", getVersion: () => "1.0.0",
getFrameworkName: () => "OpenUI5", getFrameworkVersion: () => "1.138.0"
};
const oOptions = {
workspace,
dependencies: dep,
options: {pattern: "/resources/**/.library", rootProject}
};
await assertCreatedVersionInfo(t, {
"name": "myapp",
"scmRevision": "",
"version": "1.0.0",
"framework": {"name": "OpenUI5", "version": "1.138.0"},
"libraries": [{"name": "sap.m", "scmRevision": "", "version": "1.136.0"}]
}, oOptions);
});
1 change: 1 addition & 0 deletions packages/server/test/lib/server/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ test("Get sap-ui-version.json from versionInfo middleware (/resources/sap-ui-ver
"version": "1.0.0",
"buildTimestamp": buildTimestamp,
"scmRevision": "",
"framework": {"name": "", "version": ""},
"libraries": [
{
name: "library.a",
Expand Down
1 change: 1 addition & 0 deletions packages/server/test/lib/server/middleware/versionInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ test.serial("integration: Library with dependencies and subcomponent complex sce
"name": "myname",
"scmRevision": "",
"version": "1.33.7",
"framework": {"name": "", "version": ""},
"libraries": [{
"name": "lib.a",
"scmRevision": "",
Expand Down
Loading