Skip to content

Commit 17cccf8

Browse files
committed
feat: deploy multiple apps on one github pages instance
1 parent 79f5b07 commit 17cccf8

15 files changed

Lines changed: 553 additions & 81 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Assemble Pages subdirectory
2+
description: >-
3+
Overlays a freshly built app onto the assembled-site storage branch, adds the
4+
SPA fallback (.nojekyll + 404.html), uploads the merged site as a Pages
5+
artifact, and persists the merged tree back to the storage branch. This lets
6+
each micro app deploy independently while GitHub Pages still serves a single
7+
site (see README "Hosting").
8+
9+
inputs:
10+
source-dir:
11+
description: Freshly built directory to publish (e.g. dist/apps/portfolio/browser).
12+
required: true
13+
target-subdir:
14+
description: Subdirectory within the site. Empty for the site root (the shell).
15+
required: false
16+
default: ''
17+
storage-branch:
18+
description: Branch that stores the full assembled site between deploys.
19+
required: false
20+
default: pages-content
21+
preserve:
22+
description: >-
23+
Space-separated top-level entries to keep when publishing the site root,
24+
so independent micro-app subdirectories survive a shell redeploy.
25+
required: false
26+
default: 'portfolio storybook'
27+
github-token:
28+
description: Token used to read and update the storage branch.
29+
required: true
30+
31+
runs:
32+
using: composite
33+
steps:
34+
- name: Assemble merged site 🧩
35+
shell: bash
36+
env:
37+
SOURCE_DIR: ${{ inputs.source-dir }}
38+
TARGET_SUBDIR: ${{ inputs.target-subdir }}
39+
STORAGE_BRANCH: ${{ inputs.storage-branch }}
40+
PRESERVE: ${{ inputs.preserve }}
41+
GH_TOKEN: ${{ inputs.github-token }}
42+
run: |
43+
set -euo pipefail
44+
45+
if [ ! -d "$SOURCE_DIR" ]; then
46+
echo "::error::source-dir '$SOURCE_DIR' does not exist" >&2
47+
exit 1
48+
fi
49+
50+
REPO_URL="https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
51+
52+
rm -rf _site _storage
53+
mkdir -p _site
54+
55+
# Pull the current full site from the storage branch (if it exists).
56+
if git ls-remote --exit-code --heads "$REPO_URL" "$STORAGE_BRANCH" >/dev/null 2>&1; then
57+
git clone --quiet --depth 1 --branch "$STORAGE_BRANCH" "$REPO_URL" _storage
58+
( cd _storage && tar -cf - --exclude=.git . ) | ( cd _site && tar -xf - )
59+
rm -rf _storage
60+
fi
61+
62+
# Overlay the freshly built app into its slot.
63+
if [ -z "$TARGET_SUBDIR" ] || [ "$TARGET_SUBDIR" = "." ]; then
64+
# Site root (shell): replace root entries but keep sibling micro apps.
65+
keep=".git .nojekyll $PRESERVE"
66+
shopt -s dotglob nullglob
67+
for entry in _site/*; do
68+
name="$(basename "$entry")"
69+
case " $keep " in
70+
*" $name "*) continue ;;
71+
esac
72+
rm -rf "$entry"
73+
done
74+
shopt -u dotglob nullglob
75+
cp -R "$SOURCE_DIR"/. _site/
76+
else
77+
rm -rf "_site/${TARGET_SUBDIR}"
78+
mkdir -p "_site/${TARGET_SUBDIR}"
79+
cp -R "$SOURCE_DIR"/. "_site/${TARGET_SUBDIR}/"
80+
fi
81+
82+
# SPA fallback + disable Jekyll processing.
83+
touch _site/.nojekyll
84+
if [ -f _site/index.html ]; then
85+
cp _site/index.html _site/404.html
86+
fi
87+
88+
echo "Assembled site tree:"
89+
find _site -maxdepth 2 -mindepth 1 | sort
90+
91+
- name: Upload Pages artifact πŸ“¦
92+
uses: actions/upload-pages-artifact@v5
93+
with:
94+
path: _site
95+
96+
- name: Persist assembled site to storage branch πŸ’Ύ
97+
shell: bash
98+
env:
99+
TARGET_SUBDIR: ${{ inputs.target-subdir }}
100+
STORAGE_BRANCH: ${{ inputs.storage-branch }}
101+
GH_TOKEN: ${{ inputs.github-token }}
102+
run: |
103+
set -euo pipefail
104+
REPO_URL="https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
105+
SLOT="${TARGET_SUBDIR:-/ (shell)}"
106+
107+
cd _site
108+
rm -rf .git
109+
git init -q
110+
git add -A
111+
git -c user.name="github-actions[bot]" \
112+
-c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
113+
commit -q -m "deploy: update ${SLOT} (${GITHUB_SHA})" || {
114+
echo "Nothing to persist."; exit 0;
115+
}
116+
git branch -M "$STORAGE_BRANCH"
117+
git push -f "$REPO_URL" "$STORAGE_BRANCH"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Deploy portfolio
2+
3+
# The portfolio Module Federation remote is served (and exposes its
4+
# remoteEntry.json) at:
5+
# http://codestar.nl/nx-reference/portfolio/
6+
# Only apps/portfolio (and shared libs) changes trigger this workflow, so the
7+
# remote deploys independently of the shell.
8+
on:
9+
push:
10+
branches: [main]
11+
paths:
12+
- 'apps/portfolio/**'
13+
- 'libs/**'
14+
- 'package.json'
15+
- 'package-lock.json'
16+
- 'nx.json'
17+
- 'tsconfig.base.json'
18+
- '.github/workflows/deploy-portfolio.yml'
19+
- '.github/actions/deploy-subdir/**'
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: write
24+
pages: write
25+
id-token: write
26+
27+
# Serialize with the other Pages deploys so storage-branch writes never race.
28+
concurrency:
29+
group: pages
30+
cancel-in-progress: false
31+
32+
jobs:
33+
deploy:
34+
runs-on: ubuntu-latest
35+
environment:
36+
name: github-pages
37+
url: ${{ steps.deployment.outputs.page_url }}
38+
steps:
39+
- name: Checkout πŸ›ŽοΈ
40+
uses: actions/checkout@v7
41+
42+
- name: Use Node.js 24 🟒
43+
uses: actions/setup-node@v6
44+
with:
45+
node-version: '24'
46+
cache: 'npm'
47+
48+
- name: Install dependencies πŸ”§
49+
run: npm ci
50+
51+
- name: Build portfolio remote πŸ—οΈ
52+
run: npx nx build portfolio --configuration=production
53+
54+
- name: Assemble /portfolio 🧩
55+
uses: ./.github/actions/deploy-subdir
56+
with:
57+
source-dir: dist/apps/portfolio/browser
58+
target-subdir: portfolio
59+
github-token: ${{ secrets.GITHUB_TOKEN }}
60+
61+
- name: Deploy to GitHub Pages πŸš€
62+
id: deployment
63+
uses: actions/deploy-pages@v5
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Deploy shell
2+
3+
# The shell (Module Federation host) is served at the site root:
4+
# http://codestar.nl/nx-reference/
5+
# Only apps/demo (and shared libs) changes trigger this workflow β€” a portfolio
6+
# remote change never rebuilds the shell, demonstrating module-federation
7+
# loose coupling.
8+
on:
9+
push:
10+
branches: [main]
11+
paths:
12+
- 'apps/demo/src/**'
13+
- 'apps/demo/public/**'
14+
- 'apps/demo/federation.config.js'
15+
- 'apps/demo/federation.manifest.prod.json'
16+
- 'apps/demo/project.json'
17+
- 'apps/demo/tsconfig*.json'
18+
- 'libs/**'
19+
- 'package.json'
20+
- 'package-lock.json'
21+
- 'nx.json'
22+
- 'tsconfig.base.json'
23+
- '.github/workflows/deploy-shell.yml'
24+
- '.github/actions/deploy-subdir/**'
25+
workflow_dispatch:
26+
27+
permissions:
28+
contents: write
29+
pages: write
30+
id-token: write
31+
32+
# Serialize with the other Pages deploys so storage-branch writes never race.
33+
concurrency:
34+
group: pages
35+
cancel-in-progress: false
36+
37+
jobs:
38+
deploy:
39+
runs-on: ubuntu-latest
40+
environment:
41+
name: github-pages
42+
url: ${{ steps.deployment.outputs.page_url }}
43+
steps:
44+
- name: Checkout πŸ›ŽοΈ
45+
uses: actions/checkout@v7
46+
47+
- name: Use Node.js 24 🟒
48+
uses: actions/setup-node@v6
49+
with:
50+
node-version: '24'
51+
cache: 'npm'
52+
53+
- name: Install dependencies πŸ”§
54+
run: npm ci
55+
56+
- name: Build shell πŸ—οΈ
57+
run: npx nx build demo --configuration=production
58+
59+
- name: Apply production federation manifest πŸ”—
60+
run: cp apps/demo/federation.manifest.prod.json dist/apps/demo/browser/federation.manifest.json
61+
62+
- name: Assemble site root 🧩
63+
uses: ./.github/actions/deploy-subdir
64+
with:
65+
source-dir: dist/apps/demo/browser
66+
target-subdir: ''
67+
github-token: ${{ secrets.GITHUB_TOKEN }}
68+
69+
- name: Deploy to GitHub Pages πŸš€
70+
id: deployment
71+
uses: actions/deploy-pages@v5
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Deploy storybook
2+
3+
# The aggregated Storybook catalog is served at:
4+
# http://codestar.nl/nx-reference/storybook/
5+
# Triggered by Storybook config, story files, or shared library changes.
6+
on:
7+
push:
8+
branches: [main]
9+
paths:
10+
- 'apps/demo/.storybook/**'
11+
- 'libs/**'
12+
- '**/*.stories.ts'
13+
- '**/*.stories.tsx'
14+
- '**/*.mdx'
15+
- 'tsconfig.doc.json'
16+
- 'package.json'
17+
- 'package-lock.json'
18+
- '.github/workflows/deploy-storybook.yml'
19+
- '.github/actions/deploy-subdir/**'
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: write
24+
pages: write
25+
id-token: write
26+
27+
# Serialize with the other Pages deploys so storage-branch writes never race.
28+
concurrency:
29+
group: pages
30+
cancel-in-progress: false
31+
32+
jobs:
33+
deploy:
34+
runs-on: ubuntu-latest
35+
environment:
36+
name: github-pages
37+
url: ${{ steps.deployment.outputs.page_url }}
38+
steps:
39+
- name: Checkout πŸ›ŽοΈ
40+
uses: actions/checkout@v7
41+
42+
- name: Use Node.js 24 🟒
43+
uses: actions/setup-node@v6
44+
with:
45+
node-version: '24'
46+
cache: 'npm'
47+
48+
- name: Install dependencies πŸ”§
49+
run: npm ci
50+
51+
- name: Build Storybook πŸ“š
52+
run: |
53+
npm run docs:json
54+
npx nx run demo:build-storybook
55+
56+
- name: Assemble /storybook 🧩
57+
uses: ./.github/actions/deploy-subdir
58+
with:
59+
source-dir: dist/storybook/demo
60+
target-subdir: storybook
61+
github-token: ${{ secrets.GITHUB_TOKEN }}
62+
63+
- name: Deploy to GitHub Pages πŸš€
64+
id: deployment
65+
uses: actions/deploy-pages@v5

β€Ž.github/workflows/prod.ymlβ€Ž

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
Β (0)