Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(config-migration): invoke applyPrettierFormatting at the commit stage #18150

Merged
merged 20 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ba8b19c
move applyPrettierFormatting to the commiting phase
Gabriel-Ladzaretti Oct 5, 2022
9f7a640
apply code review suggestions
Gabriel-Ladzaretti Oct 6, 2022
e66019b
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 7, 2022
5c371c8
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 9, 2022
5d543aa
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 11, 2022
2fc0144
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 14, 2022
2a3348a
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 15, 2022
64583a1
extract applyPrettier to the global scope
Gabriel-Ladzaretti Oct 15, 2022
04cbc97
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 16, 2022
05c724e
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 23, 2022
99c92a9
add wrapper function for applyPrettierFormatting
Gabriel-Ladzaretti Oct 23, 2022
862d9de
update migrated-data.spec.ts
Gabriel-Ladzaretti Oct 25, 2022
f5945c4
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Oct 29, 2022
ef865fa
add comments to and rename stripWhitespaces helper func
Gabriel-Ladzaretti Nov 2, 2022
42990de
revert getAsync rename
Gabriel-Ladzaretti Nov 8, 2022
3069291
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Nov 8, 2022
d7c3bcb
Merge branch 'main' into migration-prettier
rarkins Nov 8, 2022
0837ec2
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Nov 13, 2022
90cab25
Merge branch 'main' into migration-prettier
Gabriel-Ladzaretti Nov 15, 2022
3f93edf
Merge branch 'main' into migration-prettier
rarkins Nov 16, 2022
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
29 changes: 29 additions & 0 deletions lib/workers/repository/config-migration/branch/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { RenovateConfig, getConfig, platform } from '../../../../../test/util';
import { checkoutBranch, commitFiles } from '../../../../util/git';
import { createConfigMigrationBranch } from './create';
import type { MigratedData } from './migrated-data';
import { MigratedDataFactory } from './migrated-data';

jest.mock('../../../../util/git');

const formattedMigratedData = Fixtures.getJson(
'./migrated-data-formatted.json'
);

describe('workers/repository/config-migration/branch/create', () => {
const raw = Fixtures.getJson('./renovate.json');
const indent = ' ';
Expand All @@ -24,6 +29,11 @@ describe('workers/repository/config-migration/branch/create', () => {
});

describe('createConfigMigrationBranch', () => {
const prettierSpy = jest.spyOn(
MigratedDataFactory,
'applyPrettierFormatting'
);

it('applies the default commit message', async () => {
await createConfigMigrationBranch(config, migratedConfigData);
expect(checkoutBranch).toHaveBeenCalledWith(config.defaultBranch);
Expand Down Expand Up @@ -178,6 +188,25 @@ describe('workers/repository/config-migration/branch/create', () => {
platformCommit: false,
});
});

it('applies prettier formatting to the committed content', async () => {
const formatted = formattedMigratedData.content;
prettierSpy.mockResolvedValueOnce(formattedMigratedData.content);
await createConfigMigrationBranch(config, migratedConfigData);
expect(checkoutBranch).toHaveBeenCalledWith(config.defaultBranch);
expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: formatted,
},
],
message: 'Migrate config renovate.json',
platformCommit: false,
});
});
});
});
});
7 changes: 6 additions & 1 deletion lib/workers/repository/config-migration/branch/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { commitAndPush } from '../../../../modules/platform/commit';
import { checkoutBranch } from '../../../../util/git';
import { getMigrationBranchName } from '../common';
import { ConfigMigrationCommitMessageFactory } from './commit-message';
import { MigratedDataFactory } from './migrated-data';
import type { MigratedData } from './migrated-data';

export async function createConfigMigrationBranch(
config: Partial<RenovateConfig>,
migratedConfigData: MigratedData
): Promise<string | null> {
logger.debug('createConfigMigrationBranch()');
const contents = migratedConfigData.content;
const configFileName = migratedConfigData.filename;
logger.debug('Creating config migration branch');

Expand All @@ -30,6 +30,11 @@ export async function createConfigMigrationBranch(
}

await checkoutBranch(config.defaultBranch!);
let contents = migratedConfigData.content;
const prettified = await MigratedDataFactory.applyPrettierFormatting();
if (prettified) {
viceice marked this conversation as resolved.
Show resolved Hide resolved
contents = prettified;
}
return commitAndPush({
branchName: getMigrationBranchName(config),
files: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { logger } from '../../../../logger';
import { readLocalFile } from '../../../../util/fs';
import { getFileList } from '../../../../util/git';
import { detectRepoFileConfig } from '../../init/merge';
import { MigratedDataFactory, applyPrettierFormatting } from './migrated-data';
import { MigratedDataFactory } from './migrated-data';

jest.mock('../../../../config/migration');
jest.mock('../../../../util/git');
Expand Down Expand Up @@ -41,7 +41,6 @@ describe('workers/repository/config-migration/branch/migrated-data', () => {
isMigrated: true,
migratedConfig: migratedConfigObj,
});
mockedFunction(getFileList).mockResolvedValue([]);
});

it('Calls getAsync a first when migration not needed', async () => {
Expand Down Expand Up @@ -119,49 +118,82 @@ describe('workers/repository/config-migration/branch/migrated-data', () => {
'MigratedDataFactory.getAsync() Error initializing renovate MigratedData'
);
});
});

