Deploying a Node.js App to SAP BTP Cloud Foundry Using GitHub Actions

Estimated read time 8 min read

This post walks through setting up a simple CI/CD pipeline using GitHub Actions that automatically deploys a Node.js app to SAP BTP Cloud Foundry on every push to main.

What You’ll Need

An SAP BTP account with Cloud Foundry enabled and a space to deploy toA GitHub accountNode.js and the CF CLI installed locallyA non-SSO username and password for your BTP account (used by the pipeline

The App

We’ll deploy a minimal Node.js Express app with a simple Fiori-styled frontend. Nothing fancy — the point is the pipeline, not the app.

The project structure is:

btp-cicd-demo/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── public/
│ └── index.html
├── server.js
├── package.json
├── manifest.yml
└── .gitignore

server.js

const express = require(“express”);
const path = require(“path”);

const app = express();
app.use(express.static(path.join(__dirname, “public”)));

app.get(“/”, (req, res) => {
res.sendFile(path.join(__dirname, “public”, “index.html”));
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

package.json

{
“name”: “btp-cicd-demo”,
“version”: “1.0.0”,
“description”: “BTP CI/CD Demo App”,
“main”: “server.js”,
“scripts”: {
“start”: “node server.js”
},
“dependencies”: {
“express”: “^5.2.1”
}
}

manifest.yml

applications:
– name: btp-cicd-demo
memory: 128M
buildpacks:
– nodejs_buildpack

public/index.html

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″ />
<title>BTP CI/CD Demo</title>
<script
id=”sap-ui-bootstrap”
src=”https://openui5.hana.ondemand.com/resources/sap-ui-core.js”
data-sap-ui-theme=”sap_fiori_3″
data-sap-ui-libs=”sap.m”
data-sap-ui-compatVersion=”edge”
data-sap-ui-preload=”async”
></script>
<script>
sap.ui.getCore().attachInit(function () {
sap.ui.require([“sap/m/App”, “sap/m/Page”, “sap/m/Text”], function (App, Page, Text) {
new App({
pages: [
new Page({
title: “BTP CI/CD Demo”,
content: [
new Text({ text: “Deployed via SAP CI/CD Service” })
]
})
]
}).placeAt(“content”);
});
});
</script>
</head>
<body class=”sapUiBody sapUiSizeCompact”>
<div id=”content”></div>
</body>
</html>

.gitignore

node_modules/
.env

Initialise the repo, commit everything, and push to GitHub.

The Pipeline

Create .github/workflows/deploy.yml:

name: Deploy to Cloud Foundry

on:
push:
branches:
– main

jobs:
deploy:
runs-on: ubuntu-latest
steps:
– name: Checkout code
uses: actions/checkout@v3

– name: Install CF CLI
run: |
curl -L “https://packages.cloudfoundry.org/stable?release=linux64-binary&version=v8&source=github” | tar -zx
sudo mv cf8 /usr/local/bin/cf

– name: Deploy to Cloud Foundry
env:
CF_API: https://api.cf.ap10.hana.ondemand.com
CF_USERNAME: ${{ secrets.CF_USERNAME }}
CF_PASSWORD: ${{ secrets.CF_PASSWORD }}
CF_ORG: <your-org>
CF_SPACE: <your-space>
run: |
cf login -a $CF_API -u $CF_USERNAME -p $CF_PASSWORD -o “$CF_ORG” -s “$CF_SPACE”
cf push btp-cicd-demo

Update CF_API with your region endpoint, and CF_ORG and CF_SPACE with your actual org and space names. These are visible when you run cf target.

GitHub Secrets

The pipeline uses two secrets to authenticate with Cloud Foundry without hardcoding credentials in the workflow file. In your GitHub repo go to Settings, then Secrets and variables, then Actions, and add two repository secrets:

CF_USERNAME — your BTP account email address CF_PASSWORD — your BTP account password

These are encrypted by GitHub and injected into the workflow at runtime. They never appear in logs or the codebase.

Note: this approach requires a non-SSO login. If your account authenticates via a corporate identity provider, you will need a technical user created by your BTP administrator for pipeline use.

Running the Pipeline

Push the workflow file to main:

git add .
git commit -m “Add CI/CD workflow”
git push

Go to the Actions tab in your GitHub repo. You should see the workflow trigger immediately. Click on it to watch the steps run — checkout, CF CLI install, login, and push.

If everything is configured correctly you’ll see a green tick and your app will be live at your CF route.

Once deployed, loading the app URL in your browser should give you something like this:

 

What This Gives You

Every push to main now triggers an automatic deployment to Cloud Foundry. No manual cf push, no forgetting to deploy, no environment drift between what’s in the repo and what’s running.

It’s a simple starting point. From here you can add a build step, run tests before deploying, deploy to multiple spaces (dev, test, prod) based on branch, or introduce the MTA build tool for more complex landscapes.

Full Repo: https://github.com/neilaspin/btp-cicd-demo

 

 

 

​ This post walks through setting up a simple CI/CD pipeline using GitHub Actions that automatically deploys a Node.js app to SAP BTP Cloud Foundry on every push to main.What You’ll NeedAn SAP BTP account with Cloud Foundry enabled and a space to deploy toA GitHub accountNode.js and the CF CLI installed locallyA non-SSO username and password for your BTP account (used by the pipelineThe AppWe’ll deploy a minimal Node.js Express app with a simple Fiori-styled frontend. Nothing fancy — the point is the pipeline, not the app.The project structure is:btp-cicd-demo/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── public/
│ └── index.html
├── server.js
├── package.json
├── manifest.yml
└── .gitignoreserver.jsconst express = require(“express”);
const path = require(“path”);

const app = express();
app.use(express.static(path.join(__dirname, “public”)));

app.get(“/”, (req, res) => {
res.sendFile(path.join(__dirname, “public”, “index.html”));
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));package.json{
“name”: “btp-cicd-demo”,
“version”: “1.0.0”,
“description”: “BTP CI/CD Demo App”,
“main”: “server.js”,
“scripts”: {
“start”: “node server.js”
},
“dependencies”: {
“express”: “^5.2.1”
}
}manifest.ymlapplications:
– name: btp-cicd-demo
memory: 128M
buildpacks:
– nodejs_buildpackpublic/index.html<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″ />
<title>BTP CI/CD Demo</title>
<script
id=”sap-ui-bootstrap”
src=”https://openui5.hana.ondemand.com/resources/sap-ui-core.js”
data-sap-ui-theme=”sap_fiori_3″
data-sap-ui-libs=”sap.m”
data-sap-ui-compatVersion=”edge”
data-sap-ui-preload=”async”
></script>
<script>
sap.ui.getCore().attachInit(function () {
sap.ui.require([“sap/m/App”, “sap/m/Page”, “sap/m/Text”], function (App, Page, Text) {
new App({
pages: [
new Page({
title: “BTP CI/CD Demo”,
content: [
new Text({ text: “Deployed via SAP CI/CD Service” })
]
})
]
}).placeAt(“content”);
});
});
</script>
</head>
<body class=”sapUiBody sapUiSizeCompact”>
<div id=”content”></div>
</body>
</html>.gitignorenode_modules/
.envInitialise the repo, commit everything, and push to GitHub.The PipelineCreate .github/workflows/deploy.yml:name: Deploy to Cloud Foundry

on:
push:
branches:
– main

jobs:
deploy:
runs-on: ubuntu-latest
steps:
– name: Checkout code
uses: actions/checkout@v3

– name: Install CF CLI
run: |
curl -L “https://packages.cloudfoundry.org/stable?release=linux64-binary&version=v8&source=github” | tar -zx
sudo mv cf8 /usr/local/bin/cf

– name: Deploy to Cloud Foundry
env:
CF_API: https://api.cf.ap10.hana.ondemand.com
CF_USERNAME: ${{ secrets.CF_USERNAME }}
CF_PASSWORD: ${{ secrets.CF_PASSWORD }}
CF_ORG: <your-org>
CF_SPACE: <your-space>
run: |
cf login -a $CF_API -u $CF_USERNAME -p $CF_PASSWORD -o “$CF_ORG” -s “$CF_SPACE”
cf push btp-cicd-demoUpdate CF_API with your region endpoint, and CF_ORG and CF_SPACE with your actual org and space names. These are visible when you run cf target.GitHub SecretsThe pipeline uses two secrets to authenticate with Cloud Foundry without hardcoding credentials in the workflow file. In your GitHub repo go to Settings, then Secrets and variables, then Actions, and add two repository secrets:CF_USERNAME — your BTP account email address CF_PASSWORD — your BTP account passwordThese are encrypted by GitHub and injected into the workflow at runtime. They never appear in logs or the codebase.Note: this approach requires a non-SSO login. If your account authenticates via a corporate identity provider, you will need a technical user created by your BTP administrator for pipeline use.Running the PipelinePush the workflow file to main:git add .
git commit -m “Add CI/CD workflow”
git pushGo to the Actions tab in your GitHub repo. You should see the workflow trigger immediately. Click on it to watch the steps run — checkout, CF CLI install, login, and push.If everything is configured correctly you’ll see a green tick and your app will be live at your CF route.Once deployed, loading the app URL in your browser should give you something like this: What This Gives YouEvery push to main now triggers an automatic deployment to Cloud Foundry. No manual cf push, no forgetting to deploy, no environment drift between what’s in the repo and what’s running.It’s a simple starting point. From here you can add a build step, run tests before deploying, deploy to multiple spaces (dev, test, prod) based on branch, or introduce the MTA build tool for more complex landscapes.Full Repo: https://github.com/neilaspin/btp-cicd-demo     Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author