Skip to content

Commit

Permalink
Merge pull request #60 from Szandor72/staging
Browse files Browse the repository at this point in the history
Staging DevOps Center Changes
  • Loading branch information
Szandor72 authored Jan 14, 2024
2 parents e553494 + 78bf6dd commit 2a9919b
Show file tree
Hide file tree
Showing 55 changed files with 18,248 additions and 18,313 deletions.
2 changes: 2 additions & 0 deletions .ci/legacy-metadata-files.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
force-app/main/default/classes/AccountService.cls
force-app/main/default/classes/AccountService.cls-meta.xml
11 changes: 0 additions & 11 deletions .github/workflows/quality-checks.yml

This file was deleted.

15 changes: 15 additions & 0 deletions .github/workflows/validate-code.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Code Scans with SFDX Scanner

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- integration
paths:
- 'force-app/**'

jobs:
sfdx-scanner:
name: 'Validate Code'
uses: 'Szandor72/devops-center-actions/.github/workflows/validate-code.yml@main'
secrets: inherit
15 changes: 15 additions & 0 deletions .github/workflows/validate-deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Validate Deployment

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- integration
paths:
- 'force-app/**'

jobs:
validate-deployment:
name: 'SFDX Scanner'
uses: 'Szandor72/devops-center-actions/.github/workflows/validate-deployment.yml@main'
secrets: inherit
14 changes: 14 additions & 0 deletions .github/workflows/validate-metadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Custom Metadata Checks

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- integration
paths:
- 'force-app/**'

jobs:
validate-metadata:
name: 'Validate Metadata'
uses: 'Szandor72/devops-center-actions/.github/workflows/validate-metadata.yml@main'
118 changes: 103 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,110 @@
# Salesforce DX Project: Next Steps
# Salesforce DevOps Center Demo Project

Now that you’ve created a Salesforce DX project, what’s next? Here are some documentation resources
to get you started.
## What is this?

## How Do You Plan to Deploy Your Changes?
This Demo Project has 3 dimensions:

