GitHub Actions

Mastering Monorepo Deployment: Angular & Astro on GitHub Pages for Enhanced Software Project Metrics

In today's fast-paced development environment, monorepos have become a cornerstone for managing complex projects, offering benefits like shared codebases and simplified dependency management. However, deploying multiple static sites from a single monorepo to platforms like GitHub Pages often presents unique challenges. When not handled correctly, these deployment hurdles can directly impact software project metrics, leading to delays, increased debugging time, and a dip in team productivity.

This article delves into a common scenario: deploying an Angular application to the root path (/) and an Astro Starlight documentation site to a subpath (/wiki) from a single GitHub repository. We'll explore the pitfalls that lead to frustrating 404 errors and provide a robust, step-by-step solution that ensures seamless deployment and contributes positively to your team's delivery efficiency.

The Monorepo Deployment Challenge: Angular & Astro on GitHub Pages

Consider a developer, binder-badge, who aimed to deploy two distinct static sites from a .github.io monorepo using GitHub Pages. The setup involved:

  • An Angular site intended for the root domain (e.g., domain.com/).
  • An Astro Starlight site, used for personal documentation, intended for a subpath (e.g., domain.com/wiki).

Despite a seemingly logical GitHub Actions workflow that built both projects and attempted to consolidate their outputs into a dist folder for artifact upload, both target URLs were returning 404 errors. This situation highlights a critical gap in understanding how GitHub Pages interprets deployment artifacts and how different frameworks structure their build outputs.

Why the 404s? Unpacking the Root Causes

The core of the problem stemmed from a combination of GitHub Pages' serving mechanism and the default build behaviors of Angular and Astro:

  • GitHub Pages' Serving Mechanism: GitHub Pages is designed to serve content directly from the root of the uploaded artifact. If your main index.html isn't immediately at the artifact's root, it won't be found.
  • Angular's Output Structure: By default, an Angular build (especially for browser-based applications) places its main static assets, including index.html, within a browser/ subdirectory inside its dist folder (e.g., dist/portfolio/browser/). This means the critical files aren't directly at the root of the desired deployment path.
  • Astro's Base Path Configuration: Astro, by default, generates asset URLs relative to the root (/). Even if you meticulously place Astro's compiled files under a /wiki directory within your deployment artifact, its internal links and asset references (e.g., to CSS, JS, images) might still point to /styles.css instead of /wiki/styles.css. This leads to broken links and further 404 errors for resources, even if the main index.html for the wiki is found.

Understanding these nuances is crucial for any team looking to optimize their tooling and improve delivery metrics.

The Working Solution: A Step-by-Step Guide for Seamless Monorepo Deployment

The path to a working solution involved precise configuration adjustments and a careful consolidation of build outputs. Here’s how to achieve a robust deployment for your Angular and Astro monorepo:

1. Configure Astro's Base Path

The most critical step for the Astro site is to explicitly tell it where it will be hosted. Edit your wiki/astro.config.mjs file:

import { defineConfig } from 'astro/config';

export default defineConfig({
  site: 'https://your-custom-domain.com',
  base: '/wiki/',
});

Setting site ensures correct absolute URLs for sitemaps and canonical links, while base: '/wiki/' instructs Astro to generate all internal asset paths and links relative to the /wiki/ subdirectory. This is fundamental for Astro to function correctly under a subpath.

2. Build Both Projects

Your build commands remain largely standard. Ensure Angular is built with the correct base href and Astro is built after its configuration update:

# Angular build
npm run build -- --configuration production --base-href "/"

# Astro build
npm run build

3. The Crucial Final Artifact Structure

The key to success lies in creating a dist/ folder that GitHub Pages can correctly interpret. The final structure of your uploaded artifact must look exactly like this:

dist/
├── index.html          <-- Angular's main index.html
├── main-XXXX.js        <-- Angular's JS
├── styles-XXXX.css     <-- Angular's CSS
├── favicon.ico         <-- Angular's favicon
├── 404.html            <-- Global 404 page
├── CNAME               <-- Custom domain configuration
└── wiki/
    ├── index.html      <-- Astro's main index.html
    ├── _astro/         <-- Astro's internal assets
    ├── assets/         <-- Astro's static assets
    ├── guides/         <-- Astro's content (example)
    └── sitemap-index.xml <-- Astro's sitemap

