Skip to content

Commit

Permalink
feat(release): add semantic release prod plugin (#2)
Browse files Browse the repository at this point in the history
* feat(release): add semantic release prod plugin

* feat(release): update default paths

* feat(release): revert publish command

* feat(release): update release plugin exports

* feat(release): update plugins exports to commonjs

* feat(release): update plugins imports to commonjs

* feat(release): update plugins data

* feat(release): update env applying

* feat(release): add release cmd plugin

* Update src/plugins/release/index.js

Co-authored-by: Mikhail Yarmaliuk <hellboymxb@gmail.com>

* Update src/plugins/index.js

Co-authored-by: Mikhail Yarmaliuk <hellboymxb@gmail.com>

* Update src/release.config.mjs

Co-authored-by: Mikhail Yarmaliuk <hellboymxb@gmail.com>

* Update src/release.config.mjs

Co-authored-by: Mikhail Yarmaliuk <hellboymxb@gmail.com>

* feat(release): clean up

---------

Co-authored-by: Mikhail Yarmaliuk <hellboymxb@gmail.com>
  • Loading branch information
OlegDO and MatthewPattell authored Apr 22, 2024
1 parent cc4fe8f commit b4ed9e4
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const path = require('path');

const plugins = [
[
path.join(__dirname, 'release', 'index.js'),
],
[
'@semantic-release/exec',
{
analyzeCommitsCmd: 'echo "minor"',
generateNotesCmd: 'echo "chore: bump version"'
},
],
]

module.exports = plugins;
36 changes: 36 additions & 0 deletions src/plugins/release/get-next-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This file will be injected into semantic-release lib that USE ES6 MODULES
import fs from 'fs';
import path from 'path';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

/**
* Replace semantic-release getNextVersion
*/
export default () => {
let currentDir = __dirname; // Get the current directory of the module
let version = null;

// Traverse upwards until reaching the root of the project
while (currentDir !== '/') {
const versionFilePath = path.join(currentDir, '.version');

// Check if .version file exists
if (fs.existsSync(versionFilePath)) {
// Read the version from .version file
version = fs.readFileSync(versionFilePath, 'utf8').trim();
break;
}

// Move up one directory
currentDir = path.dirname(currentDir);
}

if(!version) {
throw new Error('Missing microservices next release version.');
}

return version;
}
179 changes: 179 additions & 0 deletions src/plugins/release/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
const fs = require('fs');
const path = require('path');

const parentDirectory = '..'; // Microservices folder
const rootMicroservicesDirectory = path.join('..', '..'); // Root microservices folder

/**
* Returns highest version
*/
function getHighestVersion(current, highest = '1.0.0') {
if(!current) {
return '1.0.0';
}

const currentParts = current.split('.').map(part => parseInt(part, 10));
const currentHighestParts = highest.split('.').map(part => parseInt(part, 10));

// Compare major version
if (currentParts[0] !== currentHighestParts[0]) {
return currentParts[0] > currentHighestParts[0] ? current : highest;
}

// Compare minor version
if (currentParts[1] !== currentHighestParts[1]) {
return currentParts[1] > currentHighestParts[1] ? current : highest;
}

// Compare patch version
if(currentParts[2] !== currentHighestParts[2]) {
return currentParts[2] > currentHighestParts[2] ? current : highest
}

// Current here will be equal to highest
return current;
}

/**
* Replace semantic release getNextVersion on release getNextVersion
*/
function replaceGetNextVersion() {
const nodeParentModulesPath = path.resolve(__dirname, '..', '..', '..', '..');
const semanticReleasePath = path.join(nodeParentModulesPath, 'semantic-release');
const getNextVersionPath = path.join(semanticReleasePath, 'lib', 'get-next-version.js');
const getNextVersionOriginPath = path.join(semanticReleasePath, 'lib', 'get-next-version-origin.js');
const getNextVersionPluginPath = path.join(nodeParentModulesPath, '@lomray', 'microservice-config', 'plugins', 'release', 'get-next-version.js')

// Handle error if file doesn't exist
if(!fs.existsSync(getNextVersionPath)) {
throw new Error('Semantic-release get-next-version file not found.');
}

// Rename the file
fs.renameSync(getNextVersionPath, getNextVersionOriginPath);

// Inject prod release "get next version" function
fs.copyFileSync(getNextVersionPluginPath, getNextVersionPath);
}

/**
* Write in .version file the highest version of microservice
*/
function writeNextReleaseVersion(context) {
const { nextRelease } = context;
const versionFilePath = '.version';

console.info('Original next release version:', nextRelease.version)

const version = nextRelease.version.match(/\d+\.\d+\.\d+/)?.[0] || '';

// Write the next release version to the file
fs.writeFileSync(versionFilePath, version);

console.info(`Next release version ${version} has been written to ${versionFilePath}`);
}

/**
* Write in .version file the highest version in the root microservices folder
*/
function writeHighestNextReleaseVersion() {
let highestVersion = '';
let isVersionUpdated = false;

try {
highestVersion = fs.readFileSync(path.join(rootMicroservicesDirectory, '.version'), 'utf8').trim();
} catch (error) {
console.info('Highest version file was not found.');
}

// Get a list of all directories in the parent directory
const directories = fs.readdirSync(parentDirectory, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => entry.name);

console.info('Found microservices:', directories.join(' '))

if(!directories.length) {
throw new Error('No one directory was found.');
}

directories.forEach(folder => {
const versionFilePath = path.join(parentDirectory, folder, '.version');

// Check if .version file exists in the directory
if (fs.existsSync(versionFilePath)) {
// Read the version from .version file
const version = fs.readFileSync(versionFilePath, 'utf8').trim();
const newVersion = highestVersion ? getHighestVersion(version, highestVersion) : version;

if(highestVersion !== newVersion) {
highestVersion = newVersion;
isVersionUpdated = true;
}
}
});

if(!isVersionUpdated || !highestVersion) {
return;
}

console.info(`Highest version found: ${highestVersion}`);

// Write the highest version to .version file in root microservices directory
const versionFilePath = path.join(rootMicroservicesDirectory, '.version');

fs.writeFileSync(versionFilePath, highestVersion);

console.info(`chore: bump version ${highestVersion}`);
}

/**
* Clean up microservices .version file
*/
function cleanUpVersionFiles() {
const directories = fs.readdirSync(parentDirectory, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => entry.name);

directories.forEach(directory => {
const versionFilePath = path.join(parentDirectory, directory, '.version');

if (fs.existsSync(versionFilePath)) {
try {
fs.unlinkSync(versionFilePath);

console.info(`Removed .version file from ${directory}`);

} catch (error) {
console.error(`Error removing .version file from ${directory}: ${error.message}`);
}
}
});
}

/**
* Analyze commits hook
* @description Remove previously created .version files
*/
async function analyzeCommits() {
console.info('Clean up version files');

cleanUpVersionFiles()
}

/**
* Prepare hook
* @description This will be not called in dry run mode. Enable it by your own hands in semantic-release lib config
*/
async function prepare(_, context) {
console.info('Preparing release version');

writeNextReleaseVersion(context);
replaceGetNextVersion();
writeHighestNextReleaseVersion();
}

module.exports = {
analyzeCommits,
prepare
}
4 changes: 4 additions & 0 deletions src/release.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import releasePlugins from "./plugins/index.js";

const shouldPublishNpm = Boolean(Number(process.env.PUBLISH_PACKAGE) || 0);
const shouldPerformProdPlugin = Boolean(Number(process.env.PERFORM_PROD_PLUGIN) || 0);
const monorepoConfig = await import('@lomray/semantic-release-monorepo');

/**
Expand Down Expand Up @@ -26,6 +29,7 @@ export default {
" && echo '${nextRelease.version}' > .version",
},
],
...(shouldPerformProdPlugin ? releasePlugins : []),
...(shouldPublishNpm ? [[
'@semantic-release/npm',
{
Expand Down

0 comments on commit b4ed9e4

Please sign in to comment.