Do you want to deploy a set of changes, or create a self-contained application? Choose a
[development model](https://developer.salesforce.com/tools/vscode/en/user-guide/development-models).
1. It is a sample project to demonstrate how Salesforce DevOps Center can be used together with
re-usable GitHub Actions and additional Salesforce tools to build a **loosely coupled **CI**
pipeline** with automated Quality Gates.
2. It acknowledges projects contain **legacy** (read: older, bad quality) **metadata** which should be scanned and reported but not block or confuse new development
3. It shows that **App Builders don't need to use VS Code**, or any IDE, or the Command Line to get the same benefits as Developers do.

## Configure Your Salesforce DX Project
## Loosely coupled how?

The `sfdx-project.json` file contains useful configuration information for your project. See
[Salesforce DX Project Configuration](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm)
in the _Salesforce DX Developer Guide_ for details about this file.
Imagine for a moment, you have created - over time - a dozen Salesforce projects, each with their
own CI yml files, ignore files, and so on. Now imagine you want to make a change to all of them. You
could go through each repository and make the change, but that's a lot of work. Instead, you could
make a change in one place, and have all of your projects automatically use the updated version.

## Read All About It
## What are common moving parts that need loose coupling?

- [Salesforce Extensions Documentation](https://developer.salesforce.com/tools/vscode/)
- [Salesforce CLI Setup Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm)
- [Salesforce DX Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_intro.htm)
- [Salesforce CLI Command Reference](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference.htm)
A Salesforce Project is tooling-wise a web development/JavaScript project with very few extra bells
and whistles. The default DX Project setup relies on a node `package.json` for tooling like ESLint,
Jest, or Prettier. Each tools comes with its own config and rule files as well as ignore file
options.

Then you have your actual CI worfklow configuration file(s) which have dependencies to the SF CLI,
Orgs and Authentication, Caching, and additional tools like PMD or the SFDX Scanner plugin.

These tools come with config and rulesets of their own.

## What will it do?

All [checks](https://github.com/Szandor72/devops-center-demo/actions) run on **Pull Request**
against `integration` branch for changes in the `force-app` folder.

An integration sandbox is supposed to be the target org.

Three workflows are configured:

- **validate-metadata**

- SFDX Scanner with PMD engine and custom rules for
- sObjects
- Flows
- Misc Metadata
- only new/modified files will be prettified (3rd Party Action)

- **validate-code**

- Jest Tests
- SFDX Scanner
- all modified files will be checked whether they have a match in
`.ci/legacy-metadata-files.txt`
- for modified legacy files
- only code will be scanned
- the build will never fail if issues are found
- Issues are reported in markdown table as step summary as well as
- CSV upload to a target org (get a notification in the org optionally)
- CSV upload is only triggered if new issues are detected upon re-rerun of a scan per
PR
- for all other files (new and modified)
- strict code scans are performed

- **validate-deployment**

- runs a test deployment against integration sandbox and runs all local tests

## How does it work?

There are several repositories involved:

- https://github.com/Szandor72/devops-center-template
- an empty DX project with no config files. Use this as starting point in DevOps Center or
create your own fork
- https://github.com/Szandor72/devops-center-demo
- a copy of the template with a bit of Salesforce Metadata and all
[Actions configured](https://github.com/Szandor72/devops-center-demo/actions) and minimal
[workflow files](https://github.com/Szandor72/devops-center-demo/tree/main/.github/workflows).
All secrets for authentication are stored here and passed on to:
- https://github.com/Szandor72/devops-center-actions
- this repository stores all the GitHub Actions that are used in the demo project. It can
"inherit" secrets from the Demo Repo because GitHub Actions can access secrets from the same
GitHub organization.
- https://github.com/Szandor72/devops-center-local-config-files
- is [a npm package ](https://www.npmjs.com/package/devops-center-local-config-files) which
contains all the config files as well as (exemplary, strict, custom) PMD
[rulesets](https://github.com/Szandor72/devops-center-local-config-files/tree/main/pmd-rulesets)
for various metadata types and code.
- The config files and rulesets are intended to be copied and used only during build time by
the GitHub Actions Workflows.
- If you run **`npm install` locally**, the files will be copied as well. If you commit your
copies, these files will be used instead of the ones from the npm package.
- The npm package also provides a few
[JavaScript files](https://github.com/Szandor72/devops-center-local-config-files/tree/main/ci-scripts)
that help preparing scans and parsing scan results by generating GitHub Annotations and
summary tables.

## How can I use it?

- Create a fork of the template repository and use it as starting point in DevOps Center
- Make sure to activate Actions, those aren't enabled by default when forking
- Configure the Actions to use your own Orgs and Secrets
- `${{ secrets.INTEGRATION_SANDBOX_SFDX_URL}}` will be used to do test deployments and CSV Scan Report File Upload
- GitHub Action workflows are configured to run when a PR against `integration` branch is opened
- Please make sure the `integration` branch exists

## Todos

- figure out how to cache yarn plugins (SFDX Scanner) or wait for Scanner GH Action to be released
80 changes: 80 additions & 0 deletions demo-classes/AccountService.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* AccountService Class
* Provides various services related to Account object
*/
public class AccountService {
/**
* Fetches details for a given account
* @param accountId The ID of the account
* @return Account The details of the account
*/
public Account getAccDetails(Id accountId) {
// SOQL query inside a loop - bad practice
for (Id accId : new List<Id>{ accountId }) {
Account acc = [SELECT Id, Name, Industry FROM Account WHERE Id = :accId];
return acc;
}
return null;
}

public Account getAccDetails2(Id accountId) {
// SOQL query inside a loop - bad practice
for (Id accId : new List<Id>{ accountId }) {
Account acc = [SELECT Id, Name, Industry FROM Account WHERE Id = :accId];
update acc;
return acc;
}
return null;
}

// This method lacks ApexDocs
public void UpdateAccount(List<Account> accounts) {
// DML operation inside a loop - bad practice
for (Account acc : accounts) {
acc.Name += ' - Updated';
update acc; // This should be done in bulk outside the loop
}
}

/**
* Deletes a specified account
* @param accountId The ID of the account to be deleted
*/
public void delete_Account(Id accountId) {
// Hardcoding IDs - bad practice
Id hardCodedId = '001xx000003DGAXAA4';
delete [SELECT Id FROM Account WHERE Id = :hardCodedId];
}

// This method lacks ApexDocs
public List<Account> ListAccounts() {
// Not using bulkified approach - bad practice
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
return accounts;
}

/**
* Calculates the annual revenue
* - This ApexDoc is incomplete and lacks parameter description
* @return Decimal The calculated annual revenue
*/
public String CalculateAnnualRevenue() {
// Lack of null checks and exception handling - bad practice
Account acc = [SELECT Industry FROM Account WHERE Id = '001xx000003DGAZAA4'];
return acc.Industry;
}

// This method lacks ApexDocs
public List<Account> ListAccounts2() {
// Not using bulkified approach - bad practice
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
return accounts;
}

// This method lacks ApexDocs
public List<Account> ListAccounts3() {
// Not using bulkified approach - bad practice
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
return accounts;
}
}
5 changes: 5 additions & 0 deletions demo-classes/AccountService.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<status>Active</status>
</ApexClass>
13 changes: 13 additions & 0 deletions demo-classes/CaseService.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* @description service related updates
*/
public with sharing class CaseService {
public static void doUpdates(Set<Id> caseIds) {
List<Case> cases = [SELECT Id, Status FROM Case WHERE Id IN :caseIds];
for (Case c : cases) {
c.Status = 'Closed';
// raise pmd error
update c;
}
}
}
5 changes: 5 additions & 0 deletions demo-classes/CaseService.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<status>Active</status>
</ApexClass>
68 changes: 68 additions & 0 deletions force-app/main/default/classes/ContentDocumentTriggerHandler.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @description Handles legacy metadata scan file uploads from GitHub
*/
public with sharing class ContentDocumentTriggerHandler {
/**
* @description sends a notification for legacy scan file uploads
* @param newContentDocumentMap - received from trigger
*/
@SuppressWarnings('PMD.ApexCRUDViolation')
public static void notifyOnLegacyCodeScanFileUploads(
Map<Id, ContentDocument> newContentDocumentMap
) {
Boolean isBulkOperation = newContentDocumentMap.values().size() > 1;
if (isBulkOperation) {
return;
}
List<CustomNotificationType> notificationTypes = [
SELECT Id, DeveloperName
FROM CustomNotificationType
WHERE DeveloperName = 'NewMetadataScanAvailable'
];
if (notificationTypes.isEmpty()) {
return;
}
CustomNotificationType notificationType = notificationTypes[0];
for (
ContentDocument matchingFile : filterContentDocuments(newContentDocumentMap.values())
) {
Messaging.CustomNotification notification = new Messaging.CustomNotification();

notification.setTitle('New Metadata Scan Available');
notification.setBody(
'Please review the scan and discuss necessary changes with the developer'
);

notification.setNotificationTypeId(notificationType.Id);
notification.setTargetId(matchingFile.Id);

// TODO identify targets for notification, i.e. public group's first level members?
// uploading user should be a technical user
try {
notification.send(new Set<String>{ UserInfo.getUserId() });
} catch (Exception e) {
return;
}
}
}

/**
* @description Filter ContentDocument sobjects by file extension and title
* @param contentDocuments list of ContentDocument sobjects to filter
* @return matchingContentDocuments
*/
public static List<ContentDocument> filterContentDocuments(
List<ContentDocument> contentDocuments
) {
List<ContentDocument> matchingContentDocuments = new List<ContentDocument>();
for (ContentDocument uploadedFile : contentDocuments) {
if (
String.isNotBlank(uploadedFile.Title) &&
uploadedFile.Title.contains('legacy-scan-results_')
) {
matchingContentDocuments.add(uploadedFile);
}
}
return matchingContentDocuments;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<status>Active</status>
</ApexClass>
Loading

0 comments on commit 2a9919b

Please sign in to comment.