12

In the first generation of Managed Packages (1GP), you are able to create Managed Extension packages that technically required a base package.

Both could be distributed and licensed using the same ISV process (Become AppExchange Partner, Pass Security Review, use LMA for licensing).

How does this work in 2GP?

  1. Can we create 2GP Managed Extension Packages? How?
  2. Is the Process the same? Will the LMA app see them?
  3. Is all of that documented in detail?
25
+100

Thanks for the question! I've created an answer in three parts, each one meant to address a specific question you've asked.

  • PART ONE: How to Create Second-Generation Extension Packages
  • PART TWO: How to Get the LMA to "see" a Second-Generation Managed Package
  • PART THREE: Technical Enablement for Second-Generation Extension Packages

Not gonna lie, this is a long read. For those who want something shorter, here's the TL;DR.

Second-generation extension packages are created by adding dependencies on metadata found in other packages to a second-generation package, then creating a new package version using force:package:version:create.

The LMA has no problem seeing 2GP extension packages so long as your Dev Hub is linked to your publishing console and you've created and promoted at least one second-generation package version from that Dev Hub.

There are several enablement materials that are relevant to this topic, but they're spread out over a large number of sites and locations. If you have just 17 minutes to learn more, the one resource to check out is this video from DreamTX '20: Build Apps Using 2nd-Generation Managed Packages.

For those with time to kill (and an itch to learn!) let's dive in!


PART ONE: How to Create Second-Generation Extension Packages

Fundamentally, an "extension package" is any package that has one or more hard dependencies on another package. For example, let's say that Package A and Package B start life out as unrelated entities. The moment that Package B adds a custom field, Apex reference, or other hard dependencies to metadata components that are defined in Package A...that moment is when Package B becomes an "extension" of Package A.

How Package Dependency Works During Development

Package dependency is at the heart of what makes one package an "extension" of another, so it's important to understand the rules that govern this interaction, especially those that impact you during development.

Package Dependency Rule #1: Package dependencies (i.e. "base packages") must be present in the org where development of the dependent (i.e. "extension") package takes place in order to satisfy design-time metadata dependencies.

  • For 1GP, this means that the package dependency must be installed in the packaging org
  • For 2GP, this means that one of two things must happen
    • If the package dependency has a different namespace, it must be installed in any namespaced scratch orgs where development takes place
    • If the package dependency has the same namespace, it could either...
      • Be installed in any namespaced scratch orgs where development takes place
      • Be present in its own package directory within the same SFDX Project as the extension package source is, allowing the source of both packages to be deployed to the scratch org using force:source:push

Package Dependency Rule #2: 2GPs that depend on other packages must explicitly define such dependencies inside of the sfdx-project.json file at the root of the SFDX project directory where the 2GP is being developed.

Package Dependency Rule #3: Second-Generation Packages (2GPs) can depend on both First-Generation Packages (1GPs) and other 2GPs. On the other hand, 1GPs can only depend on other 1GPs because extending a 2GP with a 1GP is not supported by Salesforce. In other words...

  • 1GP extending 1GP >>> OK
  • 2GP extending 1GP >>> OK
  • 1GP extending 2GP >>> NOT OK
  • 2GP extending 2GP >>> OK

Package Dependency Rule #4: Circular package dependencies are prohibited. This means that if Package B depends on Package A then it's not OK for Package A to also depend on Package B. Packages built in this way would never be installable because no matter which package you install first the subscriber org would always be missing a package dependency.

Package Dependency in Action

With all the rules of package dependency in mind, let's look at an example of 2GP extension package development in action. Consider this excerpt of a Salesforce DX project definition file, sfdx-project.json, used during the development of an expense calculator app.

// Note: This is an excerpt of an sfdx-project.json file, used for demonstration
// purposes only. A complete file would have additional keys defined.

{
  "namespace": "exp_calc_demo",
  "packageDirectories": [
    {
      "path": "accounting-logic",
      "default": false,
      "package": "Accounting Logic",
      "versionName": "Summer ‘21",
      "versionNumber": "4.5.0.NEXT",
      "dependencies": [
        {
          "package": "Apex Library@1.3.0.1"
        }
      ]
    },
    {
      "path": "expense-calculator",
      "default": true,
      "package": "Expense Calculator",
      "versionName": "Summer ‘21",
      "versionNumber": "1.2.0.NEXT",
      "dependencies": [
        {
          "package": "Apex Library@1.3.0.1"
        },
        {
          "package": "Accounting Logic",
          "versionNumber": "4.5.0.LATEST"
        }
      ]
    }
  ],
  "packageAliases": {
    "Expense Calculator": "0HoB00000002JAiWAP",
    "Accounting Logic": "0HoB00000004CFpKAM",
    "Apex Library@1.3.0.1": "04tB0000000IB1EIYR"
  }
}

Observation #1: The "Expense Calculator" app is actually an MPSN (multi-package, same-namespace) solution because both the Expense Calculator and Accounting Logic packages are 2GPs that share the same namespace. We know this because each package is defined inside of the packageDirectories object array and each one has a package alias that points to a 0Ho Package2 ID.

