switch, update, flyway support

main
guidohollander 3 years ago
parent f1adb7e93b
commit 41ece8d1b9

@ -0,0 +1,29 @@
{
"paths": {
"flywayPath": "c:/fw/flyway",
"componentMigrationsFolder": "_CONTINUOUS_DELIVERY/DATABASE_MIGRATIONS/",
"implementationMigrationsFolder": "_GENERAL/DATABASE_MIGRATIONS/"
},
"exclusions": [
".metadata",
"TEST",
"com.bearingpoint.ird.anguilla.remoting_connector",
"console-1.0-dev.jar",
"migrations.bat",
"etc"
],
"implementations": [
{
"name": "MTS_ANGUILLA",
"functionalName": "MTS Anguilla",
"customer": "ANGUILLA ",
"customerCode": "aia_mts",
"path": "D:\\repo\\aia_mts_trunk\\",
"class": "MxS",
"baseUrl": "https://svn.hollanderconsulting.nl",
"url": "/svn/MTS_ANGUILLA/trunk",
"dbConnectionString": "jdbc:sqlserver://localhost:1433;databaseName=aia_mts;integratedSecurity=true;",
"selected": true
}
]
}

@ -41,7 +41,7 @@
"class": "MxS", "class": "MxS",
"baseUrl": "https://svn.bearingpointcaribbean.com", "baseUrl": "https://svn.bearingpointcaribbean.com",
"url": "/svn/MTS_SKN/trunk", "url": "/svn/MTS_SKN/trunk",
"selected": true "selected": false
}, },
{ {
"name": "ONLINE_SKN", "name": "ONLINE_SKN",
@ -75,7 +75,7 @@
"class": "MxS", "class": "MxS",
"baseUrl": "https://svn.bearingpointcaribbean.com", "baseUrl": "https://svn.bearingpointcaribbean.com",
"url": "/svn/MTS_GRENADA/trunk", "url": "/svn/MTS_GRENADA/trunk",
"selected": true "selected": false
}, },
{ {
"name": "ONLINE_GD", "name": "ONLINE_GD",

@ -1,24 +1,67 @@
import { DirType } from "./types.js"; import { DirType } from "./types.js";
import { ExternalComponentResource } from "./ExternalComponentResource.js"; import { ExternalComponentResource } from "./ExternalComponentResource.js";
import { svnCheckout, svnInfo } from "./svn.js";
export class AngloResource { export class AngloResource {
protected _url = "";
protected _baseUrl = ""; protected _baseUrl = "";
protected _fullUrl = ""; protected _fullUrl = "";
protected _bareComponentUrl = ""; protected _bareComponentUrl = "";
protected _repository = "";
protected _repositoryUrl = "";
protected _componentFolder?: string; protected _componentFolder?: string;
protected _implementationUrl = ""; protected _componentName = "";
protected _dirType: DirType = DirType.trunk; protected _dirType: DirType = DirType.trunk;
protected _implementationUrl = "";
protected _isMissing?: boolean = undefined;
protected _workingCopyPath = "";
protected _workingCopyResource?: AngloResource = undefined;
protected _repository = "";
protected _repositoryUrl = "";
protected _switchNeeded?: boolean = undefined;
protected _url = "";
protected _version?: string; protected _version?: string;
protected _checkoutNumberOfResults = 0;
protected _componentName = ""; protected _checkoutRevision = 0;
constructor(baseUrl: string, url: string) { constructor(baseUrl: string, url: string) {
this.parseUrl(baseUrl, url); this.parseUrl(baseUrl, url);
} }
public async getWorkingCopyResource(): Promise<AngloResource | undefined> {
if (this._workingCopyPath) {
svnInfo(this._workingCopyPath)
.then((svnResource) => {
const url = svnResource.fullUrl.replace(this._baseUrl, "");
this._isMissing = svnResource.fullUrl === undefined;
if (!this._isMissing) {
this._workingCopyResource = new AngloResource(this._baseUrl, url);
this._switchNeeded =
this.dirTypeAndVersion() !==
this._workingCopyResource.dirTypeAndVersion();
return this._workingCopyResource;
}
})
.catch(async (error) => {
console.log("caught", error);
if (error.message === "NotAWorkingCopy") {
this._isMissing = true;
const checkoutResult = await svnCheckout(
this._baseUrl,
this._url,
this._workingCopyPath
);
this._checkoutNumberOfResults = checkoutResult.numberOfUpdates;
this._checkoutRevision = checkoutResult.revision;
} else {
console.log("other error");
throw error;
}
});
} else {
this._isMissing = true;
// this._switchNeeded = true;
return undefined;
}
}
componentFolder(): string | undefined { componentFolder(): string | undefined {
return this._componentFolder; return this._componentFolder;
} }
@ -41,6 +84,9 @@ export class AngloResource {
implementationUrl(): string { implementationUrl(): string {
return this._implementationUrl; return this._implementationUrl;
} }
private inspectVersion(): void {
// todo
}
repositoryName(): string { repositoryName(): string {
return this._repository; return this._repository;
} }
@ -118,7 +164,6 @@ export class AngloResource {
this._componentName = segments[trunkTagBranchIndex + 1]; this._componentName = segments[trunkTagBranchIndex + 1];
} }
} }
this._implementationUrl = segments this._implementationUrl = segments
.slice(0, trunkTagBranchIndex + (this._version === undefined ? 1 : 2)) .slice(0, trunkTagBranchIndex + (this._version === undefined ? 1 : 2))
.join("/"); .join("/");
@ -140,4 +185,19 @@ export class AngloResource {
return `${segments[0]}.${segments[1]}.${lastSegments.join("-")}`; return `${segments[0]}.${segments[1]}.${lastSegments.join("-")}`;
} }
} }
isMissingLocally(): boolean | undefined {
return this._isMissing;
}
localDiffersFromExternal(): boolean | undefined {
return this._switchNeeded;
}
// private getLocalPath(): string {
// if (this instanceof ExternalComponentResource) {
// return 'c:\\ext';
// } else if (this instanceof ImplementationResource) {
// return 'c:\\imp';
// // } else if (this instanceof InternalComponentResource) {
// // return 'c:\\int';
// } else return '';
// }
} }

