Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

Commit

Permalink
Migrate sfpowerscripts artifact to custom setting (#545)
Browse files Browse the repository at this point in the history
* Implement SfpowerscriptsArtifact as a custom setting

* Add log statement

* Set isMigrated to true

* Backwards-compatibility for custom object SfpowerscriptsArtifact__c

* Throw error if query for SfpowerscriptsArtifact fails unexpectedly

Prevent the updater or checker from updating sfpowerscripts artifacts if either
of the queries for SfpowerscriptsArtifact__c or SfpowerscriptsArtifact2__c fail
due to reason other than not existing.

* Run SfpowerscriptsArtifact query only if failed previously

* Move migration logic to method

* Fix error handling

Fix missing await on ArtifactMigrator.exec

Fix duplicate migrations

* Update sfpowerscripts-artifact package version ID

* Revert package name

* Fix ArtifactsToOrg unit tests

Mock resolved value for ArtifactorMigrator.exec()
  • Loading branch information
aly76 authored Jun 8, 2021
1 parent 2bcb861 commit 45d7c03
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 203 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import InstalledAritfactsFetcher from "./InstalledAritfactsFetcher";
import PackageMetadata from "../PackageMetadata";
import SFPLogger from "../utils/SFPLogger";
import ArtifactMigrator from "./ArtifactMigrator";

export default class ArtifactInstallationStatusChecker {

Expand All @@ -10,10 +11,12 @@ export default class ArtifactInstallationStatusChecker {
packageMetadata: PackageMetadata,
isHandledByCaller: boolean
): Promise<{isInstalled:boolean,versionNumber?:string}> {
if (isHandledByCaller) return {isInstalled:false}; //This is already handled by the caller, in that case if it reached here, we should
if (isHandledByCaller) return {isInstalled:false}; //This is already handled by the caller, in that case if it reached here, we should
//always install
let result:{isInstalled:boolean,versionNumber?:string}={isInstalled:false};
try {
await ArtifactMigrator.exec(target_org);

SFPLogger.log(`Querying for version of ${packageMetadata.package_name} in the Org..`);
result.isInstalled=false;
let installedArtifacts = await InstalledAritfactsFetcher.getListofArtifacts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PackageMetadata from "../PackageMetadata";
import SFPLogger, { LoggerLevel } from "../utils/SFPLogger";
import InstalledAritfactsFetcher from "./InstalledAritfactsFetcher";
const retry = require("async-retry");
import ArtifactMigrator from "./ArtifactMigrator";

//Update sfpowerscripts Artifats installed in an Org
export default class ArtifactInstallationStatusUpdater {
Expand All @@ -20,6 +21,8 @@ export default class ArtifactInstallationStatusUpdater {
//just ignore

try {
await ArtifactMigrator.exec(target_org);

return await ArtifactInstallationStatusUpdater.updateArtifact(target_org, packageMetadata, packageLogger);
} catch (error) {
SFPLogger.log(
Expand Down Expand Up @@ -52,12 +55,12 @@ export default class ArtifactInstallationStatusUpdater {
SFPLogger.log("Updating Org with new Artifacts "+packageName+" "+packageMetadata.package_version_number+" "+(artifactId?artifactId:""), null, packageLogger, LoggerLevel.INFO);
if (artifactId == null) {
cmdOutput = child_process.execSync(
`sfdx force:data:record:create --json -s SfpowerscriptsArtifact__c -u ${username} -v "Name=${packageName} Tag__c=${packageMetadata.tag} Version__c=${packageMetadata.package_version_number} CommitId__c=${packageMetadata.sourceVersion}"`,
`sfdx force:data:record:create --json -s ${ArtifactMigrator.objectApiName} -u ${username} -v "Name=${packageName} Tag__c=${packageMetadata.tag} Version__c=${packageMetadata.package_version_number} CommitId__c=${packageMetadata.sourceVersion}"`,
{ encoding: "utf8",stdio:"pipe"}
);
} else if (artifactId) {
cmdOutput = child_process.execSync(
`sfdx force:data:record:update --json -s SfpowerscriptsArtifact__c -u ${username} -v "Name=${packageName} Tag__c=${packageMetadata.tag} Version__c=${packageMetadata.package_version_number} CommitId__c=${packageMetadata.sourceVersion}" -i ${artifactId}`,
`sfdx force:data:record:update --json -s ${ArtifactMigrator.objectApiName} -u ${username} -v "Name=${packageName} Tag__c=${packageMetadata.tag} Version__c=${packageMetadata.package_version_number} CommitId__c=${packageMetadata.sourceVersion}" -i ${artifactId}`,
{ encoding: "utf8" ,stdio:"pipe"}
);
}
Expand Down
181 changes: 181 additions & 0 deletions packages/core/src/artifacts/ArtifactMigrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import child_process = require("child_process");
import * as fs from "fs-extra";
const retry = require("async-retry");

export default class ArtifactMigrator {

/**
* API name of latest SfpowerscriptsArtifact object installed in org
*/
public static objectApiName: string = null;

private static isMigrated: boolean = false;

private static isSfpowerscriptsArtifact2Exist: boolean;
private static isSfpowerscriptsArtifactExist: boolean;

private static sfpowerscriptsArtifact2Records;
private static sfpowerscriptsArtifactRecords;

public static async exec(username: string): Promise<void> {
if (
ArtifactMigrator.isSfpowerscriptsArtifact2Exist === undefined &&
ArtifactMigrator.isSfpowerscriptsArtifactExist === undefined
) {
ArtifactMigrator.querySfpowerscriptsArtifact2(username);
ArtifactMigrator.querySfpowerscriptsArtifact(username);

if (ArtifactMigrator.isSfpowerscriptsArtifact2Exist) {
ArtifactMigrator.objectApiName = "SfpowerscriptsArtifact2__c";
} else {
console.log("The custom object SfpowerscriptsArtifact__c will be deprecated in future release. Move to the new version of SfpowerscriptsArtifact to maintain compatibility.");
ArtifactMigrator.objectApiName = "SfpowerscriptsArtifact__c";
}
}

if (
ArtifactMigrator.isSfpowerscriptsArtifact2Exist &&
ArtifactMigrator.isSfpowerscriptsArtifactExist
) {
if (
ArtifactMigrator.sfpowerscriptsArtifact2Records.length === 0 &&
ArtifactMigrator.sfpowerscriptsArtifactRecords.length > 0 &&
!ArtifactMigrator.isMigrated
) {
await ArtifactMigrator.migrate(username);
}
}
}

/**
* Migrate records from SfpowerscriptsArtifact__c to SfpowerscriptsArtifact2__c
*/
private static async migrate(username) {
console.log("Migrating records to SfpowerscriptsArtifact2__c...");

let recordsToImport = {records: []};
ArtifactMigrator.sfpowerscriptsArtifactRecords.forEach((record, idx) => {
recordsToImport.records.push({
attributes: {
type: "SfpowerscriptsArtifact2__c",
referenceId: `SfpowerscriptsArtifact2_${idx}`
},
Name: record.Name,
Tag__c: record.Tag__c,
Version__c: record.Version__c,
CommitId__c: record.CommitId__c
});
});

fs.writeFileSync(
"SfpowerscriptsArtifact2SObjectTreeFile.json",
JSON.stringify(recordsToImport)
);

try {
await retry (
async (bail) => {
let importResultJson = child_process.execSync(
`sfdx force:data:tree:import -f SfpowerscriptsArtifact2SObjectTreeFile.json -u ${username} --json`,
{
encoding: "utf8",
stdio:"pipe"
}
);

let importResult = JSON.parse(importResultJson);
if (importResult.status === 1) {
throw new Error("Failed to migrate records from SfpowerscriptsArtifact__c to SfpowerscriptsArtifact2__c");
} else {
ArtifactMigrator.isMigrated = true;
return;
}
},
{ retries: 3, minTimeout: 2000 }
);
} catch (error) {
console.log(error.message);
throw error;
} finally {
fs.unlinkSync("SfpowerscriptsArtifact2SObjectTreeFile.json");
}
}

/**
* Sets properties for records and existence of SfpowerscriptsArtifact2
* @param username
*/
private static querySfpowerscriptsArtifact2(username): void {
try {
let queryResultJson = child_process.execSync(
`sfdx force:data:soql:query -q "SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM SfpowerscriptsArtifact2__c" -r json -u ${username}`,
{
encoding: "utf8",
stdio:"pipe"
}
);

let queryResult = JSON.parse(queryResultJson);
if (
queryResult.status === 1 &&
queryResult.message.includes("sObject type 'SfpowerscriptsArtifact2__c' is not supported")
) {
ArtifactMigrator.isSfpowerscriptsArtifact2Exist = false;
} else if (queryResult.status === 1) {
console.log(queryResult.message);
throw new Error(queryResult.message);
} else {
ArtifactMigrator.sfpowerscriptsArtifact2Records = queryResult.result.records;
ArtifactMigrator.isSfpowerscriptsArtifact2Exist = true;
}

} catch (error) {
let errorMsg = JSON.parse(error.stdout).message;
if (errorMsg.includes("sObject type 'SfpowerscriptsArtifact2__c' is not supported")) {
ArtifactMigrator.isSfpowerscriptsArtifact2Exist = false;
} else {
console.log(errorMsg);
throw error;
}
}
}

/**
* Set properties for records and existence of SfpowerscriptsArtifact
* @param username
*/
private static querySfpowerscriptsArtifact(username): void {
try {
let queryResultJson = child_process.execSync(
`sfdx force:data:soql:query -q "SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM SfpowerscriptsArtifact__c" -r json -u ${username}`,
{
encoding: "utf8",
stdio:"pipe"
}
);

let queryResult = JSON.parse(queryResultJson);
if (
queryResult.status === 1 &&
queryResult.message.includes("sObject type 'SfpowerscriptsArtifact__c' is not supported")
) {
ArtifactMigrator.isSfpowerscriptsArtifactExist = false;
} else if (queryResult.status === 1) {
console.log(queryResult.message);
throw new Error(queryResult.message);
} else {
ArtifactMigrator.sfpowerscriptsArtifactRecords = queryResult.result.records;
ArtifactMigrator.isSfpowerscriptsArtifactExist = true;
}
} catch (error) {
let errorMsg = JSON.parse(error.stdout).message;
if (errorMsg.includes("sObject type 'SfpowerscriptsArtifact__c' is not supported")) {
ArtifactMigrator.isSfpowerscriptsArtifactExist = false;
} else {
console.log(errorMsg);
throw error;
};
}
}

}
3 changes: 2 additions & 1 deletion packages/core/src/artifacts/InstalledAritfactsFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import child_process = require("child_process");
const retry = require("async-retry");
import ArtifactMigrator from "./ArtifactMigrator";

//Fetch sfpowerscripts Artifats installed in an Org
export default class InstalledAritfactsFetcher {
Expand All @@ -10,7 +11,7 @@ export default class InstalledAritfactsFetcher {
return await retry(
async (bail) => {
let cmdOutput = child_process.execSync(
`sfdx force:data:soql:query -q "SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM SfpowerscriptsArtifact__c" -r json -u ${username}`,
`sfdx force:data:soql:query -q "SELECT Id, Name, CommitId__c, Version__c, Tag__c FROM ${ArtifactMigrator.objectApiName}" -r json -u ${username}`,
{ encoding: "utf8", stdio:"pipe" }
);
let result = JSON.parse(cmdOutput);
Expand Down
4 changes: 3 additions & 1 deletion packages/core/tests/artifacts/ArtifactsToOrg.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { jest,expect } from "@jest/globals";
import child_process = require("child_process");
import ArtifactInstallationStatusUpdater from "../../src/artifacts/ArtifactInstallationStatusUpdater";
import ArtifactMigrator from "../../src/artifacts/ArtifactMigrator";
import InstalledAritfactsFetcher from "../../src/artifacts/InstalledAritfactsFetcher";
import PackageMetadata from "../../src/PackageMetadata";

Expand All @@ -9,6 +10,7 @@ describe("Fetch a list of sfpowerscripts artifacts from an org", () => {

beforeEach(() => {
InstalledAritfactsFetcher.resetFetchedArtifacts();
jest.spyOn(ArtifactMigrator, "exec").mockResolvedValue();
});

it("Return a blank list of sfpowerscripts artifact, if there are no previously installed artifacts ", async () => {
Expand Down Expand Up @@ -213,10 +215,10 @@ describe("Update a sfpowerscripts artifact to an org",()=>{
beforeEach(() => {
jest.restoreAllMocks();
InstalledAritfactsFetcher.resetFetchedArtifacts();
jest.spyOn(ArtifactMigrator, "exec").mockResolvedValue();
});

it("Update a sfpowerscripts artifact, installing it the first time",async ()=>{

const child_processMock = jest.spyOn(child_process, "execSync");
child_processMock.mockImplementationOnce(() => {
return Buffer.from(`{
Expand Down
4 changes: 2 additions & 2 deletions prerequisites/sfpowerscripts-artifact/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
Install the 'sfpowerscripts-artifact' unlocked package to your orgs using the SFDX CLI:

```bash
sfdx package --package 04t1P000000ka0fQAA -u <org> --securitytype=AdminsOnly --wait=120
sfdx package --package 04t1P000000ka9mQAA -u <org> --securitytype=AdminsOnly --wait=120
```

The sfpowerscripts-artifact package is a lightweight unlocked package consisting of an object `SfpowerscriptsArtifact__c` that is used to keep record of the artifacts that have been installed in the org. This enables package installation, using sfpowerscripts, to be skipped if the same artifact version already exists in the org.
The sfpowerscripts-artifact package is a lightweight unlocked package consisting of a custom setting `SfpowerscriptsArtifact2__c` that is used to keep record of the artifacts that have been installed in the org. This enables package installation, using sfpowerscripts, to be skipped if the same artifact version already exists in the org.

The source code for the unlocked package is provided here in the event that you would like to create the package in your own Dev Hub.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<customSettingsType>List</customSettingsType>
<description>Sfpowerscripts artifacts installed in the org</description>
<enableFeeds>false</enableFeeds>
<label>Sfpowerscripts Artifact 2</label>
<visibility>Public</visibility>
</CustomObject>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>CommitId__c</fullName>
<description>Git commit ID from which artifact was created</description>
<externalId>false</externalId>
<label>Commit Id</label>
<length>40</length>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Tag__c</fullName>
<description>Git tag of artifact</description>
<externalId>false</externalId>
<label>Tag</label>
<length>100</length>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Version__c</fullName>
<description>Version number of artifact</description>
<externalId>false</externalId>
<label>Version</label>
<length>15</length>
<length>50</length>
<required>false</required>
<trackTrending>false</trackTrending>
<type>Text</type>
Expand Down
Loading

0 comments on commit 45d7c03

Please sign in to comment.