Skip to content

Commit ccf3e79

Browse files
authored
Merge branch 'main' into fix/add-new-model-labels
2 parents 6c39450 + 3df1f1a commit ccf3e79

11 files changed

Lines changed: 121 additions & 18 deletions

File tree

.github/workflows/sonar.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
if: ${{ github.event.workflow_run.conclusion == 'success' }}
1212
permissions:
1313
actions: read # Required to download artifacts
14+
pull-requests: write # Required for PR decoration comments
15+
statuses: write # Required for SonarCloud to post quality gate commit status
1416
steps:
1517
- name: 'Checkout project'
1618
uses: actions/checkout@v6
@@ -21,7 +23,7 @@ jobs:
2123

2224
- name: 'Download cached artifact'
2325
if: github.event_name == 'workflow_run'
24-
uses: actions/download-artifact@v5
26+
uses: actions/download-artifact@v8
2527
with:
2628
name: sonar-artifact
2729
run-id: ${{ github.event.workflow_run.id }}

packages/cf-deploy-config-sub-generator/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @sap-ux/cf-deploy-config-sub-generator
22

3+
## 0.2.171
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [45e087a]
8+
- @sap-ux/cf-deploy-config-writer@0.3.99
9+
310
## 0.2.170
411

512
### Patch Changes

packages/cf-deploy-config-sub-generator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@sap-ux/cf-deploy-config-sub-generator",
33
"description": "Generators for configuring Cloud Foundry deployment configuration",
4-
"version": "0.2.170",
4+
"version": "0.2.171",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/SAP/open-ux-tools.git",

packages/cf-deploy-config-writer/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @sap-ux/cf-deploy-config-inquirer
22

3+
## 0.3.99
4+
5+
### Patch Changes
6+
7+
- 45e087a: refactor(cf-deploy-config-writer): consolidate disk-write template rendering into a single module
8+
39
## 0.3.98
410

511
### Patch Changes

packages/cf-deploy-config-writer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@sap-ux/cf-deploy-config-writer",
33
"description": "Add or amend Cloud Foundry and ABAP deployment configuration for SAP projects",
4-
"version": "0.3.98",
4+
"version": "0.3.99",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/SAP/open-ux-tools.git",