@ -0,0 +1,36 @@
import * as fs from "fs";
import { Settings } from "./types";
export class ConfigurationManager {
private static instance: ConfigurationManager;
private settings: Settings | null = null;
private constructor() {
// private
}
public static getInstance(): ConfigurationManager {
if (!ConfigurationManager.instance) {
ConfigurationManager.instance = new ConfigurationManager();
}
return ConfigurationManager.instance;
}
public loadSettings(filePath: string): void {
const fileData = fs.readFileSync(filePath, "utf-8");
this.settings = JSON.parse(fileData) as Settings;
}
public getSettings(): Settings {
if (!this.settings) {
throw new Error("Settings not loaded. Call loadSettings() first.");
}
return this.settings;
}
}
export function getConfig(): Settings {
const configManager = ConfigurationManager.getInstance();
configManager.loadSettings("./anglo-helper-config.json");
return configManager.getSettings();
}

@ -1,10 +1,26 @@
import * as fs from "fs";
import { AngloVersionedResource } from "./AngloVersionedResource.js"; import { AngloVersionedResource } from "./AngloVersionedResource.js";
import { ImplementationResource } from "./ImplementationResource.js"; import { ImplementationResource } from "./ImplementationResource.js";
import { ImplementationInternal } from "./ImplementationInternalClass.js"; import { AngloResource } from "./AngloResource.js";
import { getConfig } from "./ConfigurationManager.js";
import {
svnPropGet,
svnSwitchExternal,
svnSwitchWorkingCopy,
svnUpdate,
} from "./svn.js";
import { DirType } from "./types.js";
import { flywayMigrate } from "./flyway.js";
import { unifyPath } from "./parser.js";
const config = getConfig();
export class ExternalComponentResource extends AngloVersionedResource { export class ExternalComponentResource extends AngloVersionedResource {
public _parentSolutionImplementation: ImplementationResource; public _parentSolutionImplementation: ImplementationResource;
private implementationInternal: ImplementationInternal; private _updated = false;
private _switchedWorkingCopy = false;
private _flywayed = false;
private _dbScriptFolder = "";
constructor( constructor(
baseUrl: string, baseUrl: string,
@ -13,12 +29,47 @@ export class ExternalComponentResource extends AngloVersionedResource {
) { ) {
super(baseUrl, url); super(baseUrl, url);
this._parentSolutionImplementation = parentSolutionImplementation; this._parentSolutionImplementation = parentSolutionImplementation;
this.implementationInternal = new ImplementationInternal(); this._workingCopyPath =
parentSolutionImplementation._implementationContext && this._componentName
? `${parentSolutionImplementation._implementationContext.path}${this._componentName}`
: "";
this._dbScriptFolder = unifyPath(
`${this._workingCopyPath}/${config.paths.componentMigrationsFolder}`
);
this.getWorkingCopyResource();
} }
dbScriptFolder() {
return this._dbScriptFolder;
}
parentSolutionImplementation(): ImplementationResource { parentSolutionImplementation(): ImplementationResource {
return this._parentSolutionImplementation; return this._parentSolutionImplementation;
} }
public async flyway(): Promise<boolean> {
try {
if (
this._workingCopyPath &&
this._parentSolutionImplementation._implementationContext
) {
if (fs.existsSync(this._dbScriptFolder)) {
//returns an array of flywayed
this._flywayed = await flywayMigrate(
this._componentName,
this._dbScriptFolder,
this._parentSolutionImplementation._implementationContext
.dbConnectionString
);
return this._flywayed;
} else return false;
} else return false;
} catch (error) {
console.log("Other error", error);
throw error;
}
}
public flywayed(): boolean {
return this._flywayed;
}
public async tag(): Promise<ExternalComponentResource> { public async tag(): Promise<ExternalComponentResource> {
// todo // todo
return new ExternalComponentResource( return new ExternalComponentResource(
@ -27,7 +78,52 @@ export class ExternalComponentResource extends AngloVersionedResource {
this._parentSolutionImplementation this._parentSolutionImplementation
); );
} }
public async update(): Promise<void> { public async switchWorkingCopy(): Promise<boolean> {
await this.implementationInternal.update(this); try {
if (this._switchNeeded) {
this._switchedWorkingCopy = await svnSwitchWorkingCopy(
this._workingCopyPath,
this.baseUrl(),
this.url()
);
return this._switchedWorkingCopy;
} else return false;
} catch (error) {
console.log("Other error", error);
throw error;
}
}
public switchedWorkingCopy(): boolean {
return this._switchedWorkingCopy;
}
public async switchExternal(
target: ExternalComponentResource
): Promise<boolean> {
await svnSwitchExternal(
this.parentSolutionImplementation().baseUrl(),
this.parentSolutionImplementation().url(),
this.componentName(),
this.implementationUrl(),
target.implementationUrl()
);
return true;
}
public async update(): Promise<boolean> {
try {
if (this._workingCopyPath && this.dirType() != DirType.tags) {
//returns an array of updated
this._updated = await svnUpdate(this._workingCopyPath);
return this._updated;
} else return false;
} catch (error) {
console.log("Other error", error);
throw error;
}
}
public updated(): boolean {
return this._updated;
}
workingCopy(): AngloResource | undefined {
return this._workingCopyResource;
} }
} }

@ -1,20 +0,0 @@
import { svnUpdate } from "./svn.js";
import { ExternalComponentResource } from "./ExternalComponentResource";
import { InternalComponentResource } from "./InternalComponentResource";
export class ImplementationInternal {
public async update(
resource: InternalComponentResource | ExternalComponentResource
): Promise<void> {
if (
resource._parentSolutionImplementation._implementationContext &&
resource.componentName()
) {
await svnUpdate(
`${
resource._parentSolutionImplementation._implementationContext.path
}${resource.componentName()}`
);
}
}
}

@ -9,11 +9,13 @@ import { svnList, svnPropGet } from "./svn.js";
import { ExternalComponentResource } from "./ExternalComponentResource.js"; import { ExternalComponentResource } from "./ExternalComponentResource.js";
import { AngloVersionedResource } from "./AngloVersionedResource.js"; import { AngloVersionedResource } from "./AngloVersionedResource.js";
import { InternalComponentResource } from "./InternalComponentResource.js"; import { InternalComponentResource } from "./InternalComponentResource.js";
import { AngloResource } from "./AngloResource.js";
import { move } from "./parser.js";
export class ImplementationResource extends AngloVersionedResource { export class ImplementationResource extends AngloVersionedResource {
protected _externalsCollection: ExternalComponentResource[] = []; protected _externalsCollection: ExternalComponentResource[] = [];
protected _internalsCollection: InternalComponentResource[] = []; protected _internalsCollection: InternalComponentResource[] = [];
public _implementationContext?: TypeSolutionImplementation | undefined; public _implementationContext: TypeSolutionImplementation | undefined;
constructor( constructor(
baseUrl: string, baseUrl: string,
@ -22,8 +24,17 @@ export class ImplementationResource extends AngloVersionedResource {
) { ) {
super(baseUrl, url); super(baseUrl, url);
this._implementationContext = implementationContext; this._implementationContext = implementationContext;
this._workingCopyPath = implementationContext
? implementationContext.path
: "";
}
public async getCheckoutResults(): Promise<number> {
let sum = 0;
for (const angloResource of AngloResource) {
sum += angloResource._checkoutNumberOfResults;
}
return sum;
} }
public async getExternals(): Promise<ExternalComponentResource[]> { public async getExternals(): Promise<ExternalComponentResource[]> {
const propGetResult = await svnPropGet( const propGetResult = await svnPropGet(
"svn:externals", "svn:externals",
@ -44,12 +55,12 @@ export class ImplementationResource extends AngloVersionedResource {
(project) => !project.componentName().includes("FRONTEND") (project) => !project.componentName().includes("FRONTEND")
); );
} }
public async getInternals(): Promise<InternalComponentResource[]> { public async getInternals(): Promise<InternalComponentResource[]> {
const svnListResult: InternalComponent[] = await svnList( const svnListResult: InternalComponent[] = await svnList(
this._baseUrl, this._baseUrl,
this._url this._url
); );
svnListResult.forEach((internalComponent: InternalComponent) => { svnListResult.forEach((internalComponent: InternalComponent) => {
const thisInternal = new InternalComponentResource( const thisInternal = new InternalComponentResource(
this._baseUrl, this._baseUrl,
@ -59,9 +70,18 @@ export class ImplementationResource extends AngloVersionedResource {
this._internalsCollection.push(thisInternal); this._internalsCollection.push(thisInternal);
}); });
//move CD folder to last index position
const componentIndex = this._internalsCollection.findIndex(
(object) => object.componentName() === "_CONTINUOUS_DELIVERY"
);
move(
this._internalsCollection as [],
componentIndex,
this._internalsCollection.length - 1
);
return this._internalsCollection; return this._internalsCollection;
} }
public async deploymentCheck(): Promise<ExternalComponentResource[]> { public async deploymentCheck(): Promise<ExternalComponentResource[]> {
if (!this._externalsCollection) { if (!this._externalsCollection) {
await this.getExternals(); await this.getExternals();
@ -79,4 +99,83 @@ export class ImplementationResource extends AngloVersionedResource {
this._implementationContext this._implementationContext
); );
} }
public async suf(
doSwitch = false,
doUpdate = false,
doFlyway = false
): Promise<{
switched: AngloResource[];
updated: AngloResource[];
flyway: AngloResource[];
}> {
// switch/update/flyway internals
const internalPromises = this._internalsCollection.map(
async (internalComponentResource: InternalComponentResource) => {
if (doUpdate) await internalComponentResource.update();
if (doFlyway) await internalComponentResource.flyway();
}
);
// switch/update/flyway externals
const externalPromises = this._externalsCollection.map(
async (externalComponentResource: ExternalComponentResource) => {
if (doSwitch) await externalComponentResource.switchWorkingCopy();
if (doUpdate) await externalComponentResource.update();
if (doFlyway) await externalComponentResource.flyway();
}
);
// wait for all switch/update/flyway operations to complete
await Promise.all([...internalPromises, ...externalPromises]);
// filter only switched ones
const switchedExternals = this._externalsCollection.filter(
(externalComponentResource) => {
return externalComponentResource.switchedWorkingCopy();
}
);
// filter only updates ones
const updatedInternals = this._internalsCollection.filter(
(internalComponentResource) => {
return internalComponentResource.updated();
}
);
const updatedExternals = this._externalsCollection.filter(
(externalComponentResource) => {
return externalComponentResource.updated();
}
);
// combine internals and externals as AngloResources for return
const updatedAngloResources: AngloResource[] = [];
for (const internalComponentResource of updatedInternals) {
updatedAngloResources.push(internalComponentResource);
}
for (const externalComponentResource of updatedExternals) {
updatedAngloResources.push(externalComponentResource);
}
// filter only flywayed ones
const flywayInternals = this._internalsCollection.filter(
(internalComponentResource) => {
return internalComponentResource.flywayed();
}
);
const flywayExternals = this._externalsCollection.filter(
(externalComponentResource) => {
return externalComponentResource.flywayed();
}
);
// combine internals and externals as AngloResources for return
const flywayAngloResources: AngloResource[] = [];
for (const internalComponentResource of flywayInternals) {
flywayAngloResources.push(internalComponentResource);
}
for (const externalComponentResource of flywayExternals) {
flywayAngloResources.push(externalComponentResource);
}
return {
switched: switchedExternals,
updated: updatedAngloResources,
flyway: flywayAngloResources,
};
}
} }

@ -1,16 +1,16 @@
import * as fs from "fs"; import { TypeSolutionImplementation } from "./types.js";
import { DirType, TypeSolutionImplementation } from "./types.js";
import { InternalComponentResource } from "./InternalComponentResource.js"; import { InternalComponentResource } from "./InternalComponentResource.js";
import { ImplementationResource } from "./ImplementationResource.js"; import { ImplementationResource } from "./ImplementationResource.js";
import { ExternalComponentResource } from "./ExternalComponentResource.js"; import { ExternalComponentResource } from "./ExternalComponentResource.js";
import { getConfig } from "./ConfigurationManager.js";
export class ImplementationResourceReader { export class ImplementationResourceReader {
_solutionImplementationsInFile: TypeSolutionImplementation[]; _solutionImplementationsInFile: TypeSolutionImplementation[];
_solutionImplementationCollection: ImplementationResource[] = []; _solutionImplementationCollection: ImplementationResource[] = [];
constructor() { constructor() {
const json = fs.readFileSync("solutions.json", "utf-8"); const config = getConfig();
this._solutionImplementationsInFile = JSON.parse(json).filter( this._solutionImplementationsInFile = config.implementations.filter(
(solutionImplementation: TypeSolutionImplementation) => { (solutionImplementation: TypeSolutionImplementation) => {
return solutionImplementation.selected; return solutionImplementation.selected;
} }
@ -50,17 +50,19 @@ export class ImplementationResourceReader {
// ); // );
externals.forEach( externals.forEach(
async (externalComponentResource: ExternalComponentResource) => { async (externalComponentResource: ExternalComponentResource) => {
externalComponentResource.update(); // await externalComponentResource.getLocalWorkingCopyResource();
// await externalComponentResource.switch();
// await externalComponentResource.update();
} }
); );
} }
if (autoLoadInternals) { if (autoLoadInternals) {
const internals = await thisSolutionImplementation.getInternals(); const internals = await thisSolutionImplementation.getInternals();
internals.forEach( // internals.forEach(
async (internalComponentResource: InternalComponentResource) => { // async (internalComponentResource: InternalComponentResource) => {
internalComponentResource.update(); // await internalComponentResource.update();
} // }
); // );
} }
this._solutionImplementationCollection.push( this._solutionImplementationCollection.push(
thisSolutionImplementation thisSolutionImplementation

@ -1,10 +1,19 @@
import * as fs from "fs";
import { AngloResource } from "./AngloResource.js"; import { AngloResource } from "./AngloResource.js";
import { ImplementationResource } from "./ImplementationResource.js"; import { ImplementationResource } from "./ImplementationResource.js";
import { ImplementationInternal } from "./ImplementationInternalClass.js"; import { svnSwitchWorkingCopy, svnUpdate } from "./svn.js";
import { DirType } from "./types.js";
import { getConfig } from "./ConfigurationManager.js";
import { flywayMigrate } from "./flyway.js";
import { unifyPath } from "./parser.js";
const config = getConfig();
export class InternalComponentResource extends AngloResource { export class InternalComponentResource extends AngloResource {
public _parentSolutionImplementation: ImplementationResource; public _parentSolutionImplementation: ImplementationResource;
private sharedImplementationInternalClass: ImplementationInternal; private _updated = false;
private _flywayed = false;
private _dbScriptFolder = "";
constructor( constructor(
baseUrl: string, baseUrl: string,
@ -13,17 +22,61 @@ export class InternalComponentResource extends AngloResource {
) { ) {
super(baseUrl, url); super(baseUrl, url);
this._parentSolutionImplementation = parentSolutionImplementation; this._parentSolutionImplementation = parentSolutionImplementation;
this.sharedImplementationInternalClass = new ImplementationInternal(); this._workingCopyPath =
parentSolutionImplementation._implementationContext && this._componentName
? `${parentSolutionImplementation._implementationContext.path}${this._componentName}`
: "";
this._dbScriptFolder = unifyPath(
`${this._workingCopyPath}/${config.paths.implementationMigrationsFolder}`
);
this.getWorkingCopyResource();
} }
public async checkSpecifics(): Promise<boolean> { public async checkSpecifics(): Promise<boolean> {
// todo // todo
return true; return true;
} }
async update(): Promise<void> { public async flyway(): Promise<boolean> {
await this.sharedImplementationInternalClass.update(this); try {
if (
this._workingCopyPath &&
this._parentSolutionImplementation._implementationContext &&
this._componentName === "_CONTINUOUS_DELIVERY"
) {
if (fs.existsSync(this._dbScriptFolder)) {
//returns an array of flywayed
this._flywayed = await flywayMigrate(
"__MigrationsHistory",
this._dbScriptFolder,
this._parentSolutionImplementation._implementationContext
.dbConnectionString
);
return this._flywayed;
} else return false;
} else return false;
} catch (error) {
console.log("Other error", error);
throw error;
}
}
public flywayed(): boolean {
return this._flywayed;
}
public updated(): boolean {
return this._updated;
}
public async update(): Promise<boolean> {
try {
if (this._workingCopyPath && this.dirType() != DirType.tags) {
//returns an array of updated
this._updated = await svnUpdate(this._workingCopyPath);
return this._updated;
} else return false;
} catch (error) {
console.log("Other error", error);
throw error;
}
} }
parentSolutionImplementation(): ImplementationResource { parentSolutionImplementation(): ImplementationResource {
return this._parentSolutionImplementation; return this._parentSolutionImplementation;
} }

@ -1,8 +0,0 @@
[
".metadata",
"TEST",
"com.bearingpoint.ird.anguilla.remoting_connector",
"console-1.0-dev.jar",
"migrations.bat",
"etc"
]

@ -0,0 +1,163 @@
import { exec, ExecException } from "child_process";
import { getConfig } from "./ConfigurationManager.js";
const config = getConfig();
const flywayPath = config.paths.flywayPath;
config.paths.implementationMigrationsFolder;
function execShellCommand(cmd: string): Promise<string> {
return new Promise((resolve, reject) => {
// console.log('cmd', cmd);
exec(
cmd,
{ maxBuffer: 1024 * 500 },
(error: ExecException | null, stdout: string, stderr: string) => {
if (error) {
// Handle known errors
if (error.message.includes("ERROR")) {
reject(error);
} else {
reject(error);
}
}
resolve(stdout || stderr);
}
);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function flywayCmdWithResponse(
flywayCommand: string,
flywayArgs: string[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> {
return new Promise((resolve, reject) => {
const execCommand = `"${flywayPath}" ${flywayCommand} ${flywayArgs.join(
" "
)}`;
execShellCommand(execCommand)
.then((flywayCmdResponse) => {
resolve(flywayCmdResponse);
})
.catch((error) => {
reject(error);
});
});
}
export async function flywayMigrate(
flywayTable: string,
componentScriptFolder: string,
dbConnectionString: string
): Promise<boolean> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const flywayMigrateResponse: any = await flywayCmdWithResponse("migrate", [
"-color=always",
`-locations="filesystem:${componentScriptFolder}"`,
'-schemas="migrations"',
`-table="${flywayTable}"`,
`-url=${dbConnectionString}`,
]);
// migrate
if (!flywayMigrateResponse.includes("No migration necessary")) {
return true;
} else return false;
} catch (error) {
console.log(error);
throw error;
}
}
// async function perform(componentEntry) {
// // set default flyway action to migrate
// let flywayAction = 'migrate';
// const dir = anglo.unifyPath(state.workingCopyFolder) + componentEntry.key;
// const dirWithQuotedProjectName =
// anglo.unifyPath(state.workingCopyFolder) +
// JSON.stringify(componentEntry.key);
// // override default when command line option flywayValidateOnly or flywayRepairOnly is set
// if (clargs.argv.flywayValidateOnly) flywayAction = 'validate'; // instead of migrate
// if (clargs.argv.flywayRepairOnly) flywayAction = 'repair'; // instead of migrate
// const flywayDatabaseTable = '__MigrationsHistory';
// const flywayDatabaseSchema = 'migrations';
// if (
// componentEntry.componentContinuousDeliveryFolderFound ||
// componentEntry.generalContinuousDeliveryFolderFound
// ) {
// // in case of flywayReplaceVariables: check for any uncommitted files beforehand, since all potential changes will be reverted when variables are replaced
// const uncommittedChanges = await checkUncommittedChanges(componentEntry);
// if (!uncommittedChanges) {
// let flywayTable;
// let FlywayDir;
// let FlywayDirWithQuotedProjectName;
// let suffix;
// let flywayLocations;
// if (componentEntry.generalContinuousDeliveryFolderFound) {
// flywayTable = JSON.stringify(flywayDatabaseTable);
// suffix = '/_GENERAL/DATABASE_MIGRATIONS/';
// FlywayDir = `${dir}${suffix}`;
// FlywayDirWithQuotedProjectName = `${dirWithQuotedProjectName}/_GENERAL/DATABASE_MIGRATIONS/`;
// flywayLocations = JSON.stringify(`filesystem:${FlywayDir}`);
// } else {
// flywayTable = JSON.stringify(componentEntry.key);
// suffix = '/_CONTINUOUS_DELIVERY/DATABASE_MIGRATIONS/';
// FlywayDir = `${dir}${suffix}`;
// FlywayDirWithQuotedProjectName = `${dirWithQuotedProjectName}/_CONTINUOUS_DELIVERY/DATABASE_MIGRATIONS/`;
// flywayLocations = JSON.stringify(`filesystem:${FlywayDir}`);
// }
// const flywayDatabaseConnectionString = `jdbc:sqlserver://${
// state.profile.flywayDatabaseServer
// }:${state.profile.flywayDatabaseServerPort};databaseName=${
// state.profile.flywayDatabaseName
// };integratedSecurity=${
// state.profile.flywayDatabaseIntegratedSecurity ? 'true' : 'false'
// };`;
// let credentialsString = '';
// if (!state.profile.flywayDatabaseIntegratedSecurity) {
// credentialsString = `-user=${state.profile.flywayDatabaseUsername} -password=${state.profile.flywayDatabasePassword}`;
// }
// const flywayCommand = `"${state.profile.flywayPath}flyway" ${flywayAction} -color=always -locations=${flywayLocations} -schemas=${flywayDatabaseSchema} -table=${flywayTable} -url=${flywayDatabaseConnectionString} ${credentialsString}`;
// await updateVariablesInSqlFiles(componentEntry, FlywayDir);
// let flywayResult = await util.execShellCommand(flywayCommand);
// // flywayResult = flywayResult.replace(/^Database: .*\(Microsoft SQL Server [\d]+\.[\d]+\)/m, '');
// // flywayResult = flywayResult.replace(/^Flyway Community Edition .*/m, '');
// // flywayResult = flywayResult.replace(/^Current version of schema .*/m, '');
// // flywayResult = flywayResult.trim();
// if (flywayResult.includes('No migration necessary')) {
// consoleLog.logThisLine('[F]', 'gray');
// } else {
// consoleLog.logNewLine('', 'white');
// consoleLog.logNewLine(
// '---------------------------------------------------------------------------------------------',
// 'cyan'
// );
// consoleLog.logNewLine(`${flywayCommand}`, 'cyan');
// consoleLog.logNewLine(
// '---------------------------------------------------------------------------------------------',
// 'cyan'
// );
// consoleLog.logNewLine('', 'white');
// anglo.memorable(
// '[F]',
// state.arrFlywayUpdatedCollection,
// componentEntry,
// flywayResult,
// 'green'
// );
// if (state.profile.verbose) {
// consoleLog.logNewLine('', 'white');
// consoleLog.logNewLine('', 'white');
// consoleLog.logNewLine(flywayResult, 'gray');
// }
// }
// await revertChanges(FlywayDirWithQuotedProjectName);
// } else {
// consoleLog.logNewLine('[F] skipped - uncommitted changes found', 'red');
// }
// } else {
// // flyway enabled, but no continuous delivery folder
// }
// }

@ -2,6 +2,8 @@ import { ImplementationResourceReader } from "./ImplementationResourceReader.js"
import { ImplementationResource } from "./ImplementationResource.js"; import { ImplementationResource } from "./ImplementationResource.js";
import { ExternalComponentResource } from "./ExternalComponentResource.js"; import { ExternalComponentResource } from "./ExternalComponentResource.js";
import { ExternalComponent } from "./types.js"; import { ExternalComponent } from "./types.js";
import { ConfigurationManager } from "./ConfigurationManager.js";
// import { svnPropGet, svnList, svnGetLatestTag, } from "./svn.js"; // import { svnPropGet, svnList, svnGetLatestTag, } from "./svn.js";
// const baseUrl = 'https://svn.bearingpointcaribbean.com'; // const baseUrl = 'https://svn.bearingpointcaribbean.com';
@ -20,6 +22,24 @@ async function deploymentCheck() {
); );
} }
async function sufImplementations() {
const reader = new ImplementationResourceReader();
const implementationResources = await reader.load(true, true);
implementationResources.forEach(
async (implementationResource: ImplementationResource) => {
const implementationSufResults = await implementationResource.suf(
true,
true,
false
);
console.debug("Switched:", implementationSufResults.switched);
console.debug("Updated:", implementationSufResults.updated);
console.debug("Flyway:", implementationSufResults.flyway);
}
);
// console.debug(implementationResources);
}
// async function start() { // async function start() {
// svnList(baseUrl, url) // svnList(baseUrl, url)
// .then((listResult) => { // .then((listResult) => {
@ -98,4 +118,9 @@ async function deploymentCheck() {
//start2(); //start2();
//start3(); //start3();
// start4(); // start4();
deploymentCheck(); //deploymentCheck();
//updateAllImplementations();
// Example usage:
sufImplementations();

@ -43,6 +43,14 @@ export function parseExternals(
}); });
} }
export function unifyPath(angloPath: string) {
return angloPath.toString().replaceAll("\\", "/");
}
export function move(array: [], from: number, to: number, on = 1) {
// eslint-disable-next-line no-sequences
return array.splice(to, 0, ...array.splice(from, on)), array;
}
// splittedExternals.forEach((entry) => { // splittedExternals.forEach((entry) => {
// const tidied = anglo.tidyArrayContent(entry); // const tidied = anglo.tidyArrayContent(entry);
// // for componentBaseFolder. If domain-specific, keep first 3, else keep first 4 parts // // for componentBaseFolder. If domain-specific, keep first 3, else keep first 4 parts

@ -1,5 +1,4 @@
import exclusions from "./data/exclude.json" assert { type: "json" }; import { exec, ExecException } from "child_process";
import { exec } from "child_process";
import { parseString } from "xml2js"; import { parseString } from "xml2js";
import { import {
SVNList, SVNList,
@ -7,32 +6,56 @@ import {
SVNProperties, SVNProperties,
DirType, DirType,
InternalComponent, InternalComponent,
SvnResource,
SVNCheckoutResponse,
SVNCheckoutResult,
} from "./types.js"; } from "./types.js";
import { parseExternals } from "./parser.js"; import { parseExternals } from "./parser.js";
import { getConfig } from "./ConfigurationManager.js";
function execShellCommand(cmd: string) { function execShellCommand(cmd: string): Promise<string> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
exec(cmd, { maxBuffer: 1024 * 500 }, (error, stdout, stderr) => { exec(
cmd,
{ maxBuffer: 1024 * 500 },
(error: ExecException | null, stdout: string, stderr: string) => {
if (error) { if (error) {
console.warn(error); // Handle known errors
if (
error.message.includes("not a working copy") ||
error.message.includes("does not exist") ||
error.message.includes("None of the targets are working copies")
) {
reject(new Error("NotAWorkingCopy"));
} else {
// Reject the promise with unknown errors
console.log("Unknown error occurred", error);
reject(new Error("Unknown error occurred"));
}
} }
resolve(stdout || stderr); resolve(stdout || stderr);
}); }
);
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
async function svnCmdWithResponse( async function svnCmdWithResponse(
cmd: string, cmd: string,
baseUrl: string, baseUrl?: string,
url: string, url?: string,
returnRaw = false localPath?: string,
returnXml = true
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> { ): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const execCommand = `svn ${cmd} "${baseUrl}${url}" --xml`; let execCommand = `svn ${cmd} `;
if (baseUrl && url) execCommand += `"${baseUrl}${url}" `;
if (localPath) execCommand += `"${localPath}" `;
if (returnXml) execCommand += `--xml`;
execShellCommand(execCommand) execShellCommand(execCommand)
.then((svnCmdResponse) => { .then((svnCmdResponse) => {
if (returnXml) {
parseString( parseString(
svnCmdResponse as SVNList | SVNProperties, svnCmdResponse as SVNList | SVNProperties,
(err, result) => { (err, result) => {
@ -40,15 +63,14 @@ async function svnCmdWithResponse(
reject(err); reject(err);
return; return;
} }
if (returnRaw) {
resolve(result);
} else {
const json = JSON.stringify(result); const json = JSON.stringify(result);
const svnCmdResponseJson = JSON.parse(json); const svnCmdResponseJson = JSON.parse(json);
resolve(svnCmdResponseJson); resolve(svnCmdResponseJson);
} }
}
); );
} else {
resolve(svnCmdResponse);
}
}) })
.catch((error) => { .catch((error) => {
reject(error); reject(error);
@ -56,16 +78,49 @@ async function svnCmdWithResponse(
}); });
} }
export async function svnCheckout(
baseUrl: string,
url: string,
localPath: string
): Promise<SVNCheckoutResponse> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const svnCheckoutResponse: any = await svnCmdWithResponse(
"checkout",
baseUrl,
url,
localPath,
false
);
// checkout
if (svnCheckoutResponse.startsWith("A")) {
const updateRows = svnCheckoutResponse.split(/\r\n/);
const numberOfUpdates = updateRows.length - 3;
const revision = updateRows[updateRows.length - 2].match(/\d+/)?.[0];
return { numberOfUpdates, revision };
} else {
return { numberOfUpdates: 0, revision: 0 };
}
} catch (error) {
console.log(error);
throw error;
}
}
export async function svnList( export async function svnList(
baseUrl: string, baseUrl: string,
url: string url: string
): Promise<InternalComponent[]> { ): Promise<InternalComponent[]> {
try { try {
const svnListResponse: InternalComponent[] = await svnCmdWithResponse( const config = getConfig();
const exclusions = config.exclusions;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const svnListResponse: any = await svnCmdWithResponse(
"list", "list",
baseUrl, baseUrl,
url, url,
false undefined,
true
); );
type arrayOfSVNList = { type arrayOfSVNList = {
@ -87,6 +142,31 @@ export async function svnList(
} }
} }
export async function svnInfo(
// baseUrl: string,
// url: string,
localPath: string
): Promise<SvnResource> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const svnInfoResponse: any = await svnCmdWithResponse(
"info",
undefined,
undefined,
localPath,
true
);
const fullUrl = svnInfoResponse.info.entry[0].url[0];
const revision = svnInfoResponse.info.entry[0].$.revision;
const returnObject: SvnResource = { fullUrl, revision };
return returnObject;
} catch (error) {
console.log(error);
throw error;
}
}
async function svnCmdWithoutResponse( async function svnCmdWithoutResponse(
cmd: string, cmd: string,
localPath: string localPath: string
@ -96,7 +176,6 @@ async function svnCmdWithoutResponse(
const execCommand = `svn ${cmd} "${localPath}"`; const execCommand = `svn ${cmd} "${localPath}"`;
execShellCommand(execCommand) execShellCommand(execCommand)
.then((svnCmdResponse) => { .then((svnCmdResponse) => {
console.log(`Performed ${execCommand}`);
resolve(svnCmdResponse); resolve(svnCmdResponse);
}) })
.catch((error) => { .catch((error) => {
@ -105,16 +184,73 @@ async function svnCmdWithoutResponse(
}); });
} }
export async function svnUpdate(localPath: string): Promise<void> { export async function svnUpdate(localPath: string): Promise<boolean> {
try { try {
await svnCmdWithoutResponse("cleanup", localPath); await svnCmdWithoutResponse("cleanup", localPath);
const updateResponse = await svnCmdWithResponse(
"update",
undefined,
undefined,
localPath,
false
);
return (
!updateResponse.includes("At revision") ||
updateResponse.includes("Updated to")
);
} catch (error) { } catch (error) {
console.log(error); console.log("svnUpdateFailed", error);
throw new Error("svnUpdateFailed");
} }
}
export async function svnSwitchWorkingCopy(
localPath: string,
targetBaseUrl: string,
targetUrl: string
): Promise<boolean> {
try { try {
await svnCmdWithoutResponse("update", localPath); await svnCmdWithoutResponse("cleanup", localPath);
const switchResponse = await svnCmdWithResponse(
"switch",
targetBaseUrl,
targetUrl,
localPath,
false
);
return (
switchResponse.includes("At revision") ||
switchResponse.includes("Updated to revision")
);
} catch (error) { } catch (error) {
console.log(error); console.log("svnSwitchFailed", error);
throw new Error("svnSwitchFailed");
}
}
export async function svnSwitchExternal(
baseUrl: string,
url: string,
componentName: string, // in tortoise this is called Local path while editing svn:externals definitions
sourceUrl: string,
targetUrl: string
): Promise<boolean> {
try {
// get fresh raw externals
const propGetResult = await svnPropGet("svn:externals", baseUrl, url);
console.log(propGetResult);
// find
// if found
// propGetResult.replace()
// svmuc back as externals
//else throw error
const switchResponse = "";
return (
!switchResponse.includes("At revision") ||
switchResponse.includes("Updated to")
);
} catch (error) {
console.log("svnSwitchFailed", error);
throw new Error("svnSwitchFailed");
} }
} }
@ -127,6 +263,7 @@ export async function svnPropGet(
`propget ${property}`, `propget ${property}`,
baseUrl, baseUrl,
url, url,
undefined,
true true
); );
const rawExternals: string = const rawExternals: string =
@ -139,7 +276,7 @@ export async function svnTagsBranchesList(
baseUrl: string, baseUrl: string,
url: string, // expects url that has tags and branches url: string, // expects url that has tags and branches
latestOnly = false latestOnly = false
): Promise<string[]> { ): Promise<string[] | undefined> {
try { try {
url = url.replace(/trunk$/, ""); url = url.replace(/trunk$/, "");
url = url.replace(/tags$/, ""); url = url.replace(/tags$/, "");
@ -148,7 +285,9 @@ export async function svnTagsBranchesList(
const svnListResponse: SVNList = await svnCmdWithResponse( const svnListResponse: SVNList = await svnCmdWithResponse(
"list", "list",
baseUrl, baseUrl,
url url,
undefined,
true
); );
const regex = /^[0-9.]+$/; // Regular expression for semantic version format const regex = /^[0-9.]+$/; // Regular expression for semantic version format
type arrayOfSVNList = { type arrayOfSVNList = {
@ -207,27 +346,27 @@ export async function svnTagsBranchesList(
export async function svnGetLatestTag( export async function svnGetLatestTag(
baseUrl: string, baseUrl: string,
url: string url: string
): Promise<string[]> { ): Promise<string[] | undefined> {
return svnTagsBranchesList(DirType.tags, baseUrl, url, true); return svnTagsBranchesList(DirType.tags, baseUrl, url, true);
} }
export async function svnGetLatestBranch( export async function svnGetLatestBranch(
baseUrl: string, baseUrl: string,
url: string url: string
): Promise<string[]> { ): Promise<string[] | undefined> {
return svnTagsBranchesList(DirType.branches, baseUrl, url, true); return svnTagsBranchesList(DirType.branches, baseUrl, url, true);
} }
export async function svnGetTagList( export async function svnGetTagList(
baseUrl: string, baseUrl: string,
url: string url: string
): Promise<string[]> { ): Promise<string[] | undefined> {
return svnTagsBranchesList(DirType.tags, baseUrl, url, false); return svnTagsBranchesList(DirType.tags, baseUrl, url, false);
} }
export async function svnGetBranchesList( export async function svnGetBranchesList(
baseUrl: string, baseUrl: string,
url: string url: string
): Promise<string[]> { ): Promise<string[] | undefined> {
return svnTagsBranchesList(DirType.branches, baseUrl, url, false); return svnTagsBranchesList(DirType.branches, baseUrl, url, false);
} }

@ -1,5 +1,15 @@
import { ExternalComponentResource } from "./ExternalComponentResource"; import { ExternalComponentResource } from "./ExternalComponentResource";
export enum SVNCheckoutResult {
at_revision,
missing,
}
export type SVNCheckoutResponse = {
numberOfUpdates: number;
revision: number;
};
export type SVNProperties = { export type SVNProperties = {
properties: { properties: {
target: { target: {
@ -39,6 +49,10 @@ export type SVNList = {
}; };
}; };
export type ErrorType = {
message: string;
};
export type ExternalComponent = { export type ExternalComponent = {
original: string; original: string;
key: string; key: string;
@ -65,16 +79,22 @@ export enum DirType {
trunk = "trunk", trunk = "trunk",
} }
export type TypeSolutionImplementation = { // export type TypeSolutionImplementation = {
name: string; // name: string;
functionalName: string; // functionalName: string;
customer: string; // customer: string;
customerCode: string; // customerCode: string;
path: string; // path: string;
class: string; // class: string;
baseUrl: string; // baseUrl: string;
url: string; // url: string;
selected: boolean; // dbConnectionString: string;
// selected: boolean;
// };
export type SvnResource = {
fullUrl: string;
revision: number;
}; };
export type ApiExternalsResponse = { export type ApiExternalsResponse = {
@ -90,3 +110,28 @@ export type ApiExternalsResponse = {
}; };
}; };
}; };
export interface TypeSolutionImplementation {
name: string;
functionalName: string;
customer: string;
customerCode: string;
path: string;
class: string;
baseUrl: string;
url: string;
dbConnectionString: string;
selected: boolean;
}
export interface Paths {
flywayPath: string;
componentMigrationsFolder: string;
implementationMigrationsFolder: string;
}
export interface Settings {
paths: Paths;
exclusions: string[];
implementations: TypeSolutionImplementation[];
}

@ -39,7 +39,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
"resolveJsonModule": true /* Enable importing .json files. */, // "resolveJsonModule": true /* Enable importing .json files. */,
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

Loading…
Cancel
Save

Powered by TurnKey Linux.