Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions .github/workflows/node-azure-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
name: Node.js CI/CD to Azure

# Teaching example for GitHub Actions with Azure deployment

on:
push:
branches: [ main, master ]
paths:
- 'nodeapp-1/**'
- '.github/workflows/node-azure-deploy.yml'
pull_request:
branches: [ main, master ]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'development'
type: choice
options:
- development
- staging
- production

env:
NODE_VERSION: '18.x'
AZURE_WEBAPP_NAME: 'webapp-az400-demo'
AZURE_WEBAPP_PACKAGE_PATH: './nodeapp-1'
WORKING_DIRECTORY: 'nodeapp-1'

jobs:
# ========================================
# JOB 1: BUILD AND TEST
# ========================================
build:
name: Build and Test
runs-on: ubuntu-latest

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: 🔧 Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: '${{ env.WORKING_DIRECTORY }}/package-lock.json'

- name: 📦 Install dependencies
run: npm ci
working-directory: ${{ env.WORKING_DIRECTORY }}

- name: 🔒 Security audit
run: npm audit --audit-level=high
working-directory: ${{ env.WORKING_DIRECTORY }}
continue-on-error: true

- name: 🧹 Lint code
run: npm run lint
working-directory: ${{ env.WORKING_DIRECTORY }}

- name: 🧪 Run tests with Mocha
run: npm test
working-directory: ${{ env.WORKING_DIRECTORY }}

- name: 📊 Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: mochawesome-report
path: ${{ env.WORKING_DIRECTORY }}/mochawesome-report
retention-days: 7

- name: 📤 Upload artifact for deployment
uses: actions/upload-artifact@v4
with:
name: node-app
path: ${{ env.WORKING_DIRECTORY }}
retention-days: 5

# ========================================
# JOB 2: DEPLOY TO DEVELOPMENT
# ========================================
deploy-dev:
name: Deploy to Development
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: development
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: 📥 Download artifact
uses: actions/download-artifact@v4
with:
name: node-app
path: ./app

- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🚀 Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}-dev
package: ./app
startup-command: 'npm start'

- name: 🔥 Smoke test
run: |
sleep 30
response=$(curl -s -o /dev/null -w "%{http_code}" https://${{ env.AZURE_WEBAPP_NAME }}-dev.azurewebsites.net)
if [ $response -eq 200 ]; then
echo "✅ Smoke test passed!"
else
echo "❌ Smoke test failed with status code: $response"
exit 1
fi

# ========================================
# JOB 3: DEPLOY TO STAGING
# ========================================
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: deploy-dev
environment:
name: staging
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: 📥 Checkout for tests
uses: actions/checkout@v4

- name: 📥 Download artifact
uses: actions/download-artifact@v4
with:
name: node-app
path: ./app

- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🚀 Deploy to staging slot
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
slot-name: staging
package: ./app

- name: 🧪 Run integration tests
run: |
cd ${{ env.WORKING_DIRECTORY }}
npm ci
export TEST_URL=https://${{ env.AZURE_WEBAPP_NAME }}-staging.azurewebsites.net
npm test
continue-on-error: true

# ========================================
# JOB 4: DEPLOY TO PRODUCTION
# ========================================
deploy-prod:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: ${{ steps.swap-slots.outputs.webapp-url }}

steps:
- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🔄 Swap staging to production
id: swap-slots
run: |
az webapp deployment slot swap \
--resource-group rg-az400-demo \
--name ${{ env.AZURE_WEBAPP_NAME }} \
--slot staging \
--target-slot production

echo "webapp-url=https://${{ env.AZURE_WEBAPP_NAME }}.azurewebsites.net" >> $GITHUB_OUTPUT

- name: 🏷️ Create release tag
if: success()
uses: actions/github-script@v7
with:
script: |
const tag = `v${context.runNumber}`;
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/${tag}`,
sha: context.sha
});
console.log(`✅ Created tag: ${tag}`);

# ========================================
# REUSABLE WORKFLOW: SECURITY SCAN
# ========================================
security-scan:
name: Security Scanning
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: 🔍 Run Dependabot scan
uses: github/super-linter@v5
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_JAVASCRIPT_ES: true
VALIDATE_JSON: true
VALIDATE_YAML: true
Loading