Observation #2: The definition of the Accounting Logic package directory appears before the definition of the Expense Calculator package directory. When running the force:source:push command, this causes the Salesforce CLI to push the project source one package directory at a time in the same order as those package directories are defined inside sfdx-project.json

This is very important because the Expense Calculator package has metadata components that depend on the metadata found in the Accounting Logic package. When getting an empty scratch org ready for development, if the source from the Accounting Logic package was not pushed to the scratch org first the force:source:push command would end up failing due to invalid metadata references.

Observation #3: The dependency between Expense Calculator and Accounting Logic is an internal dependency because both packages share the same namespace and were created against the same Dev Hub. This allows the Expense Calculator package directory definition to use the specialized syntax of "versionNumber": "4.5.0.LATEST" to specify which version of the Accounting Logic package it depends on.

Observation #4: The dependency on Apex Library@1.3.0.1 is an external dependency because that package is neither part of the same namespace nor owned by the same Dev Hub as the Expense Calculator and Accounting Logic packages.

The fact that it's an external dependency is also why Apex Library@1.3.0.1 is aliased to a 04t package version ID. Note that both Expense Calculator and Accounting Logic are aliased to 0Ho Package2 IDs, something that's only possible when the packages referenced are owned by the same Dev Hub that's being used to run any force:package commands in this project.

Observation #5: It's not clear from looking at sfdx-project.json so I'll have to state it here: The Expense Calculator package in our example doesn't actually have any direct dependencies on the Apex Library@1.3.0.1 package.

If that's the case, why do we need to explicitly list Apex Library@1.3.0.1 as a dependency of Expense Calculator?

The reason is that Second-generation packages must declare all package dependencies, even indirect ones. When you run force:package:version:create the 2GP Build System needs to know the complete list of packages to install in the behind-the-scenes Build Org where your package is validated.

When the build org is created, each package dependency defined by the package being built will be installed in the same order in which they are defined. That's why Apex Library@1.3.0.1 is the first dependency listed for the Expense Calculator package. It must be installed first because the package that Expense Calculator actually depends on, Accounting Library, would fail on installation in the Build Org if Apex Library@1.3.0.1 wasn't already there.

Final Observations: There is a lot for a developer to keep track of when it comes to package dependencies in 2GP. Knowing the responsibilities upfront can help avoid problems.

  • Developers must identify the specific package versions that each 2GP depends on, whether those dependencies are direct or indirect
  • External package dependencies must be installed in scratch orgs before the source of 2GPs that depend on those packages can be pushed/deployed to the scratch org
  • Very little is automated (yet), so developers must be ready to manually determine indirect dependencies and install package dependencies into scratch orgs during development

PART TWO: How to Get the LMA to "see" a Second-Generation Managed Package

Connecting a second-generation managed package to an LMA is a relatively straightforward, two-step process...once you know how to do it. To make the first time easier, I'll walk through the process visually.

STEP ONE: Connect the Publishing Console to Your Dev Hub

This step assumes that your company has already joined the partner community and the Salesforce AppExchange Partner Program. If you haven't done this yet, please see the Connect with the Salesforce Partner Community unit on Trailhead for details on how to proceed.

  1. Log in to the Partner Community with a user who's part of your partner organization and has been granted the Listings permission
  2. Click on the Publishing tab to open the Publishing Console
  3. Click on the Organizations sub-tab
  4. Click the Connect Org button to start the Connection Wizard

Start the process of connecting your publishing console to your Dev Hub

  1. Enter the username of an admin user from the org where your Developer Hub lives
  2. Enter this user's password with the security token appended
    • If you don't have or don't know the security token for this user, you'll have to reset it
  3. Click the Connect button to complete the process

Provide authentication credentials to your Dev Hub

STEP TWO: Register Your Second-Generation Package with Your LMA

Please note that it may take several minutes for the packages owned by your linked Dev Hub to show up in the Publishing Console. Also note that ONLY released (i.e., non-beta) packages will show up here and that it may take several minutes for newly promoted 2GPs to appear.

  1. Log in to the Partner Community with a user who's part of your partner organization and has been granted the Listings permission
  2. Click on the Publishing tab to open the Publishing Console
  3. Click on the Packages sub-tab. This will show you...
    • All released (i.e. non-beta) first-generation managed packages owned by the 1GP packaging orgs that you've linked to your Publishing Console
    • All first-generation unmanaged packages owned by orgs (packaging or otherwise) that you've linked to your Publishing Console
    • All released (i.e. non-beta) second-generation managed packages owned by each Dev Hub that you've linked to your Publishing Console

Open the Packages Sub-Tab

  1. Observe how different packages, VMC 2GP Lab (core-ux) and VMC 2GP Lab (SR-Test), each with the same namespace, vmc2gplab, are both listed here.
    • This is what happens when you link a Dev Hub to the Publishing Console
    • All second-generation packages owned by the linked Dev Hub which have at least one released (i.e. non-beta) package version will show up here.
  2. Connecting a managed package to your LMA requires you to click the Register Package link next to the package version that you want to wire up to your LMA. Clicking this link will kick off the package registration wizard.

