A successful open source project needs many things to thrive. Providing something that has strong appeal is important, as well as lots of love from its users and contributors. Also key is how people get their hands on it! This falls into two camps, consumers and contributors. Either way, you want to make it super easy! Since I started playing with DX packages I have been wondering what they could bring to the open source party? Let’s find out…
Let’s review some important aspects of an open source experience:
- Packaging
Inevitably an open source library or tool exists side by side with others in a larger program or system (an org). The ability to collect up all the required artifacts of the solution in a way that makes it easy to distribute is key. As is the ability to clearly version and define the contract (the API) of the library. - Discoverability
Places like GitHub, Blogs etc are a good place to promote open source but depending on what you are sharing not the most frequently visited places, especially if your consumer is not a developer. A key part of the success of Node.js has been its packaging manager NPM and related site. - Accessibility
Once someone has discovered an open source solution like most of us, they want it now and with as little fuss as possible. The Heroku and Salesforce deploy buttons are very popular choices in this regard, no downloads just click and go! For those who need a CLI, a CLI means of installation is also highly desirable. - Contributions
Of course, once things start to kick into life folks will want to get involved and help out with improvements and new features. Developers want to download and start coding asap. Solutions that require numerous setup steps and environment configurations are a detraction.
Getting Started
I decided to take one of my most recent open source projects the Custom Metadata Services library (repo) through a test drive with DX packages. The repo for ease at the time was a mix of library code and some demo code, so I started by splitting that out and converting both to Salesforce DX format (great blog from Nathen Totten here).
Packaging, Namespaces, and Visibility
Some of my open source projects use managed packages, historically designed for ISV (Independent Software Vendor) scenarios. As such intentionally enforces some rigidity needed for that distribution model. Salesforce DX driven packages now provide more choice with respect to packaging features.
- Unlocked packages provide more freedom to make changes and allow for downgrading.
- Managed still provides the safety net of restricting changes that could break customers.
Of special interest here, is that unlocked packages have the optional ability to leverage namespaces and access modifiers to control visibility. A namespace will ensure the components in the library do not conflict with others in the org. So I have registered a globally unique namespace of cmds as described here. The following code is from the demo repo I created, no more prefixing class names!
DeployId = cmds.CustomMetadata.Operations .callback( cmds__MetadataDeployment__e.getSObjectType(), cmds__MetadataDeployment__e.cmds__DeploymentId__c, cmds__MetadataDeployment__e.cmds__Result__c) .enqueueUpsertRecords(new List<SObject> { Record }) .deployId;
<cmds:metadataRecordData aura:id="recordData" recordFullName="WidgetPreset.BluetoohToothbrush" targetRecord="{!v.record}" targetFields="DeveloperName, Label, DefaultNotification__c, Alias__c" recordUpdated="{!c.handleSaveResult}"/>
In order to expose the above classes and components, I had to use the global access modifier in Apex and Lightning. This was a bit worrying at first because I feared the same kind of lockin I got with traditionally managed packages. Not so! Since this is an unlocked package, you can modify and delete things as much as you want between releases. The platform leaves managing breaking changes up to you. So what we have here is a great way to clarify what the intended API is of your library and thus hiding its internals.
global with sharing class CustomMetadata { global static final Operations Operations = new Operations(); global class Operations { global String deployId {get;private set;} global Operations enqueueUpsertRecords(List<Metadata.CustomMetadata> records) { ... } global Operations enqueueUpsertRecords(List<SObject> records) { ... } global Operations enqueueUpsertRecords(SObjectType sobjectType, List<Map<SObjectField, Object>> sObjectRecords) { ... } global Operations callback(SaveResultCallback saveResultCallback) { ... } global Operations callback(SObjectType eventType, SObjectField deploymentIdField, SObjectField messageField) { ... } } }
<aura:component controller="MetadataRecordDataController" access="global"> <aura:attribute name="recordFullName" type="string" access="global"/> <aura:attribute name="targetRecord" type="object" access="global"/> <aura:attribute name="targetFields" type="string" access="global"/> <aura:attribute name="deploymentId" type="string" access="private"/> </aura:component>
Once I put the namespace in my DX project configuration file. I then ran a couple of commands to create my first package version. Further details on packaging can be found in the documentation here. Note the latest version of the DX CLI now updates the project configuration file for you if you use the wait parameter! Neat!
sfdx force:package:create --name "Custom Metadata Services" --description "Custom Metadata Services for all your Apex, Lightning and Visualforce needs" --packagetype Unlocked --path .
sfdx force:package:version:create --path force-app --installationkeybypass --wait 10
NOTE: Using a namespace requires the use of the access modifier global. Keep in mind namespace usage is optional for unlocked packages. Without it, you still get all the other benefits described in the blog.
Accessibility
Packages can be installed in an org via a number of different routes, ranging from clicking on a link, API calls or via the Salesforce DX CLI. The simplest approach is to provide a web link. Depending on the type of project (admin tool?) this is also the quickest way of getting things into a sandbox or production org for such a user.
If your open source project is more likely to be installed by developers, a CLI is a more preferable path. For this, you will need to share the ID of the desired package version.
sfdx force:package:install --package 04t6A000003KeLIQA0 --wait 10
Let’s face it an ID is not the most memorable of things hence the following command.
sfdx shane:github:package:install -g afawcett -r custommetadataapi
As you will have noticed this is not a standard DX CLI command. It has been developed by Shane McLaughlin using the Heroku Oclif and DX Core. Its worth noting that a recent enhancement to the DX CLI now results in the package versions and a more human readable alias being stored in the DX project file. Shane updated his command to look for this information and install the latest.
Contributions
Salesforce DX scratch orgs have made a big step forward in reducing the friction in setting up an org tailored to develop a fix or new feature. Developers fork and sync the repo locally, create a scratch org and push the code to get started. The scratch org configuration file concept allows you to define different org “types” to help contributors setup orgs easily. For example, it’s now much easier to work on a multi-currency feature in my roll-up tool by simply defining this requirement in a configuration file.
NOTE: Use of the namespace in a scratch org is limited to the DevHub that created it. Thus contributors must use the –nonamespace parameter when creating scratch orgs.
Conclusion
In respect to packaging, accessibility, and contribution I feel Salesforce DX has added some much-needed advancements to the world of open source and Salesforce. In order to make our projects more discoverable, we now need to consider new ways to promote our open source packages. Perhaps something Salesforce might expose as a category on AppExchange or the community builds some new “NPM-like” place…or perhaps a way to integrate with an existing service can be found? What I know for sure is there are exciting times ahead for Salesforce open source!
June 17, 2018 at 1:27 am
Happy to build SFPR with you – Salesforce Package Registry?! 🙂
June 17, 2018 at 1:30 am
SFPM seems a bit too cheeky 😉
Pingback: Around the Web – 20180608 - WIPDeveloper.com
Pingback: Salesforce DX, Packages and Open Source — Andy in the Cloud – SutoCom Solutions
February 25, 2019 at 12:58 pm
Not sure if this is the correct location, but we have your package Declarative Lookup Rollup Summaries Tool installed, and as it is version 2.8 and needs to be updated, how would you recommend doing so? Especially as we got a red flag warning about it when assessing preparedness for switching to Lightning.
Thank you!
April 4, 2019 at 6:07 pm
I am not sure about the warning, not seen that. I know that folks are running it in lightning, typically it has no ui really it works in the background. It’s only the config side that has a ui. You can jump to the latest one on the repo readme. But read through the install notes from 2.8 onwards.
Pingback: Managing Multiple Packages with Grunt and the Salesforce CLI – Johannes Fischer