Skip to content

Commit

Permalink
Additional options for running the standardised build process (#50)
Browse files Browse the repository at this point in the history
- New reusable workflow that runs the standardised build process using a single job
- New composite action that encapsulates the standardised build process
  • Loading branch information
JamesDawson authored Sep 10, 2024
1 parent 14c18cc commit b2f903e
Show file tree
Hide file tree
Showing 8 changed files with 525 additions and 13 deletions.
62 changes: 62 additions & 0 deletions .github/workflows/ci-composite-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: ci-compositie-action
on:
pull_request:
branches:
- main
paths:
- .github/workflows/ci-composite-action.yml
- actions/**
workflow_dispatch:
inputs:
forcePublish:
description: When true the Publish stage will always be run, otherwise it only runs for tagged versions.
required: false
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
actions: write # enable cache clean-up
checks: write # enable test result annotations
contents: write # enable creating releases
issues: read
packages: write # enable publishing packages
pull-requests: write # enable test result annotations

jobs:
build:
name: Build
runs-on: ubuntu-latest
outputs:
semver: ${{ steps.run_build.outputs.semver }}
major: ${{ steps.run_build.outputs.major }}
majorMinor: ${{ steps.run_build.outputs.majorMinor }}
preReleaseTag: ${{ steps.run_build.outputs.preReleaseTag }}

steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
fetch-depth: 0
- uses: ./actions/prepare-env-vars-and-secrets
id: prepareEnvVarsAndSecrets
with:
environmentVariablesYaml: |
BUILDVAR_NuGetPublishSource: "${{ startsWith(github.ref, 'refs/tags/') && 'https://api.nuget.org/v3/index.json' || 'https://nuget.pkg.github.com/endjin/index.json' }}"
secretsYaml: |
NUGET_API_KEY: "${{ startsWith(github.ref, 'refs/tags/') && secrets.ENDJIN_NUGET_APIKEY || secrets.ENDJIN_GITHUB_PUBLISHER_PAT }}"
SBOM_ANALYSIS_RELEASE_READER_PAT: "${{ secrets.ENDJIN_GITHUB_READER_PAT }}"
- uses: ./actions/run-build-process
id: run_build
with:
netSdkVersion: '8.x'
# workflow_dispatch inputs are always strings, the type property is just for the UI
forcePublish: ${{ github.event.inputs.forcePublish == 'true' }}
sbomOutputStorageAccountName: ${{ vars.SBOM_OUTPUT_STORAGE_ACCOUNT_NAME }}
sbomOutputStorageContainerName: ${{ vars.SBOM_OUTPUT_STORAGE_CONTAINER_NAME }}
buildEnv: ${{ steps.prepareEnvVarsAndSecrets.outputs.environmentVariablesYamlBase64}}
buildSecrets: ${{ steps.prepareEnvVarsAndSecrets.outputs.secretsYamlBase64 }}
token: ${{ secrets.GITHUB_TOKEN }}

4 changes: 4 additions & 0 deletions .github/workflows/ci-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ on:
pull_request:
branches:
- main
paths:
- .github/workflows/ci-matrix.yml
- .github/workflows/scripted-build-matrix-pipeline.yml
- actions/**
workflow_dispatch:
inputs:
forcePublish:
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/ci-single-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: ci-single-job
on:
pull_request:
branches:
- main
paths:
- .github/workflows/ci-single-job.yml
- .github/workflows/scripted-build-single-job-pipeline.yml
- actions/**
workflow_dispatch:
inputs:
forcePublish:
description: When true the Publish stage will always be run, otherwise it only runs for tagged versions.
required: false
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
actions: write # enable cache clean-up
checks: write # enable test result annotations
contents: write # enable creating releases
issues: read
packages: write # enable publishing packages
pull-requests: write # enable test result annotations

jobs:
prepareConfig:
name: Prepare Configuration
runs-on: ubuntu-latest
outputs:
RESOLVED_ENV_VARS: ${{ steps.prepareEnvVarsAndSecrets.outputs.environmentVariablesYamlBase64 }}
RESOLVED_SECRETS: ${{ steps.prepareEnvVarsAndSecrets.outputs.secretsYamlBase64 }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0

# Declare any environment variables and/or secrets that need to be available inside the build process
- uses: ./actions/prepare-env-vars-and-secrets
id: prepareEnvVarsAndSecrets
with:
environmentVariablesYaml: |
BUILDVAR_NuGetPublishSource: "${{ startsWith(github.ref, 'refs/tags/') && 'https://api.nuget.org/v3/index.json' || 'https://nuget.pkg.github.com/endjin/index.json' }}"
secretsYaml: |
NUGET_API_KEY: "${{ startsWith(github.ref, 'refs/tags/') && secrets.ENDJIN_NUGET_APIKEY || secrets.ENDJIN_GITHUB_PUBLISHER_PAT }}"
SBOM_ANALYSIS_RELEASE_READER_PAT: "${{ secrets.ENDJIN_GITHUB_READER_PAT }}"
build:
needs: prepareConfig
uses: ./.github/workflows/scripted-build-single-job-pipeline.yml
with:
netSdkVersion: '8.x'
# workflow_dispatch inputs are always strings, the type property is just for the UI
forcePublish: ${{ github.event.inputs.forcePublish == 'true' }}
buildEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }}
secrets:
buildAzureCredentials: ${{ secrets.ENDJIN_PROD_ACR_READER_CREDENTIALS }}
buildSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }}
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ on:
pull_request:
branches:
- main
paths:
- .github/workflows/ci.yml
- .github/workflows/scripted-build-pipeline.yml
- actions/**
workflow_dispatch:
inputs:
forcePublish:
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/scripted-build-single-job-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
on:
workflow_call:
inputs:
netSdkVersion:
description: The primary .NET SDK version required for the build process, as per the syntax required by the 'setup-dotnet' action.
required: true
type: string
default: '8.0.x'
additionalNetSdkVersion:
description: An additional .NET SDK version required for the build process, as per the syntax required by the 'setup-dotnet' action.
required: false
type: string
pythonVersion:
description: Specify an additional Python version required for the build process
required: false
type: string
configuration:
description: The target build configuration.
required: false
default: 'Release'
type: string
buildEnv:
description: A JSON object representing the environment variables required when running the build script.
required: false
type: string
buildArtifactName:
description: If set, during the test phase, uploads a GitHub artifact with the provided name (path must be specified in `artifactPath`)
required: false
type: string
buildArtifactPath:
description: If set, during the test phase, uploads a GitHub artifact with the provided path (name must be specified in `artifactName`). The path can be a file, directory or wildcard pattern; multiple paths can be specified using newline demiliter.
required: false
type: string
forcePublish:
description: When true, the Publish stage will be run regardless of the current branch or tag.
required: false
default: false
type: boolean
buildScriptPath:
description: The path to the build script to run.
required: false
default: ./build.ps1
type: string
buildTasks:
description: The tasks that need to be run as part of the build process, formatted as a comma-delimited string (e.g. 'FullBuild' or 'Build,Test').
required: false
default: ''
type: string
runsOn:
description: The operating system to run all stages of this workflow.
required: false
default: ubuntu-latest
type: string

secrets:
buildAzureCredentials:
required: false
buildSecrets:
description: A YAML string representing a dictionary of secrets required when running the 'compile' stage of this workflow.
required: false

jobs:
build:
name: Build
runs-on: ${{ inputs.runsOn }}
outputs:
semver: ${{ steps.run_build.outputs.semver }}
major: ${{ steps.run_build.outputs.major }}
majorMinor: ${{ steps.run_build.outputs.majorMinor }}
preReleaseTag: ${{ steps.run_build.outputs.preReleaseTag }}

steps:
- uses: endjin/Endjin.RecommendedPractices.GitHubActions/actions/run-build-process@main
id: run_build
with:
netSdkVersion: ${{ inputs.netSdkVersion }}
additionalNetSdkVersion: ${{ inputs.additionalNetSdkVersion }}
buildArtifactName: ${{ inputs.buildArtifactName }}
buildArtifactPath: ${{ inputs.buildArtifactPath }}
buildScriptPath: ${{ inputs.buildScriptPath }}
buildTasks: ${{ inputs.buildTasks }}
codeCoverageSummaryDir: ${{ vars.CODE_COVERAGE_SUMMARY_DIR || '_codeCoverage' }}
codeCoverageSummaryFile: ${{ vars.CODE_COVERAGE_SUMMARY_FILE || 'SummaryGithub.md' }}
configuration: ${{ inputs.configuration }}
pythonVersion: ${{ inputs.pythonVersion }}
runsOn: ${{ inputs.runsOn }}
# workflow_dispatch inputs are always strings, the type property is just for the UI
forcePublish: ${{ github.event.inputs.forcePublish == 'true' }}
sbomOutputStorageAccountName: ${{ vars.SBOM_OUTPUT_STORAGE_ACCOUNT_NAME }}
sbomOutputStorageContainerName: ${{ vars.SBOM_OUTPUT_STORAGE_CONTAINER_NAME }}
buildEnv: ${{ inputs.buildEnv }}
buildSecrets: ${{ secrets.buildSecrets }}
buildAzureCredentials: ${{ secrets.buildAzureCredentials }}
token: ${{ secrets.GITHUB_TOKEN }}
98 changes: 85 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,104 @@

This repository contains [re-usable GitHub Action workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) and [composite actions](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action) for our standardised CI processes.

Our standardised build process is divided into the following phases:
- Compile
- Test
- Analyse
- Package
- Publish

By default, the 'Publish' phase is only executed for tagged versions or when manually triggered with the 'Force Publish' option enabled.

> ***USAGE TIP**: For smaller/less complex repositories, you will likely get the quickest build times by using the `run-build-process` composite action, an example of how to consume this in your repo's `build.yml` can be found [here](.github/workflows/ci-composite-action.yml)*.
## Reusable Workflows

### Multi-Job Workflows
These run the logical phases of the build process using discrete jobs, which can be beneficial for large builds due to the parallelisation when running tests and building packages.

- `scripted-build-pipeline` - encapsulates our standard CI build process, using separate jobs for Compile, Test, Package & Publish phases
- `scripted-build-matrix-pipeline` - as above, except the Test phase includes [matrix](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow) support

The diagram below shows the high-level process that the non-matrix multi-job workflow implements:

```mermaid
graph LR
compile["Compile"]-->analyse["Code Analysis"]
analyse-->test["Run Tests"]
test-->pubtests["Publish Test Results"]
analyse-->package["Build Packages"]
pubtests-->publish["Publish Packages"]
package-->publish
```

The diagram below illustrates the different high-level process for the matrix-enabled version of the multi-job workflow:

```mermaid
graph LR
compile["Compile"]-->analyse["Code Analysis"]
analyse-->test1["Run Tests (matrix 1)"]
analyse-->test2["Run Tests (matrix 2)"]
test1-->pubtests["Publish Test Results"]
test2-->pubtests["Publish Test Results"]
analyse-->package["Build Packages"]
pubtests-->publish["Publish Packages"]
package-->publish
```


### Single-Job Workflow
This runs the logical phases of the build process as a single job, closely mimicking the local developer build.

***NOTE**: A consuming build will still require a second job in order to support passing arbitrary environment variables and secrets to this workflow. See the [example](.github/workflows/ci-single-job.yml) for more details.*

- `scripted-build-single-job-pipeline` - encapsulates our standard CI build process, using a single job

This diagram shows the default sequence of the build process implemented by the single-job workflow implement (NOTE: This can altered by overriding the `buildTasks` input parameter):

```mermaid
graph LR
compile["Compile"]-->test["Run Tests"]
test-->analyse["Code Analysis"]
analyse-->package["Build Packages"]
package-->publish["Publish Packages"]
publish-->pubtests["Publish Test Results"]
```


## Composite Actions

### Orchestration Composite Actions
This are used as alternatives to reusable workflows to encapsulate complete processes.

- `run-build-process` - provides an alternative to using the reusable workflows and encapsulates the complete build process. This allows the consuming CI build to be run a single job, with the trade-off of requiring somewhat more boilerplate.

The `run-build-process` action implements the same logical build process as the [Single-Job Workflow](#single-job-workflows) (NOTE: This can altered by overriding the `buildTasks` input parameter)

```mermaid
graph LR
compile["Compile"]-->test["Run Tests"]
test-->analyse["Code Analysis"]
analyse-->package["Build Packages"]
package-->publish["Publish Packages"]
publish-->pubtests["Publish Test Results"]
```


### Feature Composite Actions
These composite actions are used to encapsulated specific functionality so it can be easily re-used between workflows.

- `prepare-env-vars-and-secrets` - provides a workaround for not natively being able to pass arbitrary environment variables and secrets to a reusable workflow. Based on assembling the required values into 2 well-known variables that act as containers for the variables and secrets that need to be passed.
- `run-scripted-build` - encapsulates the steps for executing our [PowerShell-based build tooling](https://www.powershellgallery.com/packages/Endjin.RecommendedPractices.Build) - typically used via one of the above reusable workflows.
- `set-env-vars-and-secrets` - the consuming side of the workaround for passing arbitrary environment variables and secrets. Unwraps the bundled environment variables and secrets so they are available to the running workflow.

## Examples

The following serve as examples of using the reusable workflows found in this repo:
The following workflows serve as examples of how to consume the different reusable workflows and composite actions found in this repo:

- [ci.yml](.github/workflows/ci.yml) - used for validating changes to the `scripted-build-pipeline` reusable workflow
- [ci-matrix.yml](.github/workflows/ci-matrix.yml) - used for validating changes to the `scripted-build-matrix-pipeline` reusable workflow
- [ci-single-job.yml](.github/workflows/ci-single-job.yml) - used for validating changes to the `scripted-build-single-job-pipeline` reusable workflow
- [ci-composite-action](.github/workflows/ci-composite-action.yml) - used for validating change to the `run-build-process` composite action

## CI Build Process Overview

The diagram below illustrates the high-level process that workflows implementing our standard CI build use:

```mermaid
graph LR
compile["Compile"]-->analyse["Code Analysis"]
analyse-->test["Run Tests"]
test-->pubtests["Publish Test Results"]
analyse-->package["Build Packages"]
pubtests-->publish["Publish Packages"]
package-->publish
```
Loading

0 comments on commit b2f903e

Please sign in to comment.