-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new builders for metadata migration check
- Loading branch information
Showing
30 changed files
with
1,032 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
...ages/@o3r/components/builders/metadata-check/helpers/config-metadata-comparison.helper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { ComponentConfigOutput } from '@o3r/components'; | ||
import { MetadataComparator } from '@o3r/extractors'; | ||
|
||
/** | ||
* Interface describing a config migration element | ||
*/ | ||
export interface MigrationConfigData { | ||
libraryName: string; | ||
configName: string; | ||
propertyName?: string; | ||
} | ||
|
||
/** | ||
* Comparator used to compare one version of config metadata with another | ||
*/ | ||
export class ConfigMetadataComparator implements MetadataComparator<ComponentConfigOutput> { | ||
/** | ||
* Returns an array of config metadata from a metadata file. | ||
* To be easily parseable, the properties will be split in separate items of the array. | ||
* e.g. : [{ library: '@o3r/demo', properties: [{name : 'property1', type: 'string'}, {name : 'property2', type: 'number'}] }] | ||
* will become : | ||
* [{ library: '@o3r/demo', properties: [{name : 'property1', type: 'string'}] }, { library: '@o3r/demo', properties: [{name : 'property2', type: 'number'}] }] | ||
* | ||
* @param content Content of a migration metadata files | ||
*/ | ||
public getArray(content: ComponentConfigOutput[]): ComponentConfigOutput[] { | ||
return content.reduce((acc, config) => { | ||
if (config.properties.length) { | ||
const propertiesConfigs = config.properties.map((property) => { | ||
return { | ||
...config, | ||
properties: [property] | ||
}; | ||
}); | ||
acc.push(...propertiesConfigs); | ||
} else { | ||
acc.push(config); | ||
} | ||
return acc; | ||
}, [] as ComponentConfigOutput[]); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public getName(config: ComponentConfigOutput): string { | ||
return `${config.library}#${config.name}` + (config.properties.length ? ` ${config.properties[0].name}` : ''); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public isSame(config1: ComponentConfigOutput, config2: ComponentConfigOutput): boolean { | ||
return config1.name === config2.name && config1.library === config2.library | ||
&& config1.properties[0]?.name === config2.properties[0]?.name; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public isMigrationDataMatch(config: ComponentConfigOutput, migrationData: MigrationConfigData): boolean { | ||
return migrationData.configName === config.name && migrationData.libraryName === config.library | ||
&& (!migrationData.propertyName || config.properties[0]?.name === migrationData.propertyName); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
packages/@o3r/components/builders/metadata-check/helpers/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './config-metadata-comparison.helper'; |
194 changes: 194 additions & 0 deletions
194
packages/@o3r/components/builders/metadata-check/index.it.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
/** | ||
* 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-components-metadata-check | ||
* @jest-environment-o3r-type o3r-project-with-app | ||
*/ | ||
const o3rEnvironment = globalThis.o3rEnvironment; | ||
|
||
import { getPackageManager } from '@o3r/schematics'; | ||
import { | ||
getDefaultExecSyncOptions, | ||
packageManagerAdd, | ||
packageManagerExec, | ||
packageManagerInfo, | ||
packageManagerPublish, | ||
packageManagerVersion | ||
} from '@o3r/test-helpers'; | ||
import type { ExecSyncOptions } from 'node:child_process'; | ||
import { existsSync, readFileSync, writeFileSync } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
import { coerce, inc } from 'semver'; | ||
|
||
// TODO : change when mechanism is cleaned up | ||
const migrationDataName = 'MIGRATION-1.3.json'; | ||
// const migrationDataPath = '.'; | ||
const metadataFileName = 'component.config.metadata.json'; | ||
const baseVersion = '1.2.0'; | ||
|
||
const migrationData = [ | ||
{ | ||
contentType: 'CONFIG', | ||
before: { | ||
libraryName: '@o3r/test', | ||
configName: 'RenamedTestConfig', | ||
propertyName: 'Property1' | ||
}, | ||
after: { | ||
libraryName: '@o3r/test', | ||
configName: 'NewTestConfig', | ||
propertyName: 'Property3' | ||
} | ||
}, | ||
{ | ||
contentType: 'CONFIG', | ||
before: { | ||
libraryName: '@o3r/test', | ||
configName: 'RemovedTestConfig' | ||
} | ||
} | ||
]; | ||
|
||
const previousConfigurationMetadata = [ | ||
{ | ||
library: '@o3r/test', | ||
description: 'Test renamed config', | ||
name: 'RenamedTestConfig', | ||
properties: [ | ||
{ | ||
description: '', | ||
name: 'Property1', | ||
type: 'string', | ||
value: 'test' | ||
}, | ||
{ | ||
description: '', | ||
name: 'Property2', | ||
type: 'boolean', | ||
value: false | ||
} | ||
] | ||
}, | ||
{ | ||
library: '@o3r/test', | ||
description: 'Test removed config', | ||
name: 'RemovedTestConfig', | ||
properties: [ | ||
{ | ||
description: '', | ||
name: 'Property', | ||
type: 'boolean', | ||
value: true | ||
} | ||
] | ||
} | ||
]; | ||
|
||
const newConfigurationMetadata = [ | ||
{ | ||
library: '@o3r/test', | ||
description: 'Test renamed config', | ||
name: 'NewTestConfig', | ||
properties: [ | ||
{ | ||
description: '', | ||
name: 'Property1', | ||
type: 'string', | ||
value: 'test' | ||
}, | ||
{ | ||
description: '', | ||
name: 'Property2', | ||
type: 'boolean', | ||
value: false | ||
} | ||
] | ||
} | ||
]; | ||
|
||
function writeFileAsJSON(path: string, content: object): void { | ||
writeFileSync(path, JSON.stringify(content), { encoding: 'utf8' }); | ||
} | ||
|
||
function getLatestPackageVersion(packageRef: string, execOptions: ExecSyncOptions): string | undefined { | ||
try { | ||
const result = packageManagerInfo(packageRef, ['--json'], execOptions); | ||
const latestVersion = JSON.parse(result)['dist-tags']?.latest; | ||
return latestVersion && coerce(latestVersion).toString() || undefined; | ||
} catch (error) { | ||
return undefined; | ||
} | ||
} | ||
|
||
describe('check metadata migration', () => { | ||
test('check metadata changes', () => { | ||
const { workspacePath, projectName, projectPath, o3rVersion } = o3rEnvironment.testEnvironment; | ||
const execAppOptions = { ...getDefaultExecSyncOptions(), cwd: projectPath }; | ||
const execAppOptionsWorkspace = { ...getDefaultExecSyncOptions(), cwd: workspacePath }; | ||
packageManagerAdd(`@o3r/components@${o3rVersion}`, execAppOptionsWorkspace); | ||
packageManagerAdd(`@o3r/extractors@${o3rVersion}`, execAppOptionsWorkspace); | ||
const packageJsonPath = join(projectPath, 'package.json'); | ||
const angularJsonPath = join(workspacePath, 'angular.json'); | ||
const metadataPath = join(projectPath, metadataFileName); | ||
const migrationDataPath = join(projectPath, migrationDataName); | ||
|
||
// Add builder options | ||
const angularJson = existsSync(angularJsonPath) ? JSON.parse(readFileSync(angularJsonPath, { encoding: 'utf8' }).toString()) : undefined; | ||
const builderConfig = { | ||
builder: '@o3r/components:check-config-migration-metadata', | ||
options: { | ||
granularity: 'minor', | ||
metadataPath: './component.config.metadata.json', | ||
migrationDataPath: projectPath | ||
} | ||
}; | ||
angularJson.projects[projectName].architect['check-metadata'] = builderConfig; | ||
writeFileAsJSON(angularJsonPath, angularJson); | ||
|
||
// Add scope to project for registry management | ||
let packageJson = existsSync(packageJsonPath) ? JSON.parse(readFileSync(packageJsonPath, { encoding: 'utf8' }).toString()) : undefined; | ||
const packageName = `@o3r/${packageJson.name}`; | ||
packageJson = { | ||
...packageJson, | ||
name: packageName, | ||
private: false | ||
}; | ||
writeFileAsJSON(packageJsonPath, packageJson); | ||
|
||
// Set old metadata and publish to registry | ||
writeFileAsJSON(metadataPath, previousConfigurationMetadata); | ||
|
||
const bumpedVersion = inc(getLatestPackageVersion(packageName, execAppOptionsWorkspace) || baseVersion, 'patch'); | ||
|
||
const args = getPackageManager() === 'yarn' ? [] : ['--no-git-tag-version', '-f']; | ||
packageManagerVersion(bumpedVersion, args, execAppOptions); | ||
packageManagerPublish(execAppOptions); | ||
|
||
// Override with new metadata for comparison | ||
writeFileAsJSON(metadataPath, newConfigurationMetadata); | ||
writeFileAsJSON(migrationDataPath, migrationData); | ||
|
||
try { | ||
packageManagerExec({ script: 'ng', args: ['run', `${projectName}:check-metadata`] }, execAppOptionsWorkspace); | ||
throw new Error('Should have thrown'); | ||
} catch (error: any) { | ||
/* eslint-disable jest/no-conditional-expect */ | ||
expect(error.message).not.toBe('Should have thrown'); | ||
|
||
// eslint-disable-next-line no-control-regex | ||
const ansiFormatting = /\[(\d+;)?\d+m|\x1B/g; | ||
const errorLine = /^Property.*/; | ||
const errors = (error.message.split('\n') as string[]) | ||
.map((s) => s.replaceAll(ansiFormatting, '')) | ||
.filter((s) => s.match(errorLine)); | ||
|
||
// Errors are duplicated in the output so we need to unify them | ||
const uniqueErrorsNumber = new Set(errors).size; | ||
|
||
expect(uniqueErrorsNumber).toBe(2); | ||
expect(errors).toContain('Property @o3r/test#RenamedTestConfig Property1 has been modified but the new property is not present in the new metadata'); | ||
expect(errors).toContain('Property @o3r/test#RenamedTestConfig Property2 has been modified but is not documented in the migration document'); | ||
/* eslint-enable jest/no-conditional-expect */ | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { BuilderOutput, createBuilder } from '@angular-devkit/architect'; | ||
import { checkMetadataBuilder, createBuilderWithMetricsIfInstalled } from '@o3r/extractors'; | ||
import { ConfigMetadataComparator } from './helpers/config-metadata-comparison.helper'; | ||
import type { ConfigMigrationMetadataCheckBuilderSchema } from './schema'; | ||
|
||
export default createBuilder<ConfigMigrationMetadataCheckBuilderSchema>(createBuilderWithMetricsIfInstalled((options, context): Promise<BuilderOutput> => { | ||
return checkMetadataBuilder(options, context, new ConfigMetadataComparator()); | ||
})); |
37 changes: 37 additions & 0 deletions
37
packages/@o3r/components/builders/metadata-check/schema.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema", | ||
"type": "object", | ||
"$id": "ConfigMigrationMetadataCheckBuilderSchema", | ||
"title": "Check config migration metadata builder", | ||
"description": "", | ||
"properties": { | ||
"migrationDataPath": { | ||
"type": ["string", "array"], | ||
"items": { | ||
"type": "string" | ||
}, | ||
"description": "Glob of the migration files to use." | ||
}, | ||
"granularity": { | ||
"type": "string", | ||
"description": "Granularity of the migration check.", | ||
"default": "minor" | ||
}, | ||
"allowBreakingChanges": { | ||
"type": "boolean", | ||
"description": "Are breaking changes allowed.", | ||
"default": false | ||
}, | ||
"packageManager": { | ||
"type": "string", | ||
"description": "Override of the package manager, otherwise it will be computed from the project setup." | ||
}, | ||
"metadataPath": { | ||
"type": "string", | ||
"description": "Path of the config metadata file.", | ||
"default": "./component.config.metadata.json" | ||
} | ||
}, | ||
"additionalProperties": false, | ||
"required": ["migrationDataPath"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { MigrationMetadataCheckBuilderOptions } from '@o3r/extractors'; | ||
|
||
/** Migration metadata check builder schema */ | ||
export interface ConfigMigrationMetadataCheckBuilderSchema extends MigrationMetadataCheckBuilderOptions { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.