Skip to content

Commit

Permalink
[cascading] from release/11.1.0-rc to main (#2084)
Browse files Browse the repository at this point in the history
<!--
{"currentBranch":"release/11.1.0-rc","targetBranch":"main","bypassReviewers":true,"isConflicting":false}
-->

## Cascading from release/11.1.0-rc to main

The configuration requests the cascading to bypass reviewer in case of
CI success.
To not bypass the reviewing process, please check the following
checkbox:

- [ ] <!-- !cancel bypass! --> 🚫 stop reviewing process
bypass for this Pull Request

---

<small>This Pull Request has been generated with ❤️ by the
[Otter](https://github.com/AmadeusITGroup/otter) cascading tool.</small>
  • Loading branch information
matthieu-crouzet authored Aug 23, 2024
2 parents b9ad713 + f349814 commit e48e4a2
Show file tree
Hide file tree
Showing 41 changed files with 861 additions and 143 deletions.
55 changes: 55 additions & 0 deletions docs/cms-adapters/CMS_ADAPTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,58 @@ Make sure to expose them in the bundled application by adding them in the `files
```

Also make sure to place them in a folder name `migration-scripts` in your packaged app or to set the `migrationScriptFolder` in your `cms.json`.

### Case of dependencies on libraries

If the libraries that you use provide migration scripts, you need to aggregate them with your own to make the metadata-checks pass.

You will need to specify in your migration script those libraries with their current version.

```json5
{
"$schema": "https://raw.githubusercontent.com/AmadeusITGroup/otter/main/packages/@o3r/extractors/schemas/migration.metadata.schema.json",
"version": "10.0.0",
// List of libraries with migration scripts that your project depend on
"libraries": {
"@mylib/lib": "1.0.0"
},
"changes": [
// The changes specific to your project
]
}
```

Then you can automate the aggregation of migration scripts by adding the `@o3r/extractors:aggregate-migration-scripts` builder in your `angular.json` file as follows:
```json5
{
// ...,
"projects": {
// ...,
"<project-name>": {
// ...,
"architect": {
"aggregate-migration-scripts": {
"builder": "@o3r/extractors:aggregate-migration-scripts",
"options": {
"migrationDataPath": "./migration-scripts/src/MIGRATION-*.json",
"outputDirectory": "./migration-scripts/dist"
}
},
"check-localization-migration-metadata": {
"builder": "@o3r/localization:check-localization-migration-metadata",
"options": {
"migrationDataPath": "./migration-scripts/dist/MIGRATION-*.json"
}
}
}
}
}
}
```

Calling the `aggregate-migration-scripts` builder will generate the full migration-scripts including the ones from the libraries.

In the previous example, you should include the output of the aggregate in the packaged application instead of the original migration scripts (`./migration-scripts/dist` instead of `./mìgration-scripts/src`).

> [!WARNING]
> The migration scripts of the libraries need be placed in the `./migration-scripts/` folder at the root the library package.
49 changes: 16 additions & 33 deletions packages/@o3r/components/builders/metadata-check/index.it.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ import {
getLatestPackageVersion,
packageManagerAdd,
packageManagerExec,
packageManagerPublish,
packageManagerVersion
packageManagerVersion,
publishToVerdaccio
} from '@o3r/test-helpers';
import { execFileSync } from 'node:child_process';
import { promises, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { existsSync, promises, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { inc } from 'semver';
import type { ComponentConfigOutput, ConfigProperty } from '@o3r/components';
import type { MigrationConfigData } from './helpers/config-metadata-comparison.helper';
import { getExternalDependenciesVersionRange } from '@o3r/schematics';

const baseVersion = '1.2.0';
const version = '1.3.0';
const migrationDataFileName = `MIGRATION-${version}.json`;
const migrationDataFileName = `migration-scripts/MIGRATION-${version}.json`;
const metadataFileName = 'component.config.metadata.json';

const defaultMigrationData: MigrationFile<MigrationConfigData> = {
Expand Down Expand Up @@ -184,8 +183,11 @@ const newConfigurationMetadata: ComponentConfigOutput[] = [
createConfig('@new/lib9', 'MyConfig9', ['prop9'])
];

function writeFileAsJSON(path: string, content: object) {
return promises.writeFile(path, JSON.stringify(content), { encoding: 'utf8' });
async function writeFileAsJSON(path: string, content: object) {
if (!existsSync(dirname(path))) {
await promises.mkdir(dirname(path), {recursive: true});
}
await promises.writeFile(path, JSON.stringify(content), { encoding: 'utf8' });
}

const initTest = async (
Expand All @@ -197,8 +199,8 @@ const initTest = async (
const { workspacePath, appName, applicationPath, o3rVersion, isYarnTest } = o3rEnvironment.testEnvironment;
const execAppOptions = { ...getDefaultExecSyncOptions(), cwd: applicationPath };
const execAppOptionsWorkspace = { ...getDefaultExecSyncOptions(), cwd: workspacePath };
packageManagerAdd(`@o3r/components@${o3rVersion}`, execAppOptionsWorkspace);
packageManagerAdd(`@o3r/extractors@${o3rVersion}`, execAppOptionsWorkspace);
packageManagerExec({script: 'ng', args: ['add', `@o3r/extractors@${o3rVersion}`, '--skip-confirmation', '--project-name', appName]}, execAppOptionsWorkspace);
packageManagerExec({script: 'ng', args: ['add', `@o3r/components@${o3rVersion}`, '--skip-confirmation', '--project-name', appName]}, execAppOptionsWorkspace);
const versions = getExternalDependenciesVersionRange([
'semver',
...(isYarnTest ? [
Expand All @@ -212,6 +214,7 @@ const initTest = async (
warn: jest.fn()
} as any);
Object.entries(versions).forEach(([pkgName, pkgVersion]) => packageManagerAdd(`${pkgName}@${pkgVersion}`, execAppOptionsWorkspace));
const npmIgnorePath = join(applicationPath, '.npmignore');
const packageJsonPath = join(applicationPath, 'package.json');
const angularJsonPath = join(workspacePath, 'angular.json');
const metadataPath = join(applicationPath, metadataFileName);
Expand All @@ -223,7 +226,7 @@ const initTest = async (
builder: '@o3r/components:check-config-migration-metadata',
options: {
allowBreakingChanges,
migrationDataPath: `**/MIGRATION-*.json`
migrationDataPath: `apps/test-app/migration-scripts/MIGRATION-*.json`
}
};
angularJson.projects[appName].architect['check-metadata'] = builderConfig;
Expand All @@ -238,6 +241,7 @@ const initTest = async (
private: false
};
await writeFileAsJSON(packageJsonPath, packageJson);
await promises.writeFile(npmIgnorePath, '');

// Set old metadata and publish to registry
await writeFileAsJSON(metadataPath, previousConfigurationMetadata);
Expand All @@ -254,7 +258,7 @@ const initTest = async (
const args = getPackageManager() === 'yarn' ? [] : ['--no-git-tag-version', '-f'];
packageManagerVersion(bumpedVersion, args, execAppOptions);

packageManagerPublish([], execAppOptions);
await publishToVerdaccio(execAppOptions);

// Override with new metadata for comparison
await writeFileAsJSON(metadataPath, newMetadata);
Expand All @@ -264,27 +268,6 @@ const initTest = async (
};

describe('check metadata migration', () => {
beforeEach(async () => {
const { applicationPath } = o3rEnvironment.testEnvironment;
const execAppOptions = { ...getDefaultExecSyncOptions(), cwd: applicationPath, shell: true };
await promises.copyFile(
join(__dirname, '..', '..', '..', '..', '..', '.verdaccio', 'conf', '.npmrc'),
join(applicationPath, '.npmrc')
);
execFileSync('npx', [
'--yes',
'npm-cli-login',
'-u',
'verdaccio',
'-p',
'verdaccio',
'-e',
'[email protected]',
'-r',
'http://127.0.0.1:4873'
], execAppOptions);
});

test('should not throw', async () => {
await initTest(
true,
Expand Down
4 changes: 1 addition & 3 deletions packages/@o3r/components/schematics/cms-adapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ function updateCmsAdapterFn(options: { projectName?: string | undefined }): Rule
/**
* Add cms extractors builder into the angular.json
* @param tree
* @param _context
* @param context
*/
const editAngularJson = (tree: Tree, context: SchematicContext) => {
Expand All @@ -42,7 +41,7 @@ function updateCmsAdapterFn(options: { projectName?: string | undefined }): Rule
workspaceProject.architect['check-config-migration-metadata'] ||= {
builder: '@o3r/components:check-config-migration-metadata',
options: {
migrationDataPath: 'MIGRATION-*.json'
migrationDataPath: 'migration-scripts/dist/MIGRATION-*.json'
}
};

Expand All @@ -54,7 +53,6 @@ function updateCmsAdapterFn(options: { projectName?: string | undefined }): Rule
/**
* Add cms extractors scripts into the package.json
* @param tree
* @param _context
* @param context
*/
const addExtractorsScripts = (tree: Tree, context: SchematicContext) => {
Expand Down
6 changes: 4 additions & 2 deletions packages/@o3r/components/schematics/index.it.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ describe('ng add components', () => {
expect(diff.added).toContain('apps/test-app/cms.json');
expect(diff.added).toContain('apps/test-app/placeholders.metadata.json');
expect(diff.added).toContain('apps/test-app/tsconfig.cms.json');
expect(diff.added.length).toBe(3);
expect(diff.added).toContain('apps/test-app/migration-scripts/README.md');
expect(diff.added.length).toBe(4);

[libraryPath, ...untouchedProjectsPaths].forEach(untouchedProject => {
expect(diff.all.some(file => file.startsWith(path.posix.relative(workspacePath, untouchedProject)))).toBe(false);
Expand All @@ -53,7 +54,8 @@ describe('ng add components', () => {
expect(diff.added).toContain('libs/test-lib/cms.json');
expect(diff.added).toContain('libs/test-lib/placeholders.metadata.json');
expect(diff.added).toContain('libs/test-lib/tsconfig.cms.json');
expect(diff.added.length).toBe(3);
expect(diff.added).toContain('libs/test-lib/migration-scripts/README.md');
expect(diff.added.length).toBe(4);

[applicationPath, ...untouchedProjectsPaths].forEach(untouchedProject => {
expect(diff.all.some(file => file.startsWith(path.posix.relative(workspacePath, untouchedProject)))).toBe(false);
Expand Down
10 changes: 10 additions & 0 deletions packages/@o3r/extractors/builders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/angular/angular-cli/master/packages/angular_devkit/architect/src/builders-schema.json",
"builders": {
"aggregate-migration-scripts": {
"implementation": "./builders/aggregate-migration-scripts/",
"schema": "./builders/aggregate-migration-scripts/schema.json",
"description": "Aggregate migration scripts"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Test environment exported by O3rEnvironment, must be first line of the file
* @jest-environment @o3r/test-helpers/jest-environment
* @jest-environment-o3r-app-folder test-app-extractors-aggregate-migration-scripts
*/
const o3rEnvironment = globalThis.o3rEnvironment;

import {
getDefaultExecSyncOptions, getLatestPackageVersion,
packageManagerAdd,
packageManagerExec,
publishToVerdaccio
} from '@o3r/test-helpers';
import { promises, readFileSync } from 'node:fs';
import { join, relative } from 'node:path';
import { inc } from 'semver';

const migrationDataMocksPath = join(__dirname, '..', '..', 'testing', 'mocks', 'migration-scripts');

function writeFileAsJSON(path: string, content: object) {
return promises.writeFile(path, JSON.stringify(content), { encoding: 'utf8' });
}

async function expectFileToMatchMock(realPath: string, mockPath: string) {
expect(await promises.readFile(realPath, {encoding: 'utf8'})).toEqual(await promises.readFile(mockPath, {encoding: 'utf8'}));
}

async function publishLibrary(cwd: string) {
const libraryPath = join(cwd, 'mylib');
const execAppOptions = {...getDefaultExecSyncOptions(), cwd: libraryPath};
const libMigrationDataPath = join(libraryPath, 'migration-scripts');
await promises.mkdir(libMigrationDataPath, {recursive: true});

let latestVersion;
try {
latestVersion = getLatestPackageVersion('@o3r/my-lib', {
...execAppOptions,
registry: o3rEnvironment.testEnvironment.registry
});
} catch {
latestVersion = '4.0.0';
}
const bumpedVersion = inc(latestVersion, 'patch');

await promises.writeFile(join(libraryPath, 'package.json'), `{"name": "@o3r/my-lib", "version": "${bumpedVersion}"}`);
await promises.copyFile(join(migrationDataMocksPath, 'lib', 'migration-2.0.json'), join(libMigrationDataPath, 'migration-2.0.json'));
await promises.copyFile(join(migrationDataMocksPath, 'lib', 'migration-2.5.json'), join(libMigrationDataPath, 'migration-2.5.json'));
await promises.copyFile(join(migrationDataMocksPath, 'lib', 'migration-4.0.json'), join(libMigrationDataPath, 'migration-4.0.json'));

await publishToVerdaccio(execAppOptions);
}

describe('aggregate migration scripts', () => {
let migrationDataSrcPath: string;
let migrationDataDestPath: string;

beforeEach(async () => {
const { workspacePath, appName, applicationPath } = o3rEnvironment.testEnvironment;
const angularJsonPath = join(workspacePath, 'angular.json');
migrationDataSrcPath = join(applicationPath, 'migration-scripts', 'src');
migrationDataDestPath = join(applicationPath, 'migration-scripts', 'dist');
// Add builder options
const angularJson = JSON.parse(readFileSync(angularJsonPath, { encoding: 'utf8' }).toString());
const builderConfig = {
builder: '@o3r/extractors:aggregate-migration-scripts',
options: {
migrationDataPath: relative(workspacePath, `${migrationDataSrcPath}/migration-*.json`).replace(/[\\/]/g, '/'),
outputDirectory: relative(workspacePath, migrationDataDestPath).replace(/[\\/]/g, '/')
}
};
angularJson.projects[appName].architect['aggregate-migration-scripts'] = builderConfig;
await writeFileAsJSON(angularJsonPath, angularJson);

await publishLibrary(workspacePath);
});

test('should create migration scripts including lib content', async () => {
const { workspacePath, appName, applicationPath, o3rExactVersion } = o3rEnvironment.testEnvironment;
const execAppOptions = { ...getDefaultExecSyncOptions(), cwd: applicationPath };
const execAppOptionsWorkspace = { ...getDefaultExecSyncOptions(), cwd: workspacePath };

packageManagerExec({script: 'ng', args: ['add', `@o3r/extractors@${o3rExactVersion}`, '--skip-confirmation', '--project-name', appName]}, execAppOptionsWorkspace);
packageManagerAdd('@o3r/my-lib', execAppOptionsWorkspace);
packageManagerAdd('@o3r/my-lib', execAppOptions);

await promises.mkdir(migrationDataSrcPath, {recursive: true});
await promises.copyFile(join(migrationDataMocksPath, 'migration-1.0.json'), join(migrationDataSrcPath, 'migration-1.0.json'));
await promises.copyFile(join(migrationDataMocksPath, 'migration-1.5.json'), join(migrationDataSrcPath, 'migration-1.5.json'));
await promises.copyFile(join(migrationDataMocksPath, 'migration-2.0.json'), join(migrationDataSrcPath, 'migration-2.0.json'));

expect(() => packageManagerExec({ script: 'ng', args: ['run', `${appName}:aggregate-migration-scripts`] }, execAppOptionsWorkspace)).not.toThrow();
await expectFileToMatchMock(`${migrationDataDestPath}/migration-1.0.json`, `${migrationDataMocksPath}/expected/migration-1.0.json`);
await expectFileToMatchMock(`${migrationDataDestPath}/migration-1.5.json`, `${migrationDataMocksPath}/expected/migration-1.5.json`);
await expectFileToMatchMock(`${migrationDataDestPath}/migration-2.0.json`, `${migrationDataMocksPath}/expected/migration-2.0.json`);
});
});
Loading

0 comments on commit e48e4a2

Please sign in to comment.