Skip to content

Instantly share code, notes, and snippets.

@amphro
Created August 28, 2020 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amphro/1dfff8ebcffaa4a03cb25989cfd31531 to your computer and use it in GitHub Desktop.
Save amphro/1dfff8ebcffaa4a03cb25989cfd31531 to your computer and use it in GitHub Desktop.
Figures out the deploy order given a sfdx-project.json.
import { ensure } from '@salesforce/ts-types';
import { NamedPackageDir, SfdxError, SfdxProject } from '@salesforce/core';
const project = SfdxProject.getInstance();
const pkgDirs = project.getUniquePackageDirectories();
const pkgAliases = Object.keys(project.getSfdxProjectJson().getContents().packageAliases || {});
class PkgDirNode {
public pkgDir: NamedPackageDir;
// Edges
public dependencies: PkgDirNode[] = [];
constructor(pkgDir: NamedPackageDir) {
this.pkgDir = pkgDir;
}
}
// This is the goal. What order should we deploy the packages in?
// Example at the end.
const deployOrder: string[] = [];
const nodes: { [k: string]: PkgDirNode } = {};
pkgDirs.forEach(pkgDir => {
const pkgName = pkgDir.package;
// Only package dirs with package names can be specified as dependencies.
if (!pkgName) {
// No package name so it CAN NOT have any dependencies. So just deploy it first.
deployOrder.push(pkgDir.name);
return;
}
// Add the node if it doesn't exist.
if (!nodes[pkgName]) {
nodes[pkgName] = new PkgDirNode(pkgDir);
}
if (pkgDir.dependencies) {
// Add edges in for the dependencies.
pkgDir.dependencies.forEach(dep => {
// We only want to add them if they reference a package in the list, not an aliased ID.
// This is for package deploy (source locally) order, not package install order.
if (!pkgAliases.includes(dep.package)) {
const depPkg = pkgDirs.find(pkg => pkg.package === dep.package);
// If we didn't find it, then it is a dependency that points to no where...
if (!depPkg) throw new SfdxError(`Missing dependency ${dep.package}`);
// We know this is populated because of the find above.
const depPkgName = ensure(depPkg.package);
// We might not have ran into the dependency yet, so add the node.
// We won't add it again but will look at it's dependencies when we reach it in the loop.
if (!nodes[depPkgName]) {
nodes[depPkgName] = new PkgDirNode(depPkg);
}
nodes[pkgName].dependencies.push(nodes[depPkgName]);
}
});
}
});
// Only deploy them once. We could also look at deployOrder.includes but this is a little faster.
const deployedPackages: { [k: string]: PkgDirNode } = {};
const nodeSet = new Set(Object.values(nodes));
const processNode = (node: PkgDirNode) => {
// All the nodes here WILL have the package name defined.
// Ones without package name defined have already been added to the deploy list.
const packageName = ensure(node.pkgDir.package);
// If it has already been "deployed", we have already processed it and it's dependencies.
if (deployedPackages[packageName]) return;
// Has dependencies, so go down and deploy those first.
node.dependencies.forEach(dep => processNode(dep));
deployedPackages[packageName] = node;
deployOrder.push(node.pkgDir.name);
// It has been deployed
nodeSet.delete(node);
};
// Deploy all package dirs.
while (nodeSet.size !== 0) {
// We can always grab the zeroth because it will be removed when processed.
processNode(nodeSet.values().next().value);
}
/*
{
"packageDirectories": [
{
"path": "force-app-pkg4",
"package": "pkg4",
"versionNumber": "1.0.0"
},
{
"path": "force-app-pkg3",
"package": "pkg3",
"versionNumber": "1.0.0",
"dependencies": [
{
"package": "pkg2"
},
{
"package": "pkg4"
}
]
},
{
"path": "force-app-test"
},
{
"path": "force-app-pkg1",
"package": "pkg1",
"versionNumber": "1.0.0",
"default": true
},
{
"path": "force-app-pkg1",
"package": "pkg1-test",
"versionNumber": "1.0.0-023"
},
{
"path": "force-app-pkg2",
"package": "pkg2",
"versionNumber": "1.0.0",
"dependencies": [
{
"package": "pkg1"
}
]
}
]
}
*/
console.log(deployOrder);
// Output: [ 'force-app-test', 'pkg4', 'pkg1', 'pkg2', 'pkg3' ]
// pkg1-test is filtered out because it is the same deploy path as pkg1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment