GitHub Actions - SharePoint Framework CI/CD
Tags: devops, githubactions, microsoftcloud, sharepoint
Published: 2023-03-09 12:23:40

The SharePoint Framework (SPFx) is Microsoft's development model for creating custom web parts in SharePoint Online. It can greatly expand SharePoint's capabilities - something out-of-the-box SharePoint Online _desperately_ needs.

This guide will walk you through the creation of a __basic__ CI/CD pipeline for SharePoint development. The GitHub Action workflow will build and deploy custom SharePoint web parts directly to your tenant.

---

{%- # TOC start -%}

- **[Why write SPFx web parts?](#why-write-SPFx-web-parts?)**
- **[Azure Active Directory prerequisites](#azure-active-directory-prerequisites)**
   * [Service Account](#service-account)
   * [Conditional Access](#conditional-access)
   * [Application Consent](#application-consent)
- **[Pipeline walkthrough](#pipeline-walkthrough)**
   * [Workflow triggers](#workflow-triggers)
   * [Running the job](#running-the-job)
   * [Setup steps](#setup-steps)
   * [Deployment steps](#deployment-steps)
- **[Wrapping up](#wrapping-up)**

{%- # TOC end -%}

---

## Why write SPFx web parts?

You can build some useful [web parts](https://github.com/OlivierCC/spfx-40-fantastics) and [widgets](https://github.com/pnp/sp-dev-fx-webparts) with SPFx. 

It can also add features Microsoft should have included standard.

For instance - SharePoint Online does not have a dynamically updating table of contents. You have to [type it out manually](https://pnp.github.io/blog/post/create-a-table-of-contents-on-sharepoint-modern-pages/). 

![Ralph Wiggum - 404](https://i.gifer.com/6iot.gif)

If you want a dynamic table of contents you need a [custom web part](https://answers.microsoft.com/en-us/msoffice/forum/all/toc-webpart-cannot-be-seen-in-sharepoint-modern/7482e22b-0a4f-4821-9c27-e33fe6192ace).

---

## Azure Active Directory prerequisites

Before building the pipeline, a few Azure Active Directory prerequisites must be met.

### Service Account

A service account (SA) with the appropriate permissions in SharePoint is required to deploy the .sspkg to the application catalog. 

- If targeting a single site, the account will need _Site Collection Administrator_. 
- If targeting the tenant as a whole, the account will likely need _SharePoint Administrator_.

Unfortunately, we cannot use a service principal/application registration for SharePoint.

> _Attention: Currently, SharePoint does not support authentication using Azure AD App ID and Secret. CLI for Microsoft 365 commands that call the SharePoint APIs will fail while logged in to Microsoft 365 using a Secret._

See the [documentation](https://pnp.github.io/cli-microsoft365/user-guide/connecting-office-365/#log-in-using-a-secret) for additional information on this limitation. 

### Conditional Access

[Conditional Access](https://learn.microsoft.com/en-us/azure/active-directory/conditional-access/overview) rules should be configured allowing the SA access only if certain conditions are met.

I would highly recommend building a policy leveraging some or all of these rules:
- Identity (i.e. apply only to the SA)
- Trusted IP ranges
- Runner OS
- Cloud Application (31359c7f-bd7e-475c-86db-fdb8c937548e)

### Application Consent

The SA will need [application consent](https://pnp.github.io/powershell/articles/authentication.html) to login to the PnP Management Shell. Run the PowerShell commands below in the Azure Cloud Shell.

```powershell
Install-Module PnP.PowerShell
Register-PnPManagementShellAccess
```

You may not be able to perform these actions in a [development M365 tenant](https://developer.microsoft.com/en-us/microsoft-365/dev-program) - the developer instance prohibits the use of the Cloud Shell.

---

## Pipeline walkthrough

Now that the prerequisites have been addressed, we will dissect the pipeline, [spfxpipeline.yaml](https://github.com/ColeHeard/githubactions-pipeline-gallery/blob/11955db66727a04bcca5f8111f56965fcb659dcd/samples/sharepoint/spfxpipeline.yaml). 

### Workflow triggers

The workflow will only begin once a trigger condition has been met, as described by __on:__

```yaml
name: "Basic SharePoint Framework Pipeline - Microsoft 365"

on:
  push:
    branches:
      - 'main'
  workflow_dispatch:
```

This workflow is waiting for one of two events to occur:
2. A push occurs on the main branch.
3. A "workflow_dispatch" is manually triggered - useful when testing.

Once triggered, the workflow will kick off a **Job**.

### Running the job

We start by labeling the job and selecting a runner.

```yaml
jobs:
  deployment:
    name: 'SPFx Deployment'
    runs-on: self-hosted
    steps:
```

The **runs-on:** argument defines the runner our job will execute on. 

In the example above, I use a [self-hosted runner](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners) provisioned within Azure. 

Substitute _self-hosted_ with _ubuntu-latest_ to use a [GitHub-hosted runner](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners).

Next, **steps:**  define the Job's individual actions.

### Setup steps

The first two steps are Actions. Actions are small, purpose built applets. 

> Action steps are defined with the **uses:** keyword.

The [checkout](https://github.com/actions/checkout) action is a workflow staple - it clones the repository's code down to the runner. 

The [setup-node](https://github.com/actions/setup-node)  action installs and configures node. 

```yaml
      - name: Checkout
        uses: actions/checkout@v3

      - name: Node.js install
        uses: actions/setup-node@v1
        with:
          node-version: 16.x
```
The next few steps run commands directly in the runner's shell.

> Run steps are defined by the **run:** keyword.

One step installs [NPM](https://www.npmjs.com/) (with the [CI switch](https://docs.npmjs.com/cli/v9/commands/npm-ci)) and the next one installs [Gulp](https://gulpjs.com/) globally. 

```yaml
      - name: NPM install
        run: npm ci

      - name: Gulp install
        run: npm i -g gulp
```

The environment has been configured.

### Deployment steps

The runner can now compile our web part. The step below builds our package with two Gulp commands. 

```yaml
      - name: Gulp package
        run: |
          gulp bundle --ship
          gulp package-solution --ship   
```

The [action-cli-login](https://github.com/pnp/action-cli-login) action will authenticate with the [service account](#service-account) discussed earlier. 

As the SA's credentials are sensitive, the workflow will reference [GitHub Action Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) to inject the credentials into the pipeline as needed.

```yaml
      - name: M365 login
        id: login
        uses: pnp/action-cli-login@v2.2.1
        with:
          ADMIN_USERNAME:  ${{ secrets.ADMIN_USERNAME }}
          ADMIN_PASSWORD:  ${{ secrets.ADMIN_PASSWORD }}
```

Finally, the package is deployed to the tenant's SharePoint application catalog using [action-cli-deploy](https://github.com/pnp/action-cli-deploy).

We specify the package's relative path with **APP_FILE_PATH** and grant permission to overwrite any existing deployments of the application with **OVERWRITE** set to _true_.
```yaml
      - name: SharePoint deploy
        id: deploy
        uses: pnp/action-cli-deploy@v3.0.1
        with:
          APP_FILE_PATH: ./package/example-web-part.sppkg
          OVERWRITE: true
```
<sub>In production, you'd want to replace the hardcoded path and file name with a variable. 

The workflow has completed and the custom web part has been deployed to the tenant.

![The Application has been uploaded.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fw8lc82enjh7b6wb8z27.png)

---

## Wrapping up

As I said back at the beginning of this post, this is a basic pipeline. It is a solid foundation to build upon and not much more.

If you're looking for something more advanced, consider:
- Integrating the workflow with your project management platform of choice.
- Setting up [Jest](https://jestjs.io/) or [Enzyme](https://enzymejs.github.io/enzyme/)  for SPFx unit testing.
- Building a SharePoint UAT environment to pre-stage your deployment.

Thanks for reading!

![Ralph Wiggum - Goodbye](https://www.reactiongifs.com/wp-content/uploads/2013/07/ralph-wave.gif)