Now that our web app is ready to be used let’s deploy it so that it can be reached via the internet. The most obvious choice is using GitHub Pages which can be used to host static pages, for example this dev log. To make it host our Angular web app (which is a single page application) we need to do some adjustments.

Set up automatic deployment

The easiest way to automate the deployment to GitHub Pages is to use GitHub Actions.

Let’s create a GitHub Action that builds the app and deploys it to GitHub Pages. For that we specify a workflow in .github/workflows/deploy-app-github.yml which looks like this

name: Deploy Angular App to GitHub Pages

on:
  push:
    branches:
      - main
permissions:
  contents: write
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "23"

      - name: Install Dependencies and Build
        run: |
          npm ci
          npm run build -- --base-href=/open-ebike-frontend/ --configuration production

      - name: Copy 404 page
        run: |
          cp dist/open-ebike-frontend/browser/index.html dist/open-ebike-frontend/browser/404.html

      - name: Deploy to GitHub Pages
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          folder: dist/open-ebike-frontend/browser

Our workflow is configured to run whenever changes are pushed to the main branch. Let’s take a deeper look into the different steps.

  • the first step checks out the code
  • the second step sets up Node.js which is needed to build the app (we can use the same version as we did during development)
  • the third step installs dependencies and builds the app specifying the base href to be /open-ebike-frontend and the configuration to be production
  • the fourth step copies the index.html file into a 404.html file - this trick is needed since GitHub pages is intended to host static pages. By providing a 404.html page with the same content as our index.html file we facilitate the user to open pages with different paths
  • the final step deploys the compiled app into another branch gh-pages - this is why we need to give the workflow the permission contents: write

Set up GitHub Pages

To make the deployed page available we need to configure it on the settings page of our GitHub repository. In the Pages section we select Deploy from a branch as a source and gh-pages as a branch to deploy from.

Voilà, the web app is available at https://open-ebike.github.io/open-ebike-frontend.

github-pages-config.png

Configure redirect URL

When trying to log in we notice that the Bosch login page states an indirect redirect URL. This is due to the fact that we configured it to be http://localhost:4200 in an earlier post.

invalid-redirect-url.png

To fix this we need to register another client application at https://portal.bosch-ebike.com (as described in an earlier post) with the following settings:

  • client application name: github-pages-public
  • login URL: https://open-ebike.github.io/open-ebike-frontend
  • redirect URL: https://open-ebike.github.io/open-ebike-frontend/*
  • confidential client: no

Of course, we need to grant the newly created client application access to the data generated by our eBike via https://flow.bosch-ebike.com as described in an ealier post.

To use this client we add a separate environment configuration that will be used during the deployment.

Et voilà, the login now works.

github-pages-login.png

Address the issue with same-origin policy

When trying to navigate to one of the pages we notice that there is another issue: the deployed app cannot access the Bosch eBike System APIs https://api.bosch-ebike.com. By default, browsers enforce a same-origin policy which prevents calling resources from another server. The server - in this case the Bosch eBike System APIs - would need enable the access via a CORS policy, for instance by sending a Access-Control-Allow-Origin header. Since we do not have access to the server we need to find a workaround.

One simple we can try is to use cors-anywhere. With CORS anywhere we send each request of our web app to a proxy which in turn will call our desired backend. While this approach bypasses the restrictions of the same-origin policy it is not intended to be used in production.

Since this approach does not seem to work we need to find another alternative - for example proxying the server requests ourselves using cloud functions. In the next post we will deploy the app to Firebase Hosting.