Notice that Angular's output is directly at the root of dist/, while Astro's output resides entirely within dist/wiki/. This structure directly addresses GitHub Pages' serving mechanism.

Diagram showing the correct 'dist' folder structure for GitHub Pages deployment with Angular at root and Astro at /wiki
Diagram showing the correct 'dist' folder structure for GitHub Pages deployment with Angular at root and Astro at /wiki

4. Precision in Copy Commands

To achieve the desired artifact structure, your GitHub Actions workflow needs precise copy commands. This is where many initial attempts go wrong. Replace your existing consolidation step with these commands:

# Create the target directory for Astro
mkdir -p dist/wiki

# Copy Angular's browser contents directly to the root of 'dist/'
cp -r ./portfolio/dist/portfolio/browser/* ./dist/

# Copy all of Astro's build output into 'dist/wiki/'
cp -r ./wiki/dist/* ./dist/wiki/

# (Optional) Copy CNAME and a global 404.html to the root of 'dist/'
# cp ./CNAME ./dist/
# cp ./404.html ./dist/

Key points here:

  • ./portfolio/dist/portfolio/browser/*: We specifically target the browser subdirectory of Angular's output to get the actual static files.
  • ./dist/: Angular's files are copied directly to the root of the final deployment artifact.
  • ./wiki/dist/*: All of Astro's output is copied.
  • ./dist/wiki/: Astro's files are placed within the wiki subdirectory, respecting its configured base path.

5. Refined GitHub Actions Workflow

With the build and copy steps correctly defined, the final GitHub Actions workflow for uploading and deploying remains straightforward:

name: Deploy to GitHub Pages
on:
  push:
    branches: [ main ]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v5
      - name: Setup NodeJS
        uses: actions/setup-node@v4
        with:
          node-version: "22"

      # ... (Install dependencies for portfolio and wiki)

      # Build portfolio (Angular)
      - name: Build portfolio
        working-directory: portfolio
        run: npm run build -- --configuration production --base-href "/"

      # Build wiki (Astro)
      - name: Build wiki
        working-directory: wiki
        run: npm run build

      # Consolidate compiled files into the correct structure
      - name: Consolidate dists for GitHub Pages
        run: |
          mkdir -p dist/wiki
          cp -r ./portfolio/dist/portfolio/browser/* ./dist/
          cp -r ./wiki/dist/* ./dist/wiki/
          # Optional: Copy global CNAME and 404.html to root of dist
          # cp ./CNAME ./dist/
          # cp ./404.html ./dist/

      - name: Upload pages artifact
        uses: actions/upload-pages-artifact@v4
        with:
          path: ./dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

After these adjustments, https://your-custom-domain.com/ will correctly serve your Angular application, and https://your-custom-domain.com/wiki will display your Astro Starlight documentation, with all assets resolving as expected.

Key Takeaways for Technical Leaders

This scenario offers several valuable lessons for dev teams, product managers, and CTOs focused on improving software project metrics and delivery pipelines:

  • Understand Your Deployment Target: Never assume how a platform like GitHub Pages will serve files. Always verify its expectations regarding artifact structure.
  • Framework-Specific Nuances: Be aware of how different frameworks (Angular's browser/ output, Astro's base path) handle their build outputs and adjust configurations accordingly. This understanding is crucial for efficient tooling.
  • Precision in CI/CD: The copy commands are not trivial. Small errors in pathing can lead to significant debugging time. Investing in robust CI/CD practices and thorough testing of deployment artifacts can drastically improve delivery speed.
  • Impact on Productivity: Streamlining deployment workflows directly influences how to measure performance of software developers. Fewer deployment failures mean more time spent on feature development and less on infrastructure firefighting.
  • Leverage GitHub Analytics: Post-deployment, utilize github analytics to monitor page access, error rates, and overall site performance. This data can provide insights into user experience and validate the success of your deployment strategy.

By meticulously addressing these details, teams can transform complex monorepo deployments from a source of frustration into a streamlined, reliable process, ultimately enhancing overall project health and accelerating time to market.

Share:

Track, Analyze and Optimize Your Software DeveEx!

Effortlessly implement gamification, pre-generated performance reviews and retrospective, work quality analytics, alerts on top of your code repository activity

 Install GitHub App to Start
devActivity Screenshot