@cplace/cli
Advanced tools
Comparing version 0.19.12 to 0.19.15
@@ -42,6 +42,15 @@ #!/usr/bin/env node | ||
release-notes (--from <from> [--to <to>] [--lang <lang>] [--force]) | (--check [--size <size>]) | ||
Generates release notes between the two given commits (excluding <from>, including <to>). | ||
Generates release notes for a given release or between the two given commits (excluding <from>, including <to>). | ||
If <to> is not given "HEAD" is used. | ||
If <lang> is not given "en" is used. | ||
--release <version> | ||
Create release notes for commits between the head of the release Branch for the given release | ||
and the head of the predecessor release branch. | ||
(Release takes precedence over --from / --to mode) | ||
--docs | ||
Stores release notes as markdown in folder documentation/changelog/_index.md so that it can | ||
be published to cplace documentation | ||
If --force is set then release notes will also be generated if commits are commented out or in conflict. | ||
@@ -95,2 +104,3 @@ | ||
If --depth is set to a positive integer, a shallow clone with a history truncated to the specified number of commits is created. | ||
The --depth parameter is ignored if a 'commit' is set to checkout in the parent repository. | ||
The --force setting has no effect for this command. | ||
@@ -97,0 +107,0 @@ |
@@ -5,4 +5,6 @@ /** | ||
export declare class ReleaseNumber { | ||
static readonly MASTER: string; | ||
static readonly MAIN: string; | ||
private static readonly RELEASE_NUMBER_PATTERN; | ||
readonly master: boolean; | ||
readonly defaultBranch: boolean; | ||
readonly major: number; | ||
@@ -13,4 +15,7 @@ readonly minor: number; | ||
static parse(release: string): ReleaseNumber | null; | ||
static isDefaultBranch(branchName: string, remote?: string): boolean; | ||
compareTo(other: ReleaseNumber): number; | ||
toString(): string; | ||
releaseBranchName(): string; | ||
getMajorOrMinorPredecessorRelease(): ReleaseNumber; | ||
} |
@@ -8,4 +8,4 @@ "use strict"; | ||
class ReleaseNumber { | ||
constructor(master, major, minor, patch) { | ||
this.master = master; | ||
constructor(defaultBranch, major, minor, patch) { | ||
this.defaultBranch = defaultBranch; | ||
this.major = major; | ||
@@ -16,3 +16,3 @@ this.minor = minor; | ||
static parse(release) { | ||
if (release === 'master') { | ||
if (ReleaseNumber.isDefaultBranch(release)) { | ||
return new ReleaseNumber(true, null, null, null); | ||
@@ -38,10 +38,20 @@ } | ||
} | ||
static isDefaultBranch(branchName, remote = '') { | ||
let isDefault = false; | ||
if (remote && remote !== '' && (branchName === `${remote}/${ReleaseNumber.MASTER}` || branchName === `${remote}/${ReleaseNumber.MAIN}`)) { | ||
isDefault = true; | ||
} | ||
if (branchName === ReleaseNumber.MASTER || branchName === ReleaseNumber.MAIN) { | ||
isDefault = true; | ||
} | ||
return isDefault; | ||
} | ||
compareTo(other) { | ||
if (this.master && other.master) { | ||
if (this.defaultBranch && other.defaultBranch) { | ||
return 0; | ||
} | ||
else if (this.master) { | ||
else if (this.defaultBranch) { | ||
return 1; | ||
} | ||
else if (other.master) { | ||
else if (other.defaultBranch) { | ||
return -1; | ||
@@ -60,7 +70,28 @@ } | ||
toString() { | ||
return this.master ? ' master' : `${this.major}.${this.minor}.${this.patch}`; | ||
return this.defaultBranch ? 'default' : `${this.major}.${this.minor}.${this.patch}`; | ||
} | ||
releaseBranchName() { | ||
return this.defaultBranch ? '' : `release/${this.major}.${this.minor}`; | ||
} | ||
getMajorOrMinorPredecessorRelease() { | ||
if (this.major === 5 && this.minor === 0 && (this.patch === 0 || this.patch === null)) { | ||
return ReleaseNumber.parse('4.57'); | ||
} | ||
else if (this.major === 22 && this.minor === 2 && (this.patch === 0 || this.patch === null)) { | ||
return ReleaseNumber.parse('5.20'); | ||
} | ||
else { | ||
if (this.major > 22 && this.minor === 1) { | ||
return ReleaseNumber.parse(`${this.major - 1}.4`); | ||
} | ||
if (this.minor > 1) { | ||
return ReleaseNumber.parse(`${this.major}.${this.minor - 1}`); | ||
} | ||
} | ||
} | ||
} | ||
exports.ReleaseNumber = ReleaseNumber; | ||
ReleaseNumber.MASTER = 'master'; | ||
ReleaseNumber.MAIN = 'main'; | ||
ReleaseNumber.RELEASE_NUMBER_PATTERN = new RegExp(/^\d+(\.\d+){0,2}$/); | ||
//# sourceMappingURL=ReleaseNumber.js.map |
@@ -7,2 +7,3 @@ "use strict"; | ||
const Global_1 = require("../../Global"); | ||
const ReleaseNumber_1 = require("./ReleaseNumber"); | ||
class SplitRepository { | ||
@@ -74,3 +75,3 @@ constructor() { | ||
this.sourceBranchName = currentBranch.trim(); | ||
if (this.sourceBranchName === 'master') { | ||
if (ReleaseNumber_1.ReleaseNumber.isDefaultBranch(this.sourceBranchName)) { | ||
const currentDate = new Date(); | ||
@@ -77,0 +78,0 @@ this.targetBranchName = `source-master-${currentDate.getDate()}-${currentDate.getMonth() + 1}-${currentDate.getFullYear()}`; |
@@ -42,3 +42,3 @@ "use strict"; | ||
return this.repo | ||
.fetch() | ||
.fetch({}) | ||
.then(() => this.checkRepoClean()) | ||
@@ -102,3 +102,3 @@ .then(() => this.checkForRelease()) | ||
let customerName; | ||
if (branch.name === `${this.remote}/master`) { | ||
if (ReleaseNumber_1.ReleaseNumber.isDefaultBranch(branch.name, this.remote)) { | ||
version = 'master'; | ||
@@ -105,0 +105,0 @@ } |
@@ -6,2 +6,4 @@ import { ICommand, ICommandParameters } from '../models'; | ||
private static readonly PARAMETER_LANG; | ||
private static readonly PARAMETER_RELEASE; | ||
private static readonly PARAMETER_DOCS; | ||
private static readonly PARAMETER_FORCE; | ||
@@ -13,4 +15,8 @@ private static readonly FILE_NAME_CHANGELOG; | ||
private force; | ||
private release; | ||
private messagesFile; | ||
private explicitsFile; | ||
private repo; | ||
private changelog; | ||
private generateMarkdownForDocs; | ||
prepareAndMayExecute(params: ICommandParameters): boolean; | ||
@@ -21,3 +27,5 @@ execute(): Promise<void>; | ||
private readExplicits; | ||
private generateChangelog; | ||
private generateChangelogCircleCi; | ||
private generateChangelogDocs; | ||
private createMarkdownForCplaceDocs; | ||
} |
@@ -20,7 +20,23 @@ "use strict"; | ||
const ReleaseNotesMessagesFile_1 = require("./ReleaseNotesMessagesFile"); | ||
const ReleaseNumber_1 = require("../flow/ReleaseNumber"); | ||
const child_process_1 = require("child_process"); | ||
const path = require("path"); | ||
class GenerateReleaseNotes { | ||
constructor() { | ||
this.release = null; | ||
this.generateMarkdownForDocs = false; | ||
} | ||
prepareAndMayExecute(params) { | ||
let release = params[GenerateReleaseNotes.PARAMETER_RELEASE]; | ||
if (release) { | ||
release = release.replace('release/', ''); | ||
this.release = ReleaseNumber_1.ReleaseNumber.parse(release); | ||
} | ||
const docs = params[GenerateReleaseNotes.PARAMETER_DOCS]; | ||
if (docs) { | ||
this.generateMarkdownForDocs = docs; | ||
} | ||
const fromHash = params[GenerateReleaseNotes.PARAMETER_FROM]; | ||
if (!fromHash) { | ||
console.error(`Missing required parameter "${GenerateReleaseNotes.PARAMETER_FROM}"`); | ||
if (!release && !fromHash) { | ||
console.error(`Missing required parameter '${GenerateReleaseNotes.PARAMETER_FROM}'`); | ||
return false; | ||
@@ -51,19 +67,36 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
Global_1.Global.isVerbose() && console.log('generating release notes from', this.fromHash, 'to', this.toHash); | ||
const repo = new git_1.Repository(); | ||
try { | ||
this.fromHash = yield repo.commitExists(this.fromHash); | ||
Global_1.Global.isVerbose() && console.log(`from commit has hash ${this.fromHash}`); | ||
this.repo = new git_1.Repository(); | ||
if (this.release) { | ||
console.log('generating release notes for release branch: ', this.release.releaseBranchName()); | ||
const predecessorReleaseBranch = this.release.getMajorOrMinorPredecessorRelease().releaseBranchName(); | ||
if (this.repo.checkBranchExistsOnRemote(this.release.releaseBranchName()) && this.repo.checkBranchExistsOnRemote(predecessorReleaseBranch)) { | ||
const fetchAll = child_process_1.execSync(`git fetch --all`).toString(); | ||
Global_1.Global.isVerbose() && console.log(fetchAll); | ||
this.fromHash = child_process_1.execSync(`git log -n 1 --pretty=format:"%H" origin/${this.release.getMajorOrMinorPredecessorRelease().releaseBranchName()}`).toString(); | ||
this.toHash = child_process_1.execSync(`git log -n 1 --pretty=format:"%H" origin/${this.release.releaseBranchName()}`).toString(); | ||
} | ||
else { | ||
console.error(`Either given release branch or branch of predecessor release does not exist on remote.`); | ||
process.exit(1); | ||
} | ||
} | ||
catch (_a) { | ||
throw new Error(`Commit does not exist: ${this.fromHash}`); | ||
else { | ||
console.log(`generating release notes for given --from Hash ${this.fromHash} and --to Hash ${this.toHash}`); | ||
try { | ||
this.fromHash = yield this.repo.commitExists(this.fromHash); | ||
Global_1.Global.isVerbose() && console.log(`from commit has hash ${this.fromHash}`); | ||
} | ||
catch (_a) { | ||
throw new Error(`Commit does not exist: ${this.fromHash}`); | ||
} | ||
try { | ||
this.toHash = yield this.repo.commitExists(this.toHash); | ||
Global_1.Global.isVerbose() && console.log(`--to commit has hash ${this.toHash}`); | ||
} | ||
catch (_b) { | ||
throw new Error(`Commit does not exist: ${this.toHash}`); | ||
} | ||
} | ||
try { | ||
this.toHash = yield repo.commitExists(this.toHash); | ||
Global_1.Global.isVerbose() && console.log(`to commit has hash ${this.toHash}`); | ||
} | ||
catch (_b) { | ||
throw new Error(`Commit does not exist: ${this.toHash}`); | ||
} | ||
const log = yield repo.log(this.fromHash, this.toHash); | ||
console.log(`commits for --from Hash ${this.fromHash} and --to Hash ${this.toHash} exist `); | ||
const log = yield this.repo.log(this.fromHash, this.toHash); | ||
return yield this.parseLog(log); | ||
@@ -75,16 +108,13 @@ }); | ||
const relevant = log.all.filter(ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.filterRelevantCommits); | ||
try { | ||
yield fs_1.fs.statAsync(ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES); | ||
if (!fs_1.fs.existsSync(ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES)) { | ||
fs_1.fs.mkdirSync(ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES); | ||
} | ||
catch (_a) { | ||
try { | ||
yield fs_1.fs.mkdirAsync(ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES); | ||
} | ||
catch (_b) { | ||
throw new Error(`Failed to create directory ${ReleaseNotesMessagesFile_1.ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES}`); | ||
} | ||
} | ||
const file = yield this.updateMessagesFile(relevant); | ||
const files = yield this.readExplicits(file); | ||
yield this.generateChangelog(files.messages, files.explicits, log.all); | ||
if (this.generateMarkdownForDocs) { | ||
this.generateChangelogDocs(files.messages, files.explicits, log.all); | ||
} | ||
else { | ||
this.generateChangelogCircleCi(files.messages, files.explicits, log.all); | ||
} | ||
}); | ||
@@ -143,18 +173,61 @@ } | ||
} | ||
generateChangelog(file, explicits, log) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const changelog = [`# Changelog ${new Date().toDateString()}`, '']; | ||
changelog.push(''); | ||
changelog.push(`_Commit range: ${this.fromHash} - ${this.toHash}_`); | ||
changelog.push(''); | ||
for (const c of log) { | ||
const message = file.getMessage(c.hash) || (explicits && explicits.getMessage(c.hash)); | ||
if (message) { | ||
changelog.push(` * ${message}`); | ||
generateChangelogCircleCi(file, explicits, log) { | ||
const changelog = [`# Changelog ${new Date().toDateString()}`, '']; | ||
changelog.push(''); | ||
changelog.push(`_Commit range: ${this.fromHash} - ${this.toHash}_`); | ||
changelog.push(''); | ||
for (const c of log) { | ||
const message = file.getMessage(c.hash) || (explicits && explicits.getMessage(c.hash)); | ||
if (message) { | ||
changelog.push(` * ${message}`); | ||
} | ||
} | ||
fs_1.fs.writeFileSync(GenerateReleaseNotes.FILE_NAME_CHANGELOG, changelog.join('\n'), 'utf8'); | ||
console.log(`>> Changelog has successfully been generated in ${GenerateReleaseNotes.FILE_NAME_CHANGELOG}`); | ||
} | ||
generateChangelogDocs(file, explicits, log) { | ||
var _a, _b; | ||
this.changelog = [`_Changelog created on ${new Date().toDateString()}_`]; | ||
if (this.release) { | ||
this.changelog.push(`_for ${this.release.releaseBranchName()}_`); | ||
} | ||
this.changelog.push(' ', `_Commit range: ${this.fromHash} - ${this.toHash}_`, ''); | ||
log = log.sort((a, b) => { | ||
return new Date(b.date).getTime() - new Date(a.date).getTime(); | ||
}); | ||
const remoteUrl = (_a = child_process_1.execSync('git config --get remote.origin.url') | ||
.toString()) === null || _a === void 0 ? void 0 : _a.replace('.git', '').replace(/(\r\n|\n|\r)/gm, '').replace('git@github.com:collaborationFactory', 'https://github.com/collaborationFactory'); | ||
if (!remoteUrl) { | ||
throw new Error(`Remote url of your local git repository doesn't exist.`); | ||
} | ||
for (const c of log) { | ||
const message = file.getMessage(c.hash) || (explicits && explicits.getMessage(c.hash)); | ||
if (message) { | ||
const prNumber = (_b = message.split('#')[1]) === null || _b === void 0 ? void 0 : _b.replace(']', '').trim(); | ||
if (prNumber && remoteUrl) { | ||
this.changelog.push(` * ${message}(${remoteUrl}/pull/${prNumber})`); | ||
} | ||
else { | ||
this.changelog.push(` * ${message}`); | ||
} | ||
} | ||
yield fs_1.fs.writeFileAsync(GenerateReleaseNotes.FILE_NAME_CHANGELOG, changelog.join('\n'), 'utf8'); | ||
console.log(`>> Changelog has successfully been generated in ${GenerateReleaseNotes.FILE_NAME_CHANGELOG}`); | ||
}); | ||
} | ||
fs_1.fs.writeFileSync(GenerateReleaseNotes.FILE_NAME_CHANGELOG, this.changelog.join('\n'), 'utf8'); | ||
console.log(`>> Changelog has successfully been generated in ${GenerateReleaseNotes.FILE_NAME_CHANGELOG}`); | ||
this.createMarkdownForCplaceDocs(); | ||
} | ||
createMarkdownForCplaceDocs() { | ||
const pathToReleaseNotesInMarkdown = path.join(this.repo.baseDir, 'documentation', 'changelog', `_index.md`); | ||
const markdownHeader = `--- | ||
title: "Release Notes" | ||
weight: "10" | ||
type: "section" | ||
--- | ||
`; | ||
if (!fs_1.fs.existsSync(pathToReleaseNotesInMarkdown)) { | ||
fs_1.fs.mkdirSync(path.join(this.repo.baseDir, 'documentation', 'changelog'), { recursive: true }); | ||
} | ||
fs_1.fs.writeFileSync(pathToReleaseNotesInMarkdown, markdownHeader + ' ' + this.changelog.join('\n'), 'utf8'); | ||
console.log(`>> Changelog has successfully been generated in ${pathToReleaseNotesInMarkdown}`); | ||
} | ||
} | ||
@@ -165,4 +238,6 @@ exports.GenerateReleaseNotes = GenerateReleaseNotes; | ||
GenerateReleaseNotes.PARAMETER_LANG = 'lang'; | ||
GenerateReleaseNotes.PARAMETER_RELEASE = 'release'; | ||
GenerateReleaseNotes.PARAMETER_DOCS = 'docs'; | ||
GenerateReleaseNotes.PARAMETER_FORCE = 'force'; | ||
GenerateReleaseNotes.FILE_NAME_CHANGELOG = 'CHANGELOG.md'; | ||
//# sourceMappingURL=GenerateReleaseNotes.js.map |
@@ -18,2 +18,3 @@ /** | ||
static getPathToExplicits(lang: string): string; | ||
static createReleaseNotesFolderIfNotYetExists(): void; | ||
static filterRelevantCommits(entry: IGitLogEntry): boolean; | ||
@@ -20,0 +21,0 @@ static getAllMessagesFiles(): Promise<ReleaseNotesMessagesFile[]>; |
@@ -18,7 +18,22 @@ "use strict"; | ||
static getPathToMessages(lang) { | ||
return `${ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES}/messages_${lang}.db`; | ||
ReleaseNotesMessagesFile.createReleaseNotesFolderIfNotYetExists(); | ||
const pathToMessages = `${ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES}/messages_${lang}.db`; | ||
if (!fs_1.fs.existsSync(pathToMessages)) { | ||
fs_1.fs.writeFileSync(pathToMessages, ''); | ||
} | ||
return pathToMessages; | ||
} | ||
static getPathToExplicits(lang) { | ||
return `${ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES}/explicits_${lang}.db`; | ||
ReleaseNotesMessagesFile.createReleaseNotesFolderIfNotYetExists(); | ||
const pathToExplicits = `${ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES}/explicits_${lang}.db`; | ||
if (!fs_1.fs.existsSync(pathToExplicits)) { | ||
fs_1.fs.writeFileSync(pathToExplicits, ''); | ||
} | ||
return pathToExplicits; | ||
} | ||
static createReleaseNotesFolderIfNotYetExists() { | ||
if (!fs_1.fs.existsSync(ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES)) { | ||
fs_1.fs.mkdirSync(ReleaseNotesMessagesFile.DIRECTORY_RELEASE_NOTES); | ||
} | ||
} | ||
static filterRelevantCommits(entry) { | ||
@@ -25,0 +40,0 @@ if (!entry.message) { |
@@ -14,3 +14,5 @@ import { ICommand, ICommandParameters } from '../models'; | ||
protected depth: number; | ||
prepareAndMayExecute(params: ICommandParameters): boolean; | ||
protected parentReposConfigPath: string; | ||
protected rootDir: string; | ||
prepareAndMayExecute(params: ICommandParameters, rootDir?: string): boolean; | ||
abstract execute(): Promise<void>; | ||
@@ -17,0 +19,0 @@ protected doPrepareAndMayExecute(params: ICommandParameters): boolean; |
@@ -22,4 +22,5 @@ "use strict"; | ||
class AbstractReposCommand { | ||
prepareAndMayExecute(params) { | ||
prepareAndMayExecute(params, rootDir = './') { | ||
Global_1.Global.isVerbose() && console.log('running in verbose mode'); | ||
this.rootDir = rootDir; | ||
this.force = !!params[AbstractReposCommand.PARAMETER_FORCE]; | ||
@@ -44,11 +45,12 @@ if (this.force) { | ||
} | ||
if (!fs.existsSync(AbstractReposCommand.PARENT_REPOS_FILE_NAME)) { | ||
console.error('Cannot find repo description', AbstractReposCommand.PARENT_REPOS_FILE_NAME); | ||
this.parentReposConfigPath = path.join(rootDir, AbstractReposCommand.PARENT_REPOS_FILE_NAME); | ||
if (!fs.existsSync(this.parentReposConfigPath)) { | ||
console.error('Cannot find repo description', this.parentReposConfigPath); | ||
return false; | ||
} | ||
try { | ||
this.parentRepos = JSON.parse(fs.readFileSync(AbstractReposCommand.PARENT_REPOS_FILE_NAME, 'utf8')); | ||
this.parentRepos = JSON.parse(fs.readFileSync(this.parentReposConfigPath, 'utf8')); | ||
} | ||
catch (e) { | ||
console.error('Failed to parse repo description', AbstractReposCommand.PARENT_REPOS_FILE_NAME, e); | ||
console.error('Failed to parse repo description', this.parentReposConfigPath, e); | ||
return false; | ||
@@ -89,7 +91,5 @@ } | ||
Global_1.Global.isVerbose() && console.log('new repo description', newParentReposContent); | ||
return fs | ||
.writeFileAsync(AbstractReposCommand.PARENT_REPOS_FILE_NAME, newParentReposContent, 'utf8') | ||
.then(() => { | ||
this.parentRepos = newParentRepos; | ||
}); | ||
fs.writeFileSync(this.parentReposConfigPath, newParentReposContent, 'utf8'); | ||
this.parentRepos = newParentRepos; | ||
return Promise.resolve(); | ||
} | ||
@@ -96,0 +96,0 @@ } |
@@ -59,3 +59,3 @@ "use strict"; | ||
validateRepoClean(repo) { | ||
return repo.fetch() | ||
return repo.fetch({}) | ||
.then(() => repo.status()) | ||
@@ -62,0 +62,0 @@ .then((status) => this.checkRepoClean(repo, status)) |
@@ -8,3 +8,4 @@ /** | ||
execute(): Promise<void>; | ||
private handleRepo; | ||
private checkRepoMissing; | ||
} |
@@ -23,3 +23,3 @@ "use strict"; | ||
const toPath = path.resolve('..', repoName); | ||
return git_1.Repository.clone(toPath, repoProperties.url, repoProperties.branch, this.depth); | ||
return this.handleRepo(toPath, repoName, repoProperties, this.depth); | ||
}); | ||
@@ -31,2 +31,10 @@ return promiseAllSettled_1.promiseAllSettledParallel(promises) | ||
} | ||
handleRepo(toPath, repoName, repoProperties, depth) { | ||
return git_1.Repository.getLatestTagOfReleaseBranch(repoName, repoProperties) | ||
.then((latestTag) => { | ||
repoProperties.latestTagForRelease = latestTag; | ||
return git_1.Repository.clone(toPath, repoProperties, this.depth); | ||
}) | ||
.catch((err) => Promise.reject('failed to clone repos: ' + err)); | ||
} | ||
checkRepoMissing(repoName) { | ||
@@ -33,0 +41,0 @@ const pathToRepo = path.resolve('..', repoName); |
@@ -11,2 +11,4 @@ /** | ||
tag?: string; | ||
tagMarker?: string; | ||
latestTagForRelease?: string; | ||
commit?: string; | ||
@@ -13,0 +15,0 @@ description?: string; |
@@ -16,3 +16,4 @@ /** | ||
private handleRepo; | ||
private checkout; | ||
private getTargetBranch; | ||
} |
@@ -62,9 +62,12 @@ "use strict"; | ||
Global_1.Global.isVerbose() && console.log('tag', repoProperties.tag); | ||
Global_1.Global.isVerbose() && console.log('tagMarker', repoProperties.tagMarker); | ||
if (!repoProperties.branch && !repoProperties.tag) { | ||
return Promise.reject('No branch or tag given in parent-repos.json for repo: ' + repoName); | ||
} | ||
const latestTagOfRelease = yield git_1.Repository.getLatestTagOfReleaseBranch(repoName, repoProperties); | ||
repoProperties.latestTagForRelease = latestTagOfRelease; | ||
const pathToRepo = path.join(process.cwd(), '..', repoName); | ||
const repo = new git_1.Repository(pathToRepo); | ||
if (!this.noFetch) { | ||
yield repo.fetch(); | ||
yield repo.fetch({}); | ||
} | ||
@@ -80,6 +83,3 @@ const status = yield repo.status(); | ||
const repoProperties = this.parentRepos[repoName]; | ||
const commit = repoProperties.commit; | ||
const branch = repoProperties.branch; | ||
const tag = repoProperties.tag; | ||
if (!branch && !tag) { | ||
if (!repoProperties.branch && !repoProperties.tag) { | ||
throw new Error(`Internal error: handleRepo was called for ${repoName} despite rejection in prepareRepo!`); | ||
@@ -104,11 +104,33 @@ } | ||
} | ||
if (branch) { | ||
yield this.checkout(repo, repoName, repoProperties); | ||
const isGradleBuild = new GradleBuild_1.GradleBuild(pathToRepo).containsGradleBuild(); | ||
if (isGradleBuild !== wasGradleBuild) { | ||
const toFrom = wasGradleBuild ? 'away from' : 'back to'; | ||
console.warn(`WARNING: Repository ${repoName} has changed ${toFrom} a Gradle build!`); | ||
console.warn(` This might cause issues in IntelliJ - be aware.`); | ||
} | ||
Global_1.Global.isVerbose() && console.log('successfully updated', repoName); | ||
}); | ||
} | ||
checkout(repo, repoName, repoProperties) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (repoProperties.commit) { | ||
Global_1.Global.isVerbose() && console.log(repoName, 'checking out commit', repoProperties.commit); | ||
yield repo.fetch({ branch: repoProperties.branch }); | ||
yield repo.checkoutCommit(repoProperties.commit); | ||
} | ||
else if (repoProperties.tag || repoProperties.latestTagForRelease) { | ||
const tagToCheckout = repoProperties.tag || repoProperties.latestTagForRelease; | ||
yield repo.resetHard(); | ||
yield repo.checkoutBranch(branch); | ||
if (commit) { | ||
yield repo.checkoutCommit(commit); | ||
yield repo.fetch({ tag: tagToCheckout }); | ||
yield repo.checkoutTag(tagToCheckout); | ||
yield repo.createBranchForTag(tagToCheckout); | ||
} | ||
else { | ||
// checkout branch | ||
yield repo.resetHard(); | ||
yield repo.checkoutBranch(repoProperties.branch); | ||
if (this.resetToRemote) { | ||
yield repo.resetHard(repoProperties.branch); | ||
} | ||
else if (this.resetToRemote) { | ||
yield repo.resetHard(branch); | ||
} | ||
else if (this.noFetch) { | ||
@@ -118,33 +140,27 @@ // don't update to the remote branch, but stay at the current local branch | ||
else { | ||
yield repo.pullOnlyFastForward(branch); | ||
yield repo.pullOnlyFastForward(repoProperties.branch); | ||
} | ||
} | ||
else { | ||
yield repo.resetHard(); | ||
yield repo.checkoutTag(tag); | ||
yield repo.createBranchForTag(tag); | ||
} | ||
const isGradleBuild = new GradleBuild_1.GradleBuild(pathToRepo).containsGradleBuild(); | ||
if (isGradleBuild !== wasGradleBuild) { | ||
const toFrom = wasGradleBuild ? 'away from' : 'back to'; | ||
console.warn(`WARNING: Repository ${repoName} has changed ${toFrom} a Gradle build!`); | ||
console.warn(` This might cause issues in IntelliJ - be aware.`); | ||
} | ||
Global_1.Global.isVerbose() && console.log('successfully updated', repoName); | ||
}); | ||
} | ||
getTargetBranch(repo, { branch, commit, tag }) { | ||
getTargetBranch(repo, repoProperties) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let targetBranch; | ||
if (branch) { | ||
if (commit) { | ||
targetBranch = commit; | ||
if (repoProperties.commit) { | ||
targetBranch = repoProperties.commit; | ||
} | ||
else if (repoProperties.tag) { | ||
targetBranch = repoProperties.tag; | ||
} | ||
else if (repoProperties.latestTagForRelease) { | ||
targetBranch = repoProperties.latestTagForRelease; | ||
} | ||
else if (repoProperties.branch) { | ||
if (this.resetToRemote) { | ||
targetBranch = `origin/${repoProperties.branch}`; | ||
} | ||
else if (this.resetToRemote) { | ||
targetBranch = `origin/${branch}`; | ||
} | ||
else if (this.noFetch) { | ||
let branchExists; | ||
try { | ||
yield repo.commitExists(branch); | ||
yield repo.commitExists(repoProperties.branch); | ||
branchExists = true; | ||
@@ -155,12 +171,9 @@ } | ||
} | ||
targetBranch = branchExists ? branch : `origin/${branch}`; | ||
targetBranch = branchExists ? repoProperties.branch : `origin/${repoProperties.branch}`; | ||
} | ||
else { | ||
// we did fetch, so the current remote branch is very likely the same as the one we may pull later | ||
targetBranch = `origin/${branch}`; | ||
targetBranch = `origin/${repoProperties.branch}`; | ||
} | ||
} | ||
else { | ||
targetBranch = tag; | ||
} | ||
return targetBranch; | ||
@@ -167,0 +180,0 @@ }); |
@@ -8,8 +8,13 @@ import * as Promise from 'bluebird'; | ||
export declare class WriteRepos extends AbstractReposCommand { | ||
private static readonly PARAMETER_FREEZE; | ||
static readonly PARAMETER_FREEZE: string; | ||
static readonly PARAMETER_UN_FREEZE: string; | ||
static readonly PARAMETER_USE_TAGS: string; | ||
private static readonly RELEASE_VERSION_BRANCH_PATTERN; | ||
private freeze; | ||
private unFreeze; | ||
private useTags; | ||
execute(): Promise<void>; | ||
protected doPrepareAndMayExecute(params: ICommandParameters): boolean; | ||
private handleRepo; | ||
private mapStatus; | ||
private mapCommitStatus; | ||
} |
@@ -8,2 +8,3 @@ "use strict"; | ||
const AbstractReposCommand_1 = require("./AbstractReposCommand"); | ||
const path = require("path"); | ||
/** | ||
@@ -16,2 +17,4 @@ * General write-repos-state command | ||
this.freeze = false; | ||
this.unFreeze = false; | ||
this.useTags = false; | ||
} | ||
@@ -24,3 +27,5 @@ execute() { | ||
const newParentRepos = {}; | ||
states.forEach((s) => newParentRepos[s.repoName] = s.status); | ||
states.forEach((s) => { | ||
newParentRepos[s.repoName] = s.status; | ||
}); | ||
Global_1.Global.isVerbose() && console.log('status and revparse successfully completed'); | ||
@@ -32,3 +37,5 @@ return this.writeNewParentRepos(newParentRepos); | ||
this.freeze = params[WriteRepos.PARAMETER_FREEZE]; | ||
Global_1.Global.isVerbose() && this.freeze && console.log('Freezing repo states'); | ||
this.unFreeze = params[WriteRepos.PARAMETER_UN_FREEZE]; | ||
this.useTags = params[WriteRepos.PARAMETER_USE_TAGS]; | ||
Global_1.Global.isVerbose() && this.freeze && console.log(`Freezing repo states, using tags: ${this.useTags}.`); | ||
return true; | ||
@@ -40,9 +47,55 @@ } | ||
Global_1.Global.isVerbose() && console.log('repoProperties', repoProperties); | ||
const repo = new git_1.Repository(`../${repoName}`); | ||
return repo.status() | ||
.then((status) => this.checkRepoClean(repo, status)) | ||
.then((status) => this.mapStatus(repo, status)) | ||
.then((status) => ({ repoName, status })); | ||
if (this.unFreeze) { | ||
return new Promise((resolve) => { | ||
const current = this.parentRepos[repoName]; | ||
let currentBranch = current.branch; | ||
if (currentBranch.startsWith('release-version/')) { | ||
const match = WriteRepos.RELEASE_VERSION_BRANCH_PATTERN.exec(currentBranch); | ||
if (match.length > 1) { | ||
const major = match[1]; | ||
const minor = match[2]; | ||
currentBranch = `release/${major}.${minor}`; | ||
} | ||
} | ||
const status = { | ||
url: current.url, | ||
branch: currentBranch, | ||
description: current.description ? current.description : repoName | ||
}; | ||
resolve({ repoName, status }); | ||
}); | ||
} | ||
else if (this.freeze && this.useTags) { | ||
return git_1.Repository.getActiveTagOfReleaseBranch(repoName, this.parentRepos[repoName]) | ||
.then((activeTag) => { | ||
const current = this.parentRepos[repoName]; | ||
const status = { | ||
url: current.url, | ||
branch: current.branch, | ||
description: current.description ? current.description : repoName | ||
}; | ||
if (activeTag) { | ||
status.tag = activeTag; | ||
status.tagMarker = activeTag; | ||
Global_1.Global.isVerbose() && console.log(`using tag ${status.tag}`); | ||
} | ||
else { | ||
Global_1.Global.isVerbose() && console.log(`no tag found for ${repoName}`); | ||
if (current.commit) { | ||
Global_1.Global.isVerbose() && console.log('preserving commit'); | ||
status.commit = current.commit; | ||
} | ||
} | ||
return ({ repoName, status }); | ||
}); | ||
} | ||
else { | ||
const repo = new git_1.Repository(path.join(this.rootDir, '..', repoName)); | ||
return repo.status() | ||
.then((status) => this.checkRepoClean(repo, status)) | ||
.then((status) => this.mapCommitStatus(repo, status)) | ||
.then((status) => ({ repoName, status })); | ||
} | ||
} | ||
mapStatus(repo, status) { | ||
mapCommitStatus(repo, status) { | ||
return repo | ||
@@ -59,2 +112,3 @@ .getCurrentCommitHash() | ||
result.commit = commit; | ||
Global_1.Global.isVerbose() && console.log(`using HEAD commit ${result.commit}`); | ||
} | ||
@@ -67,2 +121,5 @@ return result; | ||
WriteRepos.PARAMETER_FREEZE = 'freeze'; | ||
WriteRepos.PARAMETER_UN_FREEZE = 'unFreeze'; | ||
WriteRepos.PARAMETER_USE_TAGS = 'useTags'; | ||
WriteRepos.RELEASE_VERSION_BRANCH_PATTERN = new RegExp(/^release-version\/(\d+).(\d+)/); | ||
//# sourceMappingURL=WriteRepos.js.map |
@@ -14,2 +14,3 @@ "use strict"; | ||
const promiseAllSettled_1 = require("../../promiseAllSettled"); | ||
const ReleaseNumber_1 = require("../flow/ReleaseNumber"); | ||
class VisualizeCommand { | ||
@@ -130,3 +131,3 @@ constructor() { | ||
} | ||
if (branch.indexOf('master') === 0) { | ||
if (branch.indexOf(ReleaseNumber_1.ReleaseNumber.MASTER) === 0 || branch.indexOf(ReleaseNumber_1.ReleaseNumber.MAIN) === 0) { | ||
result.push(` "${branch}" [style=bold,color="green"];${os.EOL}`); | ||
@@ -133,0 +134,0 @@ this.styledBranches.push(branch); |
@@ -6,4 +6,4 @@ /** | ||
import { IGitBranchAndCommit, IGitBranchDetails, IGitLogSummary, IGitStatus } from './models'; | ||
import { IRepoStatus } from '../commands/repos/models'; | ||
export declare class Repository { | ||
get baseDir(): string; | ||
private static readonly TRACKING_BRANCH_PATTERN; | ||
@@ -15,4 +15,8 @@ private static readonly ADDITIONAL_INFO_PATTERN; | ||
constructor(repoPath?: string); | ||
static clone(toPath: string, remoteUrl: string, branch: string, depth: number): Promise<Repository>; | ||
private static includeBranch; | ||
static clone(toPath: string, repoProperties: IRepoStatus, depth: number): Promise<Repository>; | ||
static includeBranch(branch: string, regexForExclusion: string, regexForInclusion: string): boolean; | ||
static getLatestTagOfReleaseBranch(repoName: string, repoProperties: IRepoStatus): Promise<string>; | ||
static getActiveTagOfReleaseBranch(repoName: string, repoProperties: IRepoStatus): Promise<string>; | ||
static getLatestTagOfPattern(repoUrl: string, tagPattern: string): Promise<string>; | ||
get baseDir(): string; | ||
checkRepoHasPathInBranch(options: { | ||
@@ -25,3 +29,6 @@ branch: string; | ||
commitExists(hash: string): Promise<string>; | ||
fetch(): Promise<void>; | ||
fetch({ tag, branch }: { | ||
tag?: string; | ||
branch?: string; | ||
}): Promise<void>; | ||
status(): Promise<IGitStatus>; | ||
@@ -59,4 +66,5 @@ checkoutBranch(branch: string | string[]): Promise<void>; | ||
rawWrapper(rawCommand: string[]): Promise<void>; | ||
checkBranchExistsOnRemote(branchName: string): boolean; | ||
private extractTrackingInfoFromLabel; | ||
private getBranchNameIfRemote; | ||
} |
@@ -15,3 +15,9 @@ "use strict"; | ||
constructor(repoPath = './') { | ||
this.git = simpleGit(repoPath); | ||
try { | ||
this.git = simpleGit(repoPath); | ||
} | ||
catch (e) { | ||
console.log(`Error at initialising a new Repository for ${repoPath}!`, e); | ||
throw e; | ||
} | ||
if (Global_1.Global.isVerbose()) { | ||
@@ -25,13 +31,22 @@ this.git.outputHandler((command, stdout, stderr) => { | ||
} | ||
get baseDir() { | ||
return this.git._baseDir; | ||
} | ||
static clone(toPath, remoteUrl, branch, depth) { | ||
static clone(toPath, repoProperties, depth) { | ||
return new Promise((resolve, reject) => { | ||
Global_1.Global.isVerbose() && console.log('cloning branch', branch, 'from', remoteUrl, 'to', toPath); | ||
const options = ['--branch', branch]; | ||
if (depth > 0) { | ||
const options = []; | ||
let refToCheckout = repoProperties.branch; | ||
let refIsTag = false; | ||
if (!repoProperties.commit && (repoProperties.tag || repoProperties.latestTagForRelease)) { | ||
refToCheckout = repoProperties.tag || repoProperties.latestTagForRelease; | ||
refIsTag = true; | ||
} | ||
if (refToCheckout) { | ||
Global_1.Global.isVerbose() && console.log('cloning tag or branch', refToCheckout, 'from', repoProperties.url, 'to', toPath); | ||
options.push('--branch', refToCheckout); | ||
} | ||
else { | ||
Global_1.Global.isVerbose() && console.log('cloning default branch from', repoProperties.url, 'to', toPath); | ||
} | ||
if (depth > 0 && !repoProperties.commit) { | ||
options.push('--depth', depth.toString(10)); | ||
} | ||
simpleGit().clone(remoteUrl, toPath, options, (err) => { | ||
simpleGit().clone(repoProperties.url, toPath, options, (err) => { | ||
if (err) { | ||
@@ -41,3 +56,12 @@ reject(err); | ||
else { | ||
resolve(new Repository(toPath)); | ||
const newRepo = new Repository(toPath); | ||
if (refIsTag) { | ||
newRepo.createBranchForTag(refToCheckout) | ||
.then(() => { | ||
resolve(newRepo); | ||
}); | ||
} | ||
else { | ||
resolve(newRepo); | ||
} | ||
} | ||
@@ -59,2 +83,68 @@ }); | ||
} | ||
static getLatestTagOfReleaseBranch(repoName, repoProperties) { | ||
return new Promise((resolve, reject) => { | ||
if (repoProperties.branch.startsWith('release/')) { | ||
const currentReleaseVersion = repoProperties.branch.substring('release/'.length); | ||
Global_1.Global.isVerbose() && console.log(repoName, `release version: ${currentReleaseVersion}`); | ||
this.getLatestTagOfPattern(repoProperties.url, `version/${currentReleaseVersion}.*`) | ||
.then((latestTag) => { | ||
Global_1.Global.isVerbose() && console.log(repoName, `latest tag for release ${currentReleaseVersion}: ${latestTag}`); | ||
resolve(latestTag); | ||
}) | ||
.catch((error) => reject(error)); | ||
} | ||
else { | ||
resolve(null); | ||
} | ||
}); | ||
} | ||
static getActiveTagOfReleaseBranch(repoName, repoProperties) { | ||
return new Promise((resolve, reject) => { | ||
if (repoProperties.tag) { | ||
Global_1.Global.isVerbose() && console.log(repoName, `release version from predefined tag: ${repoProperties.tag}`); | ||
resolve(repoProperties.tag); | ||
} | ||
else if (repoProperties.branch.startsWith('release-version/')) { | ||
const currentReleaseVersion = repoProperties.branch.substring('release-'.length); | ||
Global_1.Global.isVerbose() && console.log(repoName, `release version from local tag branch name: ${currentReleaseVersion}`); | ||
resolve(currentReleaseVersion); | ||
} | ||
else { | ||
Repository.getLatestTagOfReleaseBranch(repoName, repoProperties) | ||
.then((latestTag) => { | ||
if (latestTag) { | ||
Global_1.Global.isVerbose() && console.log(repoName, `release version from latest tag: ${latestTag}`); | ||
} | ||
resolve(latestTag); | ||
}) | ||
.catch((error) => reject(error)); | ||
} | ||
}); | ||
} | ||
static getLatestTagOfPattern(repoUrl, tagPattern) { | ||
return new Promise((resolve, reject) => { | ||
Global_1.Global.isVerbose() && console.log(`Getting the last tag with pattern ${tagPattern}:\n`); | ||
simpleGit().listRemote(['--tags', '--refs', '--sort=version:refname', repoUrl, tagPattern], (err, result) => { | ||
if (err) { | ||
Global_1.Global.isVerbose() && console.log(repoUrl, ': ls-remote failed!\n', err); | ||
reject(err); | ||
} | ||
else { | ||
Global_1.Global.isVerbose() && console.log('result of git ls-remote:\n', result); | ||
const lines = result.match(/[^\r\n]+/g); | ||
if (lines) { | ||
const lastLine = lines.slice(-1)[0]; | ||
const tagMatch = lastLine.match(tagPattern); | ||
resolve(tagMatch ? tagMatch[0] : null); | ||
} | ||
else { | ||
resolve(null); | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
get baseDir() { | ||
return this.git._baseDir; | ||
} | ||
checkRepoHasPathInBranch(options) { | ||
@@ -108,6 +198,11 @@ const pathname = options.pathname; | ||
} | ||
fetch() { | ||
fetch({ tag, branch }) { | ||
return new Promise((resolve, reject) => { | ||
Global_1.Global.isVerbose() && console.log(`fetching repo ${this.repoName}`); | ||
this.git.fetch((err) => { | ||
const options = []; | ||
if (branch || tag) { | ||
options.push('--no-tags', '--force', 'origin'); | ||
tag ? options.push('tag', tag) : options.push(branch); | ||
} | ||
Global_1.Global.isVerbose() && console.log(`fetching repo ${this.repoName} with options ${options}`); | ||
this.git.fetch(options, (err) => { | ||
if (err) { | ||
@@ -187,10 +282,11 @@ reject(err); | ||
return new Promise((resolve, reject) => { | ||
Global_1.Global.isVerbose() && console.log(`Creating branch ${tag} for tag ${tag}`); | ||
this.git.checkoutLocalBranch(tag, (err) => { | ||
const branchName = tag.startsWith('version/') ? `release-${tag}` : tag; | ||
Global_1.Global.isVerbose() && console.log(`Creating branch ${branchName} for tag ${tag}`); | ||
this.git.checkout(['-B', branchName], (err) => { | ||
if (err) { | ||
Global_1.Global.isVerbose() && console.error(`failed to create branch ${tag} for tag ${tag}`, err); | ||
Global_1.Global.isVerbose() && console.error(`failed to create branch ${branchName} for tag ${tag}`, err); | ||
reject(err); | ||
} | ||
else { | ||
Global_1.Global.isVerbose() && console.log(`Created branch ${tag} for tag ${tag}`); | ||
Global_1.Global.isVerbose() && console.log(`Created branch ${branchName} for tag ${tag}`); | ||
resolve(); | ||
@@ -574,2 +670,12 @@ } | ||
} | ||
checkBranchExistsOnRemote(branchName) { | ||
let result = ''; | ||
try { | ||
result = child_process_1.execSync(`git rev-parse origin/${branchName}`).toString(); | ||
} | ||
catch (e) { | ||
throw new Error(`Branch ${branchName} doesn't exist. ${e}`); | ||
} | ||
return result === '' ? false : true; | ||
} | ||
extractTrackingInfoFromLabel(label) { | ||
@@ -576,0 +682,0 @@ const match = Repository.TRACKING_BRANCH_PATTERN.exec(label); |
{ | ||
"name": "@cplace/cli", | ||
"version": "0.19.12", | ||
"version": "0.19.15", | ||
"description": "", | ||
@@ -25,4 +25,11 @@ "main": "dist/index.js", | ||
"jest": { | ||
"rootDir": "src/", | ||
"preset": "ts-jest" | ||
"testTimeout": 10000, | ||
"globals": { | ||
"ts-jest": { | ||
"diagnostics": false | ||
} | ||
}, | ||
"preset": "ts-jest", | ||
"rootDir": "test", | ||
"verbose": true | ||
}, | ||
@@ -29,0 +36,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
401489
5524
3