Click the Register Package link

  1. Click the Log In button to go to the next stage of the package registration wizard.

Click the Log In Button

  1. Enter the username of an admin user from your License Management Org (LMO)
    • "License Management Org" or "LMO" is a term used to describe an org where the License Management App (LMA) is currently installed.
  2. Enter this user's password with the security token appended
    • If you don't have or don't know the security token for this user, you'll have to reset it.
  3. Click the Log In button to move to the next step in the package registration wizard.

Enter Your Login Information

  1. Choose the default license behavior.
    • Trial licenses give subscribers access to your solution for up to 90 days.
    • Active licenses provide subscribers with continuous access.
  2. Set the length of trial licenses to some number of days.
    • The maximum value that can be set is 90 days.
    • When the default license is *active instead of trial, the option to specify the length of trial disappears.
  3. Choose whether the default license should be site-wide (available for all users) or per-seat (must be granted to users individually).
    • If selecting per-seat as your default license, you must also specify the number of seats that will be granted to the subscriber by default.
  4. Click the Save button when done to complete the package registration wizard.

Set the behavior of the default license

Key Considerations When Using the LMA with Second-Generation Packages

Please keep these things in mind when using the LMA with first and second-generation packages

  • Packages will only appear in your publishing console once you've...
    • Connected a packaging org (1GP) or Dev Hub (2GP) to your Publishing Console
    • Uploaded a managed/released package version (1GP) or promoted a beta package version to the released state (2GP)
  • Packages do not have to pass security review before being registered with your LMA
    • Partners are welcome to register packages with their LMA early
    • This allows partners to get comfortable with being "hands-on" with the LMA
    • Registering your package with the LMA also makes it possible to see how the Feature Management App (FMA) works end-to-end
    • IMPORTANT! Being able to register a package with your LMA before passing Security Review is not an invitation to skip Security Review or the AppExchange Listing/Contracting process and distribute your package directly to customers. Doing so is a violation of the Salesforce Developer Services MSA and the Salesforce Partner Program Agreement (SPAA).
      • Installing in sandbox and trial orgs is fine.
      • Installing in production orgs is not. Please don't do it!
  • To go through Security Review, packages must be associated with a Listing.
    • This is a core part of how the AppExchange publishing system works and can not be bypassed.
    • Having a listing does not mean your app has to be publicly listed (i.e. visible) on the AppExchange.
    • Creating a listing, going through Security Review, and keeping your listing private so customers must install via URL or 04t package version ID is perfectly acceptable.

PART THREE: Technical Enablement for Second-Generation Extension Packages

Documentation and enablement material for packaging, in general, can be found in many places. The Salesforce ISV Platform Expert Team aggregates 2GP specific enablement content in this Trailmix:

Tech Enablement: Second-Generation Packaging (2GP)

As for enablement resources that are relevant to the topic of 2GP extension packages, Phil W shared some great links in his answer. I'll share them again here along with some other ones I know of. (Thanks, Phil!)

Finally, there's this super long article-disguised-as-StackExchange-answer which is (hopefully) a nice enablement resource all by itself. :-)

1
  • 4
    you rocks!!! Thank you very much in name of Copado Architecture Team for this complete and thoughtful answer.. – Martin Borthiry May 1 at 9:03
5

2GP managed packages only appear in the LMA after security review has been passed (as you have pondered). (Note that, as an aside, without the security review being passed it is actually impossible to create patch releases for a 2GP managed package.)

Publishing 2GPs on the AppExchange is explicitly covered in the documentation, as is dealing with 2GP security reviews. That first document includes the following interesting point:

Packages that share a namespace can be associated the same License Management Org (LMO), or you can associate the packages with different LMOs.

It doesn't matter if such packages have dependencies or not, the package creation process is the same. What varies is the content of your sfdx-project.json - you use this to enumerate the dependency packages (so your extension package depends on your core package, for example). Unfortunately the sfdx CLI doesn't yet automate creation of scratch orgs with dependencies installed, so you'll have to write your own scripts around it to do this. There's a sample in the documentation - we opted for a completely different approach, but with the same end result.

2GP is heading towards the idea of lots of small "modular" packages that sit in the same namespace and use NamespaceAccessible APIs to interact. Eventually Salesforce will provide Package Bundles so you can install a tree of these "module" packages in one action, but that's a while off still.

I'm not sure whether the security review fee is per namespace or per listing (our extension packages are not publicly listed) though you submit all the 2GPs for review separately.

I suspect you asked this question on the Partner Forums, and I expect you'll get a better answer there :D

3
  • Great answer. Thanks Phil. But: Your first link is broken. – Robert Sösemann Apr 27 at 22:28
  • 1
    @RobertSösemann try the URL again. I find that Salesforce intermittently has 404s on their documentation site these days... I just managed to successfully access the page via the link above. – Phil W Apr 28 at 8:42
  • 2
    I will leave it open as someone from Salesforce also will add an answer. – Robert Sösemann Apr 28 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.