The time has come for you to create an add-on package, and you are determined to use Second Generation Packaging. We’ll show you how we did it here.
Purpose
One of the biggest bonuses when using Second Generation Packaging (2GP) is the ability to develop two packages with dependencies using the same namespace and ‘packaging org’ (actually a Dev Hub here). You can even use the same IDE, and not ever have to worry about namespace prefixing in any of the source code.
Methodology
We’re going to go through the process while using only the SFDX CLI. Everything set up here works fine in the common IDEs, but I won’t be diving into that here. Let me know if this would be helpful someday.
We’re also going to use the same application that we were developing and upgrading in the previous blog post here. So if you’ve been through that post this should be easy to follow along with. If you’re following from a theoretical standpoint I think you’ll be fine, there’s not a ton going on here.
I’ll be focusing on a scenario with one parent/main package and one child/add on package.
How is 2GP different from 1GP?
Similar to our previous post regarding ancestry, there are many differences. But with regards to multiple packages the following are the ones to focus on:
- 1GP: For each package we want to create, we have to create a separate packaging org. In the ‘child’ orgs we must first install our ‘parent’ package, and then when we write code that references components from the ‘parent’ package we include the namespace prefix of the parent. Each package usually requires its own VCS repos and project directory. Dependencies are determined solely by which packages refer to components in other packages.
- 2GP: Here multiple packages can happily exist in the same project directory, and the same version control repository. Dependencies between packages are defined in the
sfdx-project.json
file. Each segment below will dive deeper into the differences.
The Main Package
Let’s set up the main package in the force-app
directory, just like before. The sfdx-project.json file from the last article looks like this:
The Add-on Package
Now we’re going to add the child package. Let’s start by creating the directory in your project root called child-app
.
Next, create the package usign sfdx CLI, this will update your sfdx-package.json
file with a new packageDirectory
element.
This creates another package in the same namespace (and a new alias), but it doesn’t tie the two packages together just yet. Let’s add the dependencies collection. Here’s what it looks like:
What we’ve done here is told the system that when we package up the contents of the child-app
folder, it will register a dependency on version 0.5.0 of the parent package. It will not be able to install unless parent package is installed first.
Packaging the Child App
In the child-app
directory, add a directory called main
, and under main, create default
.
- At this time, if you’re using Illuminated Cloud 2, right click and mark
default
as a sources root. This will allow you to save metadata you place in here to the Scratch Org you’re working in.
Now let’s add the following two classes:
As you can see here we’ve referenced code in the parent package, but we’re in the child package directory. Let’s package it now.
I did something wrong on purpose to illustrate something important…. In version 0.5 of the parent app we made the class public.
ERROR running force:package:version:create: ChildDemo: Type is not visible: curiousancestry.CuriousIndeed,ChildDemoTest: Type is not visible: curiousancestry.CuriousIndeed
This means that even though we’re in the same namespace we forgot something in the base/parent package. That’s what @NamespaceAccessible
is for. We could also use global instead of public, but this would expose things to customers. To make everything work, I’m going to add @NamespaceAccessible
and release version 0.6 of the parent package while keeping everything public. This is what the CuriousIndeed
class looks like now:
Here I updated the sfdx-package.json
so that it would build version 0.6, pushed the code, created a new package version, and released the parent package again. When done your sfdx-project.json
should look like this (note the update to dependencies):
Let’s try creating a new version of the child app now…
Result:
Successfully created the package version [removed]. Subscriber Package Version Id: childVersion1
Now grab the package version id (starts with 04t
) returned from the create command, and promote it.
At this point you can play with installing version 0.6 of the parent and version 0.1 of the child.
Weird Stuff
Custom fields on the same object in two packages
Most things in this setup work great when you run force:source:push
. One thing that doesn’t at the time of this writing is deploying fields to the same Object from multiple packages. Let’s use Account
for our example.
If your directory structure looks like this:
Then expect CustomField1__c
to deploy, but CustomField2__c
will not. It looks like they’ll still package up just fine, but they will require manual creation in the SO in order to use them. (there are other options, but manual creation for me is the simplest)
Unit tests in second package must cover code in first
In all of our examples we were calling and executing 100% of the code from the parent package in the unit tests from the child package. So everything played nicely…
Let’s pretend the child package did not call the code in the first package. Or maybe it didn’t fire a trigger - and triggers need at least 1% coverage. This rule still holds… Well, when packaging the child you’ll code coverage issues. I’ll include some skeletonized versions of the child package code as an example.
Also, increment the child version in your sfdx-project.json
:
Crete a new package version, and try to promote it:
You will see this:
ERROR running force:package:version:promote: The code coverage required to promote this version has not been met. Please add additional test coverage and ensure the code coverage check passes during version creation.
So… for now be prepared to have a lot of unit tests in your child packages, even if there’s not much to them.
Conclusion
I hope this has been helpful. I think 2GP is really cool. Setting up a directory structure and project definition seems pretty obvious now, but it wasn’t so when I first went through it. If you find more weirdities, or anything that has changed since this was published please reach out.
Thanks for reading!