Skip to content

Commit

Permalink
feat: Introduce @theunderscorer/nx-semantic-release:install generator
Browse files Browse the repository at this point in the history
  • Loading branch information
TheUnderScorer committed Jun 23, 2022
1 parent c28cd7a commit 22aaec8
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 139 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Run:

```shell
npm install -D @theunderscorer/nx-semantic-release
nx g @theunderscorer/nx-semantic-release:install
```

For now this package supports only <b>Independent</b> versioning mode, synced mode is planned to be added soon.
Expand Down
252 changes: 119 additions & 133 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@nrwl/linter": "14.3.6",
"@nrwl/node": "14.3.6",
"@nrwl/nx-cloud": "14.1.2",
"@nrwl/nx-plugin": "^14.3.6",
"@nrwl/workspace": "14.3.6",
"@types/jest": "27.4.1",
"@types/node": "^14.14.33",
Expand All @@ -84,4 +85,3 @@
"typescript": "4.7.4"
}
}

12 changes: 12 additions & 0 deletions packages/nx-semantic-release/generators.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/schema",
"name": "nx-semantic-release-plugin",
"version": "0.0.1",
"generators": {
"install": {
"factory": "./src/generators/install/generator",
"schema": "./src/generators/install/schema.json",
"description": "@theunderscorer/nx-semantic-release installator"
}
}
}
4 changes: 3 additions & 1 deletion packages/nx-semantic-release/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@
"type": "git",
"url": "https://github.com/TheUnderScorer/nx-semantic-release"
},
"builders": "./builders.json"
"builders": "./builders.json",
"executors": "./builders.json",
"generators": "./generators.json"
}
6 changes: 6 additions & 0 deletions packages/nx-semantic-release/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"main": "packages/nx-semantic-release/src/index.ts",
"assets": [
"packages/nx-semantic-release/*.md",
"packages/nx-semantic-release/**/files/*",
"packages/nx-semantic-release/builders.json",
{
"input": "packages/nx-semantic-release",
Expand All @@ -43,6 +44,11 @@
"output": "/"
}
]
},
"configurations": {
"watch": {
"watch": true
}
}
},
"semantic-release": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const resolvePlugins = (
assets: [
// Git requires relative paths from project root in a posix format
path
.relative(context.cwd, options.changelogFile)
.relative(context.cwd, options.changelogFile as string)
.split(path.sep)
.join(path.posix.sep),
path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export type SemanticReleaseOptions = Omit<release.Options, 'extends'> & {
npm: boolean;
github: boolean;
buildTarget?: string;
changelog: boolean;
changelogFile: string;
changelog?: boolean;
changelogFile?: string;
outputPath?: string;
commitMessage: string;
commitMessage?: string;
gitAssets?: string[];
packageJsonDir?: string;
parserOpts?: Record<string, unknown>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { writeJsonFile } from '@nrwl/devkit';
import { SemanticReleaseOptions } from '../../executors/semantic-release/semantic-release';
import { InstallGeneratorOptions } from './generator';

export const generatedConfigFileName = '.nxrelease.json';

export function createConfigFile(options: InstallGeneratorOptions) {
const config: SemanticReleaseOptions = {
changelog: false,
npm: true,
github: options.github,
releaseRules: options.repositoryUrl,
branches: [options.baseBranch],
};

writeJsonFile(generatedConfigFileName, config);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {
addDependenciesToPackageJson,
readJson,
Tree,
updateJson,
} from '@nrwl/devkit';
import type { PackageJson } from 'type-fest';
import { constants } from 'fs';

export const commitLintPreset = '@commitlint/config-conventional';
export const generatedCommitLintConfigName = '.commitlintrc.json';

export function addCommitEnforceDependencies(tree: Tree) {
addDevDependencies(tree);
setupHuskyExecutable(tree);
setupHuskyConfig(tree);
addCommitlintConfig(tree);
}

function addDevDependencies(tree: Tree) {
addDependenciesToPackageJson(
tree,
{},
{
'@commitlint/cli': '^17.0.0',
[commitLintPreset]: '^17.0.0',
husky: '^8.0.0',
}
);
}

function setupHuskyExecutable(tree: Tree) {
return updateJson(tree, 'package.json', (packageJson: PackageJson) => {
const huskyExists = tree.exists('.husky/_/husky.sh');

if (!huskyExists) {
packageJson.scripts = {
...packageJson.scripts,
...{ prepare: 'husky install' },
};
}

return packageJson;
});
}

function setupHuskyConfig(tree: Tree) {
const hasConfigFile: boolean = tree.exists('.husky/commit-msg');

if (!hasConfigFile) {
const commitMsg = `#!/bin/sh\n. "$(dirname "$0")/_/husky.sh"\n\nnpx --no-install commitlint --edit $1\n`;

tree.write('.husky/commit-msg', commitMsg, {
mode: constants.S_IRWXU,
});
}
}

function addCommitlintConfig(tree: Tree) {
const possibleConfigs = [
'commitlint.config.js',
'commitlint',
'.commitlintrc.js',
'.commitlintrc.json',
'.commitlintrc.yml',
];

const packageJson = readJson(tree, 'package.json');

const hasConfig =
packageJson.commitlint != null ||
possibleConfigs.some((config) => tree.exists(config));

if (!hasConfig) {
tree.write(
generatedCommitLintConfigName,
JSON.stringify(
{
extends: [commitLintPreset],
rules: {},
},
null,
2
)
);
}

return tree;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import {
ensureNxProject,
getCwd,
readJson,
runNxCommandAsync,
runPackageManagerInstall,
tmpProjPath,
updateFile,
} from '@nrwl/nx-plugin/testing';
import path from 'path';
import { PackageJson } from 'type-fest';
import { generatedConfigFileName } from './create-config-file';
import {
commitLintPreset,
generatedCommitLintConfigName,
} from './enforce-commit-deps';
import fs from 'fs';
import { SemanticReleaseOptions } from '../../executors/semantic-release/semantic-release';

// TODO Use testing utils for semantic-release executor as well
const defaultExpectedConfig: SemanticReleaseOptions = {
changelog: false,
npm: true,
github: true,
releaseRules: 'test',
branches: ['master'],
};

describe('Installer', () => {
beforeAll(() => {
ensureNxProject(
'@theunderscorer/nx-semantic-release',
'dist/packages/nx-semantic-release'
);

updateFile('package.json', (contents) => {
const pkg = JSON.parse(contents) as PackageJson;

pkg.devDependencies = {
...pkg.devDependencies,
'@theunderscorer/nx-semantic-release': `file:${path.resolve(
getCwd(),
'dist/packages/nx-semantic-release'
)}`,
};

return JSON.stringify(pkg, null, 2);
});

runPackageManagerInstall();
});

afterAll(() => {
runNxCommandAsync('reset');
});

it('should create example', async () => {
await runNxCommandAsync(
`generate @theunderscorer/nx-semantic-release:install --repositoryUrl=test --enforceConventionalCommits=false`
);

const config = readJson(generatedConfigFileName);
expect(config).toEqual(defaultExpectedConfig);
});

describe('--enforceConventionalCommits', () => {
it('should setup project to enforce conventional commits', async () => {
await runNxCommandAsync(
`generate @theunderscorer/nx-semantic-release:install --repositoryUrl=test --enforceConventionalCommits`
);

const paths = {
commitLintConfig: tmpProjPath(generatedCommitLintConfigName),
releaseConfig: tmpProjPath(generatedConfigFileName),
huskyCommitMsgHook: tmpProjPath('.husky/commit-msg'),
};

Object.values(paths).forEach((filePath) => {
expect(fs.existsSync(filePath)).toBe(true);
});

const commitLintConfig = readJson(paths.commitLintConfig);
const releaseConfig = readJson(paths.releaseConfig);
const pkgJson = readJson<PackageJson>('package.json');

expect(commitLintConfig).toEqual({
extends: [commitLintPreset],
rules: {},
});
expect(releaseConfig).toEqual(defaultExpectedConfig);

expect(pkgJson.scripts?.prepare).toEqual('husky install');
expect(
Object.keys(pkgJson.devDependencies as Record<string, string>)
).toEqual(
expect.arrayContaining([
'@commitlint/cli',
'@commitlint/config-conventional',
])
);
});
});
});
29 changes: 29 additions & 0 deletions packages/nx-semantic-release/src/generators/install/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SemanticReleaseOptions } from '../../executors/semantic-release/semantic-release';
import { formatFiles, logger, Tree, installPackagesTask } from '@nrwl/devkit';
import { addCommitEnforceDependencies } from './enforce-commit-deps';
import { createConfigFile } from './create-config-file';

export interface InstallGeneratorOptions
extends Pick<SemanticReleaseOptions, 'github' | 'repositoryUrl'> {
baseBranch?: string;
enforceConventionalCommits?: boolean;
}

export default async function installGenerator(
tree: Tree,
options: InstallGeneratorOptions
) {
if (options.enforceConventionalCommits) {
await addCommitEnforceDependencies(tree);
}

createConfigFile(options);

await formatFiles(tree);

if (options.enforceConventionalCommits) {
logger.log('Installing dependencies...');

installPackagesTask(tree);
}
}
37 changes: 37 additions & 0 deletions packages/nx-semantic-release/src/generators/install/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "http://json-schema.org/schema",
"title": "@theunderscorer/nx-semantic-release installator",
"type": "object",
"cli": "nx",
"properties": {
"baseBranch": {
"description": "Configure base branch",
"type": "string",
"default": "master"
},
"enforceConventionalCommits": {
"description": "Install & configure commitlint and husky.",
"type": "boolean",
"default": true,
"x-prompt": "Would you want to enforce conventional commits?"
},
"repositoryUrl": {
"description": "Remote repository url",
"type": "string",
"x-prompt": "What is the remote repository url? (example: https://github.com/TheUnderScorer/nx-semantic-release)"
},
"github": {
"description": "Create github releases",
"type": "boolean",
"default": true,
"x-prompt": "Would you want to create github releases?"
},
"npm": {
"description": "Create npm releases",
"type": "boolean",
"default": true,
"x-prompt": "Would you want to create npm releases?"
}
},
"required": ["repositoryUrl"]
}

0 comments on commit 22aaec8

Please sign in to comment.