it('format and migrate a JSON config file', async () => {
describe(' MigratedDataFactory.applyPrettierFormatting', () => {
beforeAll(() => {
mockedFunction(detectIndent).mockReturnValueOnce({
type: 'space',
amount: 2,
indent: ' ',
});
mockedFunction(detectRepoFileConfig).mockResolvedValueOnce({
configFileName: 'renovate.json',
configFileRaw: rawNonMigrated,
});
mockedFunction(migrateConfig).mockReturnValueOnce({
isMigrated: true,
migratedConfig: migratedConfigObj,
});
mockedFunction(getFileList).mockResolvedValue(['.prettierrc']);
MigratedDataFactory.reset();
await expect(MigratedDataFactory.getAsync()).resolves.toEqual(
formattedMigratedData
);
});

it('should not stop run for invalid package.json', async () => {
mockedFunction(detectRepoFileConfig).mockResolvedValueOnce({
configFileName: 'renovate.json',
configFileRaw: 'abci',
});
mockedFunction(readLocalFile).mockResolvedValueOnce(rawNonMigrated);
MigratedDataFactory.reset();
await expect(MigratedDataFactory.getAsync()).resolves.toEqual(
migratedData
);
beforeEach(() => {
mockedFunction(getFileList).mockResolvedValue([]);
});

it('should not stop run for readLocalFile error', async () => {
mockedFunction(detectRepoFileConfig).mockResolvedValueOnce({
configFileName: 'renovate.json',
configFileRaw: 'abci',
});
it('returns null if invoked before factory is initialized', async () => {
await expect(
MigratedDataFactory.applyPrettierFormatting()
).resolves.toBeNull();
});

it('does not format when no prettier config is present', async () => {
const unformatted = migratedData.content;
mockedFunction(readLocalFile).mockResolvedValueOnce(null);
await MigratedDataFactory.getAsync();
await expect(
MigratedDataFactory.applyPrettierFormatting()
).resolves.toEqual(unformatted);
});

it('does not format when failing to fetch package.json file', async () => {
const unformatted = migratedData.content;
mockedFunction(readLocalFile).mockRejectedValueOnce(null);
MigratedDataFactory.reset();
await expect(MigratedDataFactory.getAsync()).resolves.toEqual(
migratedData
);
await MigratedDataFactory.getAsync();
await expect(
MigratedDataFactory.applyPrettierFormatting()
).resolves.toEqual(unformatted);
});

it('does not format when there is an invalid package.json file', async () => {
const unformatted = migratedData.content;
mockedFunction(readLocalFile).mockResolvedValueOnce('invalid json');
await MigratedDataFactory.getAsync();
await expect(
MigratedDataFactory.applyPrettierFormatting()
).resolves.toEqual(unformatted);
});

it('formats when prettier config file is found', async () => {
const formatted = formattedMigratedData.content;
mockedFunction(getFileList).mockResolvedValue(['.prettierrc']);
await MigratedDataFactory.getAsync();
await expect(
MigratedDataFactory.applyPrettierFormatting()
).resolves.toEqual(formatted);
});

it('return original content if its invalid', async () => {
it('formats when finds prettier config inside the package.json file', async () => {
const formatted = formattedMigratedData.content;
mockedFunction(detectRepoFileConfig).mockResolvedValueOnce({
configFileName: 'renovate.json',
});
mockedFunction(readLocalFile).mockResolvedValueOnce('{"prettier":{}}');
await MigratedDataFactory.getAsync();
await expect(
applyPrettierFormatting(`{"name":"Rahul"`, 'json', {
indent: ' ',
amount: 2,
})
).resolves.toBe(`{"name":"Rahul"`);
MigratedDataFactory.applyPrettierFormatting()
).resolves.toEqual(formatted);
});
});
});
83 changes: 42 additions & 41 deletions lib/workers/repository/config-migration/branch/migrated-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface MigratedData {
content: string;
filename: string;
}

interface Indent {
amount: number;
indent: string;
Expand All @@ -30,42 +31,10 @@ const prettierConfigFilenames = new Set([
'.prettierrc.toml',
]);

export async function applyPrettierFormatting(
content: string,
parser: string,
indent: Indent
): Promise<string> {
const fileList = await getFileList();
let prettierExists = fileList.some((file) =>
prettierConfigFilenames.has(file)
);
if (!prettierExists) {
try {
const packageJsonContent = await readLocalFile('package.json', 'utf8');
prettierExists =
packageJsonContent && JSON.parse(packageJsonContent).prettier;
} catch {
logger.warn(
'applyPrettierFormatting() - Error processing package.json file'
);
}
}

if (!prettierExists) {
return content;
}
const options = {
parser,
tabWidth: indent.amount === 0 ? 2 : indent.amount,
useTabs: indent.type === 'tab',
};

return prettier.format(content, options);
}

export class MigratedDataFactory {
// singleton
private static data: MigratedData | null;
private static indent: Indent;

public static async getAsync(): Promise<MigratedData | null> {
if (this.data) {
Expand All @@ -81,6 +50,44 @@ export class MigratedDataFactory {
return this.data;
}

public static async applyPrettierFormatting(): Promise<string | null> {
logger.trace('applyPrettierFormatting() - START');
if (!this.data) {
return null;
}

const { content, filename } = this.data;
const indent = this.indent;
const fileList = await getFileList();
let prettierExists = fileList.some((file) =>
prettierConfigFilenames.has(file)
);

if (!prettierExists) {
try {
const packageJsonContent = await readLocalFile('package.json', 'utf8');
prettierExists =
packageJsonContent && JSON.parse(packageJsonContent).prettier;
} catch {
logger.warn(
'applyPrettierFormatting() - Error processing package.json file'
);
}
}

if (!prettierExists) {
return content;
}
const options = {
parser: filename.endsWith('.json5') ? 'json5' : 'json',
tabWidth: indent.amount === 0 ? 2 : indent.amount,
useTabs: indent.type === 'tab',
};

logger.trace('applyPrettierFormatting() - END');
Gabriel-Ladzaretti marked this conversation as resolved.
Show resolved Hide resolved
return prettier.format(content, options);
}

public static reset(): void {
this.data = null;
}
Expand All @@ -105,8 +112,8 @@ export class MigratedDataFactory {

// indent defaults to 2 spaces
// TODO #7154
const indent = detectIndent(raw!);
const indentSpace = indent.indent ?? ' ';
this.indent = detectIndent(raw!);
const indentSpace = this.indent.indent ?? ' ';
const filename = configFileName!;
let content: string;

Expand All @@ -116,12 +123,6 @@ export class MigratedDataFactory {
content = JSON.stringify(migratedConfig, undefined, indentSpace);
}

// format if prettier is found in the user's repo
content = await applyPrettierFormatting(
content,
filename.endsWith('.json5') ? 'json5' : 'json',
indent
);
if (!content.endsWith('\n')) {
content += '\n';
}
Expand Down
33 changes: 32 additions & 1 deletion lib/workers/repository/config-migration/branch/rebase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ import {
platform,
} from '../../../../../test/util';
import { GlobalConfig } from '../../../../config/global';
import { checkoutBranch } from '../../../../util/git';
import { checkoutBranch, commitFiles } from '../../../../util/git';
import { MigratedDataFactory } from './migrated-data';
import type { MigratedData } from './migrated-data';
import { rebaseMigrationBranch } from './rebase';

jest.mock('../../../../util/git');

const formattedMigratedData = Fixtures.getJson(
'./migrated-data-formatted.json'
);

describe('workers/repository/config-migration/branch/rebase', () => {
const prettierSpy = jest.spyOn(
MigratedDataFactory,
'applyPrettierFormatting'
);

beforeAll(() => {
GlobalConfig.set({
localDir: '',
Expand Down Expand Up @@ -63,6 +73,27 @@ describe('workers/repository/config-migration/branch/rebase', () => {
expect(git.commitFiles).toHaveBeenCalledTimes(1);
});

it('applies prettier formatting when rebasing the migration branch ', async () => {
const formatted = formattedMigratedData.content;
prettierSpy.mockResolvedValueOnce(formattedMigratedData.content);
git.isBranchBehindBase.mockResolvedValueOnce(true);
await rebaseMigrationBranch(config, migratedConfigData);
expect(checkoutBranch).toHaveBeenCalledWith(config.defaultBranch);
expect(git.commitFiles).toHaveBeenCalledTimes(1);
expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: formatted,
},
],
message: 'Migrate config renovate.json',
platformCommit: false,
});
});

it('does not rebases migration branch when in dryRun is on', async () => {
GlobalConfig.set({
dryRun: 'full',
Expand Down
Loading