Salesforce, Python, SQL, & other ways to put your data where you need it

Need event music? 🎸

Live and recorded jazz, pop, and meditative music for your virtual conference / Zoom wedding / yoga class / private party with quality sound and a smooth technical experience

Modular devops with Salesforce

23 Sep 2022 🔖 salesforce
💬 EN

I created 5 folders inside a parent folder and opened up the parent folder in VSCode.

Opened the terminal into folder2.

Ran cci flow run dev_org --org beta in a folder with nothing but a .gitignore. Error was Error: The file cumulusci.yml was not found in the repo root: C:\folder2. Are you in a CumulusCI Project directory?. So I definitely need a cumulusci.yml file. Hmmm – let’s see what cci project init makes.

# Project Info
The following prompts will collect general information about the project

Enter the project name.  The name is usually the same as your repository name.  NOTE: Do not use spaces in the project name!
Project Name [sf-devops-02-org-agnostic-utils]:

CumulusCI uses an unmanaged package as a container for your project's metadata.  Enter the name of the package you want to use.
Package Name [sf-devops-02-org-agnostic-utils]:

Is this a managed package project? [y/N]: N

Salesforce API Version [55.0]:

Salesforce metadata can be stored using Metadata API format or DX source format. Which do you want to use?
Source format (sfdx, mdapi) [sfdx]:
# Extend Project
CumulusCI makes it easy to build extensions of other projects configured for CumulusCI like Salesforce.org's NPSP and EDA.  If you are building an extension of another project using CumulusCI and have 
access to its Github repository, use this section to configure this project as an extension.
Are you extending another CumulusCI project such as NPSP or EDA? [y/N]: y
Please select from the following options:
  1: EDA (https://github.com/SalesforceFoundation/EDA)
  2: NPSP (https://github.com/SalesforceFoundation/NPSP)
  3: Github URL (provide a URL to a Github repository configured for CumulusCI)
Enter your selection: 2

# Git Configuration
CumulusCI assumes the current git branch is your default branch, your feature branches are named feature/*, your beta release tags are named beta/*, and your release tags are release/*.  If you want to use a different branch/tag naming scheme, you can configure the overrides here.  Otherwise, just accept the defaults.
Default Branch [main]:
Feature Branch Prefix [feature/]:
Beta Tag Prefix [beta/]:
Release Tag Prefix [release/]:

# Apex Tests Configuration
The CumulusCI Apex test runner uses a SOQL where clause to select which tests to run.  Enter the SOQL pattern to use to match test class names.
Test Name Match [%_TEST%]:
Do you want to check Apex code coverage when tests are run? [Y/n]: 
Minimum code coverage percentage [75]: 
dot-gitignore already exists. As a reference, here is what would be placed in the file if it didn't already exist:
# CCI
.cci
/src.orig
/src

# Python
*.pyc
__pycache__

# Robot Framework results
robot/sf-devops-02-org-agnostic-utils/results/

# Salesforce cache
.sf/
.sfdx/
.localdevserver/
deploy-options.json

# LWC VSCode autocomplete
**/lwc/jsconfig.json

# LWC Jest coverage reports
coverage/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Dependency directories
node_modules/

# Eslint cache
.eslintcache

# MacOS system files
.DS_Store

# Windows system files
Thumbs.db
ehthumbs.db
[Dd]esktop.ini
$RECYCLE.BIN/

# Editors
*.sublime-project
*.sublime-workspace
.vscode
.idea
.project
.settings

Your project is now initialized for use with CumulusCI

Here’s the sfdx-project.json it made:

{"packageDirectories": [{"path": "force-app", "default": true}], "namespace": null, "sourceApiVersion": "55.0"}

Here’s the cumulusci.yml it made:

minimum_cumulusci_version: '3.65.0'
project:
    name: sf-devops-02-org-agnostic-utils
    package:
        name: sf-devops-02-org-agnostic-utils
        api_version: '55.0'
    dependencies:
        - github: 'https://github.com/SalesforceFoundation/NPSP'
    git:
        default_branch: 'main'
    source_format: sfdx

tasks:
    robot:
        options:
            suites: robot/sf-devops-02-org-agnostic-utils/tests
            options:
                outputdir: robot/sf-devops-02-org-agnostic-utils/results

    robot_testdoc:
        options:
            path: robot/sf-devops-02-org-agnostic-utils/tests
            output: robot/sf-devops-02-org-agnostic-utils/doc/sf-devops-02-org-agnostic-utils_tests.html

    run_tests:
        options:
            required_org_code_coverage_percent: 75

It created an empty force-app folder.

I deleted README.md, robot folder that came w/ a test-contact, I went ahead and left the 4 beta.json, dev.json, feature.json, and release.json configs it gave me in orgs, the datasets/mapping.yml folder it made me with the way one would populate Account and Contact if one had some data handy, and the .github/PULL_REQUEST_TEMPLATE.md file it made me.


Ok, actually, I deleted everything, updated my .gitignore a bit, and added a cumulusci.yml like this:

project:
  name: devops-demo-02-org-agnostic-utils
  package:
    name: DevOps Demo 02 - Org-Agnostic Utils
  git:
    default_branch: 'main'
  source_format: sfdx

Now I get this error from cci flow run dev_org --org beta: Error: [Errno 2] No such file or directory: 'orgs/beta.json'.

Guess I’ll grab those 4 no-dependency org templates out of .ignoreme and throw them in, changing the package name a bit.


OK, now cci flow run dev_org --org beta errors out as follows:

[09/23/22 07:35:38] ============================================================
                    Initializing flow: FlowCoordinator (dev_org)
                    Set up an org as a development environment for unmanaged metadata
                    ============================================================

                    Verifying and refreshing credentials for the specified org: beta.
                    Creating scratch org with command: sfdx force:org:create --json  -f orgs/beta.json -w 120 -n --durationdays 1 -a devops-demo-02-org-agnostic-utils__beta
                    [email protected]

Error: Failed to create scratch org: 
{
  "status": 1,
  "name": "RequiresDevhubUsername",
  "message": "This command requires a dev hub org username set either with a flag or by default in the config.",
  "exitCode": 1,
  "context": "OrgCreateCommand",
  "stack": "RequiresDevhubUsername: This command requires a dev hub org username set either with a flag or by default in the config.\n    at Messages.createError (C:\\Program 
Files\\sfdx\\client\\node_modules\\@salesforce\\core\\lib\\messages.js:446:16)\n    at OrgCreateCommand.run (C:\\Program 
Files\\sfdx\\client\\node_modules\\salesforce-alm\\dist\\commands\\force\\org\\create.js:102:32)\n    at async OrgCreateCommand._run (C:\\Program 
Files\\sfdx\\client\\node_modules\\@salesforce\\command\\lib\\sfdxCommand.js:89:40)\n    at async Config.runCommand (C:\\Program 
Files\\sfdx\\client\\node_modules\\@oclif\\config\\lib\\config.js:173:24)\n    at async SfdxMain.run (C:\\Program Files\\sfdx\\client\\node_modules\\@oclif\\command\\lib\\main.js:27:9)\n    at async   
SfdxMain._run (C:\\Program Files\\sfdx\\client\\node_modules\\@oclif\\command\\lib\\command.js:43:20)\n    at async Object.run (C:\\Program Files\\sfdx\\client\\dist\\cli.js:162:47)",
  "warnings": [],
  "commandName": "OrgCreateCommand"
}

Ok, let’s see what I nicknamed various orgs I’ve logged the sfdx CLI into: sfdx force:auth:list.

=== authenticated orgs

 ALIAS         USERNAME          ORG ID             INSTANCE URL                    AUTH METHOD 
 ───────────── ───────────────── ────────────────── ─────────────────────────────── ─────────── 
 sfdxorgalias1 [email protected] 00D111111111111AAA https://example1.salesforce.com web
 sfdxorgalias2 [email protected] 00D222222222222BBB https://example2.salesforce.com web

Cool, I’ll pick sfdxorgalias1.

I have to do all of the following (inside each repo’s folder – .sfdx above the 5 one level doesn’t work):

  1. .sfdx/sfdx-config.json of {"defaultdevhubusername": "sfdxorgalias1"}.
  2. sfdx-project.json of {"packageDirectories": [{"path": "force-app","default": true}],"namespace": null, "sourceApiVersion": "53.0"}. I hate locking down version, but as of today, we’re in release-season, and CumulusCI is already trying to do version 56.0 when I don’t specify, but scratch orgs with the hub I picked are still on version 55.0.
  3. force-app/.gitkeep (lest I get Error: Command exited with return code 1: ERROR running force:source:tracking:reset: The path "force-app", specified in sfdx-project.json, does not exist. Be sure this directory is included in your project root.).

Ok, now cci flow run dev_org --org beta works, as does cci org browser --org beta.


I added a test class and a unit test class to a project and ran cci task run dx_push --org beta (without having yet committed my changes) and refreshed the Apex Classes page in my browser and sure enough, there they were. Yay, commit and push.


OK, I went over to folder2 and spun it up w/ just a force-app/.gitkeep baseline, broswer’ed just fine.

Then I ran cci task run update_dependencies --org beta after adding - github: https://github.com/kkgthb/sf-devops-02-org-agnostic-utils to project.dependencies in cumulusci.yml an dit said it was pushing out 2 Apex classes.

Refreshing the Apex Classes page in the browser, they show up!

Run All Tests works great (93%). It seems that even though the tests came from #2 and the scratch org was spun up from #4, the resulting org considers them native, not “packaged” in any way.

Let’s throw in a proper package as a second dependency and see what happens.

See comments of my YAML for this. Interestingly, all of the repos that have proper packages – managed or unmanaged – do this – hence version IDs:

                    Resolving dependencies...

Error: 404 Not Found

aoristdual — Today at 1:38 PM

Yeah, I should have been more specific. What I described is required to consume package versions. When you use a repo like that one as a dependency, CumulusCI will see that there are no releases and fall back to deploying the latest commit on the main branch as unmanaged metadata.

project:
  name: devops-demo-04-an-hr-feature
  package:
    name: DevOps Demo 04 - An HR Feature
  dependencies:
    - github: https://github.com/kkgthb/sf-devops-02-org-agnostic-utils
    - version_id: '04t6g000008b0RpAAI' # Apex rollup.  Unmanaged package.  2 minutes to deploy.  Shows up with null "Namespace Prefix" but a value in "Installed Package."  Banner alerts "This Apex Class is part of a package. You'll lose any edits you make directly in the org if you reinstall or upgrade the package. Immediately inform your development team of any changes you make."  Uninstallable from "Installed Packages" settings page.
    #- version_id: '04t5Y0000015lvuQAA' # 04t5Y0000015lvuQAA is Nebula Logger unlocked package; 04t5Y0000015lsgQAA is Nebula Logger managed package
    - version_id: '04t4x000000RqyHAAS' # RecordTypePicker.  Unlocked package.  1-2 minutes to deploy.
    - namespace: et4ae5 # Marketing Cloud.  Managed package.  10 minutes to deploy.  Shows up with "Namespace Prefix" & "Marketing Cloud" both populated.  Banner alerts "This Apex Class is managed, meaning that you may only edit certain attributes."  Uninstallable from "Installed Packages" settings page.
      version: 238.3
  git:
    default_branch: 'main'
  source_format: sfdx
--- ---