Wilco Menge
Read all my blogsIn the past we have written about automated builds and deployments of SAP Commerce in SAP Commerce Cloud V2 (CCV2). Today I would like to show you how to achieve this with Github Actions, the CI/CD platform of Github. Our goal is to automate builds and deployments as much as possible to reduce errors and streamline the delivery of features.
What is Github Actions?
Github Actions is a CI/CD platform provided by Github, offering automation for building, testing, and deploying software. It allows you to automate activities related to building, testing and deploying your software. A lot of out of the box flows are available, for example, performing a sonar analysis or run a unit test suite or integration test suite. A comprehensive list can be found on the marketplace.
The marketplace however doesn’t contain any actions to build and deploy to CCV2, so that means we will have to do some configuration and scripting
Github Actions: Workflows, Jobs, Steps, Actions
Github actions consist of a number of components:
- Workflows
On the highest level, a workflow is a container of a number of related jobs. The workflow reacts on events, which can be a cron trigger, or a repository event, such as the merge of a PR. When a relevant event occurs, all jobs that have no other start conditions are triggered (by default in parallel) - Jobs
A job is basically a container for a number of steps that contain actual logic. All steps within a job are executed on the same runner. This means that the steps within the same job can easily share data and state. When a job begins, a runner is created and started. Think of a runner as a virtual machine or container, that is active as long as the job is running. - Steps
A step is a discrete operation or command that is being executed within the context of a job on a runner. Depending on the type of runner (for example, Windows or Ubuntu linux), different commands or applicatons are available.
Finally, a number of workflows together can be packaged as an action and can be distributed separate from your application repository. We will not delve into this topic in this episode. Far more detailed information can be found in the documentation of Github actions
Implement Steps to Build and Deploy
The high level structure of a workflow is now clear: We will need a workflow that does a CCV2 Build followed by a CCV2 Deployment. but: we still need to figure out how to implement those steps. SAP supplies an API to perform most tasks that you can manually perform in the CCV2 Portal. There are a number of ways to tap into this functionality from Github Actions:
- SAP Provides a CLI that can be downloaded and executed as part of the job. We have looked into the CLI before in the past when it was stable. Currently the application is stable and usable. It does however add some complexity as the CLI needs to be either available in the repo on donwloaded on demand.
- Roll your own CLI using any scripting environment. This is what my colleague Maikel Bollemeijer did earlier, then based on Python. Also this will add some complexity as the runtime and CLI need to be available
- Roll your own using a bash script. As the runners run ubuntu linux, we have all available components already available: bash as a scripting environment, and curl as our default go-to solution whenever a network request is needed.
As building a bash script would introduce the least dependencies and overhead, for me this was the most interesting option to explore:
./github/scripts/build-sap-commerce.sh
:
[bash]#!/bin/bash branch=$1 timestamp=$(date +"%Y-%m-%d-%H-%M-%S") create_build_output=$(curl -K "./.github/scripts/curl-config.txt" \ -X POST "https://portalapi.commerce.ondemand.com/v2/subscriptions/$SUBSCRIPTION_CODE/builds" \ --header "Authorization: Bearer $API_TOKEN" \ --data "{\"branch\":\"$1\",\"name\":\"$branch-$timestamp\"}") # Check if the command succeeded if [ $? -ne 0 ]; then echo "$create_build_output" | jq . exit 1 fi code=$(echo $create_build_output | jq -r .code) echo "Successfully created build:" echo $create_build_output | jq . # Share data between jobs echo "build_code=$code" >> "$GITHUB_OUTPUT" counter=0 status=UNKNOWN while [[ $counter -lt 100 ]] && [[ "$status" == "UNKNOWN" || "$status" == "BUILDING" ]]; do let counter=counter+1 build_progress_output=$(curl -K "./.github/scripts/curl-config.txt" \ --header "Authorization: Bearer $API_TOKEN" \ "https://portalapi.commerce.ondemand.com/v2/subscriptions/$SUBSCRIPTION_CODE/builds/$code/progress") # Check if the command succeeded if [ $? -ne 0 ]; then echo "$build_progress_output" | jq . exit 1 fi status=$(echo $build_progress_output | jq -r .buildStatus) percentage=$(echo $build_progress_output | jq -r .percentage) echo "$status $percentage%" if [[ "$status" != "UNKNOWN" && "$status" != "BUILDING" ]]; then echo "build has reached end state: $status" echo $build_progress_output | jq . if [[ "$status" == "SUCCESS" ]]; then exit 0 fi if [[ "$status" == "FAILURE" ]]; then exit 1 fi fi sleep 150 done[/bash]
The script above triggers a build by calling the /v2/subscriptions/$SUBSCRIPTION_CODE/builds
endpoint. If creation of a build is succesful, the progress of the build is monitored by repeatedly performing requests to the /v2/subscriptions/$SUBSCRIPTION_CODE/builds/$code/progress
endpoint until and end state (SUCCESS
or FAILURE
) is reached. (In case something goes very wrong, we will also stop polling after 100 tries).
./github/scripts/deploy-sap-commerce.sh:
[bash]#!/bin/bash create_deployment_output=$(curl -K "./scripts/curl-config.txt" \ -X POST "https://portalapi.commerce.ondemand.com/v2/subscriptions/$SUBSCRIPTION_CODE/deployments" \ --header "Authorization: Bearer $API_TOKEN" \ --data "{\"buildCode\":\"$1\",\"databaseUpdateMode\":\"UPDATE\", \"environmentCode\": \"$2\", \"strategy\": \"ROLLING_UPDATE\"}") # Check if the command succeeded if [ $? -ne 0 ]; then echo "$create_deployment_output" | jq . exit 1 fi code=$(echo $create_deployment_output | jq -r .code) echo "Successfully created deployment:" echo $create_deployment_output | jq . counter=0 status=SCHEDULED while [[ $counter -lt 100 ]] && [[ "$status" == "SCHEDULED" || "$status" == "DEPLOYING" ]]; do let counter=counter+1 deployment_progress_output=$(curl -K "./scripts/curl-config.txt" \ --header "Authorization: Bearer $API_TOKEN" \ "https://portalapi.commerce.ondemand.com/v2/subscriptions/$SUBSCRIPTION_CODE/deployments/$code/progress") # Check if the command succeeded if [ $? -ne 0 ]; then echo "$deployment_progress_output" | jq . exit 1 fi status=$(echo $deployment_progress_output | jq -r .deploymentStatus) percentage=$(echo $deployment_progress_output | jq -r .percentage) echo "$status $percentage%" if [[ "$status" != "SCHEDULED" && "$status" != "DEPLOYING" ]]; then echo "build has reached end state: $status" echo $deployment_progress_output | jq . if [[ "$status" == "DEPLOYED" ]]; then exit 0 fi if [[ "$status" == "FAIL" ]]; then exit 1 fi fi sleep 150 done[/bash]
Very similar to the previous step: We create a deployment based on the build code of the previous step. If triggering of a deployment has been succesfull, the progress of the deployment is monitored by repeatedly performing requests to the /v2/subscriptions/$SUBSCRIPTION_CODE/deployments/$code/progress
endpoint until and end state (DEPLOYED
or FAIL
) is reached. (In case something goes very wrong, we will also stop polling after 100 tries).
The workflow file will glue the steps together in one workflow, containing of 2 separate jobs:
.github/workflows/main.yml
:
[yml]name: sap-commerce-ccv2-deployment run-name: Deploy to SAP Commerce Cloud on: workflow_dispatch: schedule: - cron: '0 22 * * *' # daily at 22:00 env: API_TOKEN: ${{ secrets.CCV2_API_TOKEN }} SUBSCRIPTION_CODE: ${{ vars.CCV2_SUBSCRIPTION_CODE }} jobs: build_commerce: runs-on: ubuntu-latest outputs: build_code: ${{ steps.build_commerce.outputs.build_code }} name: Build SAP Commerce steps: - uses: actions/checkout@v4 with: sparse-checkout: .github - id: build_commerce run: ./.github/scripts/build-sap-commerce.sh develop shell: bash deploy_commerce: needs: build_commerce runs-on: ubuntu-latest name: Deploy SAP Commerce to CCV steps: - uses: actions/checkout@v4 with: sparse-checkout: .github - env: BUILD_CODE: ${{needs.build_commerce.outputs.build_code}} run: ./.github/scripts/deploy-sap-commerce.sh $BUILD_CODE d1 shell: bash[/yml]
The workflow consists of two jobs, build_commerce
and deploy_commerce
. Secrets and environment variable (CCV2_API_TOKEN
and CCV2_API_TOKEN
) defined in github ui and imported. For each job, first, the .github
directory containing all scripts is fetched from the repository by the checkout action. Next the scripts are run in order: first build and (if successful) deploy. This workflow currently has two triggers: it can be started manually and will be run every night at 22:00
In github i’ve put the the complete example.
Further Possibilities
We have shown the basics of automating builds and deployment in CCV2 using Github actions. There are a lot of additional possibilities, such as:
- Running unit tests/integration test suite: Automate the execution of unit tests and integration tests as part of your CI/CD pipeline to ensure code quality and functionality.
- Running Sonar analysis: Integrate SonarQube or SonarCloud to perform static code analysis, identify code smells, bugs, and security vulnerabilities, and enforce code quality standards.
- Sending notifications on success or error states: Configure notifications to be sent via email, Slack, or other communication channels to notify team members of successful builds or deployments, as well as any errors or failures that occur.
- Managing releases with GitHub: Utilize GitHub’s release management features to create, publish, and manage releases of your software, including versioning, release notes, and asset management. This can help streamline the distribution and communication of new features and updates to users.
If you have any questions, don’t hestitate to get in touch!