packages/cf-deploy-config-writer/src/mta-config/index.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { readFileSync, writeFileSync } from 'node:fs';
21
import { join } from 'node:path';
3-
import { render } from 'ejs';
42
import { MtaConfig } from './mta';
3+
import { renderTemplateToDisk } from './template-renderer';
54
import {
65
addXSSecurityConfig,
76
getTemplatePath,
@@ -84,15 +83,13 @@ export function toMtaModuleName(appId: string): string {
8483
*/
8584
export function createMTA(config: MTABaseConfig): void {
8685
const mtaId = `${config.mtaId.slice(0, MAX_MTA_ID_LENGTH)}`;
87-
const mtaTemplate = readFileSync(getTemplatePath(`app/${FileName.MtaYaml}`), 'utf-8');
88-
const mtaContents = render(mtaTemplate, {
86+
config.mtaId = mtaId;
87+
// Written to disk immediately! Subsequent calls are dependent on it being on the file system i.e mta-lib.
88+
renderTemplateToDisk(`app/${FileName.MtaYaml}`, join(config.mtaPath, FileName.MtaYaml), {
8989
id: mtaId,
9090
mtaDescription: config.mtaDescription ?? MTADescription,
9191
mtaVersion: config.mtaVersion ?? MTAVersion
9292
});
93-
config.mtaId = mtaId;
94-
// Written to disk immediately! Subsequent calls are dependent on it being on the file system i.e mta-lib.
95-
writeFileSync(join(config.mtaPath, FileName.MtaYaml), mtaContents);
9693
LoggerHelper.logger?.debug(t('debug.mtaCreated', { mtaPath: config.mtaPath }));
9794
}
9895

@@ -162,14 +159,12 @@ export function validateMtaConfig(config: CFBaseConfig): void {
162159
* @deprecated This function is deprecated and will be removed in future releases
163160
*/
164161
async function createCAPMTAAppFrontend(config: CAPConfig, fs: Editor): Promise<void> {
165-
const mtaTemplate = readFileSync(getTemplatePath(`frontend/${FileName.MtaYaml}`), 'utf-8');
166-
const mtaContents = render(mtaTemplate, {
162+
// Written to disk immediately! Subsequent calls are dependent on it being on the file system i.e mta-lib.
163+
renderTemplateToDisk(`frontend/${FileName.MtaYaml}`, join(config.mtaPath, FileName.MtaYaml), {
167164
id: `${config.mtaId.slice(0, MAX_MTA_ID_LENGTH)}`,
168165
mtaDescription: config.mtaDescription ?? MTADescription,
169166
mtaVersion: config.mtaVersion ?? MTAVersion
170167
});
171-
// Written to disk immediately! Subsequent calls are dependent on it being on the file system i.e mta-lib.
172-
writeFileSync(join(config.mtaPath, FileName.MtaYaml), mtaContents);
173168
// Add missing configurations
174169
addXSSecurityConfig(config, fs, false);
175170
LoggerHelper.logger?.debug(t('debug.mtaCreated', { mtaPath: config.mtaPath }));

packages/cf-deploy-config-writer/src/mta-config/mta.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { format } from 'node:util';
22
import { dirname, join } from 'node:path';
33
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
4-
import { render } from 'ejs';
54
import { Mta, type mta } from '@sap/mta-lib';
65
import { type Destination, isGenericODataDestination, isAbapEnvironmentOnBtp } from '@sap-ux/btp-utils';
76
import { YamlDocument } from '@sap-ux/yaml';
@@ -46,6 +45,7 @@ import {
4645
type SupportedResources,
4746
RouterModuleType
4847
} from '../types';
48+
import { renderTemplateToDisk } from './template-renderer';
4949

5050
/**
5151
* A class representing interactions with the MTA binary, found at https://sap.github.io/cloud-mta-build-tool/.
@@ -906,8 +906,7 @@ export class MtaConfig {
906906
destinationServiceName: destinationServiceName,
907907
mtaVersion: '1.0.0'
908908
};
909-
const mtaExtTemplate = readFileSync(join(__dirname, `../../templates/app/${FileName.MtaExtYaml}`), 'utf-8');
910-
writeFileSync(mtaExtFilePath, render(mtaExtTemplate, mtaExt));
909+
renderTemplateToDisk(`app/${FileName.MtaExtYaml}`, mtaExtFilePath, mtaExt);
911910
this.log?.info(t('info.mtaExtensionCreated', { appMtaId, mtaExtFile: FileName.MtaExtYaml }));
912911
} else {
913912
// Create an entry in an existing mta extension file
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { readFileSync, writeFileSync } from 'node:fs';
2+
import { render } from 'ejs';
3+
import { getTemplatePath } from '../utils';
4+
5+
/**
6+
* Render an EJS template directly to disk.
7+
* Intentionally bypasses mem-fs — mta-lib and other consumers require the file to be
8+
* physically present on the file system before they can read it back.
9+
*
10+
* @param templateName Template path relative to the `templates/` folder (e.g. `app/mta.yaml`)
11+
* @param outputPath Absolute path where the rendered file will be written
12+
* @param data Template data object passed to EJS
13+
*/
14+
export function renderTemplateToDisk(templateName: string, outputPath: string, data: Record<string, unknown>): void {
15+
const template = readFileSync(getTemplatePath(templateName), 'utf-8');
16+
writeFileSync(outputPath, render(template, data));
17+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { join } from 'node:path';
2+
import * as nodeFs from 'node:fs';
3+
import * as ejs from 'ejs';
4+
import * as utils from '../../src/utils';
5+
import { renderTemplateToDisk } from '../../src/mta-config/template-renderer';
6+
7+
jest.mock('node:fs', () => ({
8+
readFileSync: jest.fn(),
9+
writeFileSync: jest.fn()
10+
}));
11+
12+
jest.mock('ejs', () => ({
13+
render: jest.fn()
14+
}));
15+
16+
jest.mock('../../src/utils', () => ({
17+
getTemplatePath: jest.fn()
18+
}));
19+
20+
describe('renderTemplateToDisk', () => {
21+
const getTemplatePathMock = utils.getTemplatePath as jest.MockedFunction<typeof utils.getTemplatePath>;
22+
const readFileSyncMock = nodeFs.readFileSync as jest.MockedFunction<typeof nodeFs.readFileSync>;
23+
const writeFileSyncMock = nodeFs.writeFileSync as jest.MockedFunction<typeof nodeFs.writeFileSync>;
24+
const renderMock = ejs.render as jest.MockedFunction<typeof ejs.render>;
25+
26+
beforeEach(() => {
27+
jest.clearAllMocks();
28+
});
29+
30+
test('resolves template path, renders, and writes to disk', () => {
31+
const templateName = 'app/mta.yaml';
32+
const outputPath = '/project/mta.yaml';
33+
const data = { id: 'my-mta', mtaVersion: '0.0.1' };
34+
const resolvedTemplatePath = '/dist/templates/app/mta.yaml';
35+
const rawTemplate = '<%= id %>';
36+
const rendered = 'my-mta';
37+
38+
getTemplatePathMock.mockReturnValue(resolvedTemplatePath);
39+
readFileSyncMock.mockReturnValue(rawTemplate as any);
40+
renderMock.mockReturnValue(rendered);
41+
42+
renderTemplateToDisk(templateName, outputPath, data);
43+
44+
expect(getTemplatePathMock).toHaveBeenCalledWith(templateName);
45+
expect(readFileSyncMock).toHaveBeenCalledWith(resolvedTemplatePath, 'utf-8');
46+
expect(renderMock).toHaveBeenCalledWith(rawTemplate, data);
47+
expect(writeFileSyncMock).toHaveBeenCalledWith(outputPath, rendered);
48+
});
49+
50+
test('passes all data properties to the template renderer', () => {
51+
const data = { id: 'mta-id', mtaDescription: 'desc', mtaVersion: '1.0.0' };
52+
getTemplatePathMock.mockReturnValue('/tmpl');
53+
readFileSyncMock.mockReturnValue('tmpl' as any);
54+
renderMock.mockReturnValue('rendered');
55+
56+
renderTemplateToDisk('some/template.yaml', '/out.yaml', data);
57+
58+
expect(renderMock).toHaveBeenCalledWith('tmpl', data);
59+
});
60+
61+
test('uses output path directly for writeFileSync', () => {
62+
const outputPath = join('/nested', 'deep', 'output.yaml');
63+
getTemplatePathMock.mockReturnValue('/tmpl');
64+
readFileSyncMock.mockReturnValue('t' as any);
65+
renderMock.mockReturnValue('out');
66+
67+
renderTemplateToDisk('t', outputPath, {});
68+
69+
expect(writeFileSyncMock).toHaveBeenCalledWith(outputPath, 'out');
70+
});
71+
});

packages/deploy-config-sub-generator/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @sap-ux/deploy-config-sub-generator
22

3+
## 0.5.144
4+
5+
### Patch Changes
6+
7+
- @sap-ux/cf-deploy-config-sub-generator@0.2.171
8+
39
## 0.5.143
410

511
### Patch Changes

0 commit comments

Comments
 (0)