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
15 changes: 15 additions & 0 deletions nodeapp-1/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
npm-debug.log
mochawesome-report
.git
.gitignore
.npmrc
.eslintrc.json
.mocharc.json
test
debug-solution.txt
*.code-workspace
.vscode
.devcontainer
.github
.azure
20 changes: 20 additions & 0 deletions nodeapp-1/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"env": {
"node": true,
"es2021": true,
"mocha": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"],
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-console": "off"
}
}
13 changes: 13 additions & 0 deletions nodeapp-1/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"timeout": 5000,
"exit": true,
"reporter": "mochawesome",
"reporter-options": [
"reportDir=mochawesome-report",
"reportFilename=test-results",
"html=true",
"json=true",
"overwrite=true",
"inline=true"
]
}
19 changes: 19 additions & 0 deletions nodeapp-1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:18-alpine

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./

RUN npm ci --only=production

# Bundle app source
COPY . .

# Expose port
EXPOSE 3000

# Start the application
CMD [ "node", "app.js" ]
69 changes: 53 additions & 16 deletions nodeapp-1/README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,77 @@
# Node & Express Demo App for GitHub Actions, Azure DevOps, and Beyond
# Node Express Azure - AZ-400 Demo App

## Last edited by Tim Warner
Sample Node.js Express application for demonstrating CI/CD pipelines in AZ-400 training.

> Build Your First CI/CD Pipeline using Azure DevOps with this Demo App.
## Features

This is a Node and Express web application used to demonstrate CI/CD with Azure DevOps. You can clone this repo and use it within Azure DevOps to build, test, and release to an Azure App Service web app.
- Express.js web application
- Handlebars templating
- Mocha/Chai testing with mochawesome reports
- ESLint for code quality
- Docker support
- Azure DevOps and GitHub Actions ready

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/timothywarner/node-express-azure)
## Getting Started

## Running and Testing Locally:
### Prerequisites

You can use these commands to install, test, and run the app locally. (Not Required)
- Node.js 18.x or higher
- npm 8.x or higher

### Install
### Installation

```
```bash
npm install
```

### Test
### Running Locally

```bash
npm start
```

The app will be available at http://localhost:3000

### Running Tests

```bash
npm test
```

![alt text](https://user-images.githubusercontent.com/5126491/51065379-c1743280-15c1-11e9-80fd-6a3d7ab4ac1b.jpg "Unit Test")
### Linting

Navigate to the `/test` folder to review the unit tests for this project. These tests will run as part of your Azure DevOps Build pipeline. See `azure-pipelines.yml` in this repo.
```bash
npm run lint
```

### Start
### Docker

Build the image:
```bash
docker build -t nodeapp-1 .
```
npm start

Run the container:
```bash
docker run -p 3000:3000 nodeapp-1
```

## CI/CD Pipelines

This app includes example pipelines for:
- Azure Pipelines (see `/pipelines` folder in repo root)
- GitHub Actions (see `.github/workflows` in repo root)

## Azure Artifacts

To publish to Azure Artifacts:

1. Create `.npmrc` from `.npmrc.template`
2. Set up authentication
3. Run `npm run publish:dev` or `npm run publish:prod`

### License
## Environment Variables

This project is licensed under the Apache License 2.0
- `PORT` - Server port (default: 3000)
- `NODE_ENV` - Environment (development/production)
- `AZURE_ARTIFACTS_FEED` - Azure Artifacts feed URL
13 changes: 9 additions & 4 deletions nodeapp-1/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ app.use('/', index);
app.use('/who', who);
app.use('/contact', contact);

// Start the server
app.listen(app.get('port'), () => {
console.log(`Server running on port ${app.get('port')}`);
});
// Export the app for testing
module.exports = app;

// Only start the server if this file is run directly
if (require.main === module) {
app.listen(app.get('port'), () => {
console.log(`Server running on port ${app.get('port')}`);
});
}
6 changes: 5 additions & 1 deletion nodeapp-1/config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module.exports = {
port: process.env.PORT || 443,
port: process.env.PORT || 3000,
environment: process.env.NODE_ENV || 'development',
azure: {
artifactsFeed: process.env.AZURE_ARTIFACTS_FEED || 'https://pkgs.dev.azure.com/certstarorg/_packaging/az400-npm-feed/npm/registry/'
}
};
9 changes: 9 additions & 0 deletions nodeapp-1/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Server entry point for production
const app = require('./app');

const port = process.env.PORT || app.get('port');

app.listen(port, () => {
console.log(`Server running on port ${port}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});
8 changes: 4 additions & 4 deletions nodeapp-1/test/contact_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ const config = require('../config');
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
const server = require('../app');
const app = require('../app');

chai.use(chaiHttp);

describe('/GET', () => {
describe('/GET contact', () => {
it('returns the contact page', (done) => {
chai.request(`http://localhost:${config.port}`)
chai.request(app)
.get('/contact')
.end((err, res) => {
res.should.have.status(200);
res.text.should.contain('Contact Us');
res.text.should.contain('Contact information');
done();
});
});
Expand Down
6 changes: 3 additions & 3 deletions nodeapp-1/test/index_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ const config = require('../config');
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
const server = require('../app');
const app = require('../app');

chai.use(chaiHttp);

describe('/GET', () => {
it('returns the homepage', (done) => {
chai.request(`http://localhost:${config.port}`)
chai.request(app)
.get('/')
.end((err, res) => {
res.should.have.status(200);
res.text.should.contain('Welcome to GitHub Copilot Training!');
done();
});
});
});
});
10 changes: 5 additions & 5 deletions nodeapp-1/test/who_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ const config = require('../config');
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
const server = require('../app');
const app = require('../app');

chai.use(chaiHttp);

describe('/GET', () => {
describe('/GET who', () => {
it('returns the who page', (done) => {
chai.request(`http://localhost:${config.port}`)
chai.request(app)
.get('/who')
.end((err, res) => {
res.should.have.status(200);
res.text.should.contain('Who We Are');
res.text.should.contain('Who are you?');
done();
});
});
});
});
Loading