Skip to content

Commit 8972bfa

Browse files
authored
Merge pull request #172 from interledger/stephan/intorg-200
feat(ci): workflow to also build to gcs bucket
2 parents 206b8b0 + c54a684 commit 8972bfa

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed

.github/workflows/deploy_gcs.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Deploy to GCS
2+
3+
on:
4+
# Trigger the workflow every time you push to the `main` branch
5+
# Using a different branch name? Replace `main` with your branch's name
6+
push:
7+
branches: [main]
8+
# Allows you to run this workflow manually from the Actions tab on GitHub.
9+
workflow_dispatch:
10+
11+
# Allow this job to clone the repo and create a page deployment
12+
permissions:
13+
contents: read
14+
pages: write
15+
id-token: write
16+
17+
jobs:
18+
deploy:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout your repository using git
22+
uses: actions/checkout@v3
23+
- uses: actions/setup-node@v3
24+
with:
25+
node-version: 18
26+
27+
- name: Authenticate to Google Cloud
28+
uses: google-github-actions/auth@v2
29+
with:
30+
credentials_json: ${{ secrets.GSA_JSON }}
31+
32+
- name: Set up Cloud SDK
33+
uses: google-github-actions/setup-gcloud@v2
34+
35+
- uses: oven-sh/setup-bun@v1
36+
- name: Install dependencies
37+
run: bun install
38+
- name: Test build website
39+
run: bun run build
40+
41+
- name: Deploy to GCS
42+
run: gsutil -m rsync -r -d ./dist/ gs://${{ secrets.GCS_BUCKET }}/developers
43+
44+
- name: Build nginx container
45+
run: |
46+
cd ci/nginx-rewrite
47+
gcloud builds submit --tag gcr.io/interledger-websites/nginx-rewrite:latest .
48+
49+
- name: Deploy to Cloud Run
50+
run: |
51+
gcloud run deploy nginx-rewrite \
52+
--image gcr.io/interledger-websites/nginx-rewrite:latest \
53+
--platform managed \
54+
--region us-central1 \
55+
--allow-unauthenticated \
56+
--port 8080 \
57+
--memory 512Mi \
58+
--cpu 1 \
59+
--min-instances 0 \
60+
--max-instances 10 \
61+
--quiet
62+
63+
- name: Invalidate CDN cache
64+
run: |
65+
gcloud compute url-maps invalidate-cdn-cache interledger-org \
66+
--path "/developers/*" \
67+
--async

ci/nginx-rewrite/Dockerfile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
FROM google/cloud-sdk:alpine AS fetcher
2+
3+
# Fetch the developers content from GCS
4+
RUN mkdir -p /content/developers && \
5+
gsutil -m rsync -r gs://interledger-org-developers-portal/developers/ /content/developers/
6+
7+
FROM nginx:alpine
8+
9+
# Copy the fetched content
10+
COPY --from=fetcher /content /usr/share/nginx/html
11+
12+
# Copy custom nginx configuration
13+
COPY nginx.conf /etc/nginx/nginx.conf
14+
15+
# Ensure nginx runs as non-root (Cloud Run requirement)
16+
RUN touch /var/run/nginx.pid && \
17+
chown -R nginx:nginx /var/run/nginx.pid && \
18+
chown -R nginx:nginx /var/cache/nginx && \
19+
chown -R nginx:nginx /var/log/nginx && \
20+
chown -R nginx:nginx /usr/share/nginx/html
21+
22+
USER nginx
23+
24+
EXPOSE 8080
25+
26+
CMD ["nginx", "-g", "daemon off;"]

ci/nginx-rewrite/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Nginx Rewrite Service
2+
3+
Simple nginx-based static file server for the developers portal under the `/developers` path.
4+
5+
## Background
6+
7+
Previously, this content was served from an AWS S3 bucket through AWS CloudFront CDN. After migrating to GCS, we encountered a significant limitation: the GCS CDN lacks CloudFront's intelligent URL rewriting capabilities, and automatic `index.html` serving doesn't come out of the box. The simplest solution was to host the content through nginx and handle all rewrite rules at the server level.
8+
9+
## What it does
10+
11+
- Serves static files from the GCS bucket `gs://interledger-org-developers-portal/developers`
12+
- Content is baked into the container image at build time (multi-stage Docker build)
13+
- Handles index.html fallback for pretty URLs using `try_files`
14+
- Redirects paths without trailing slash to the slash version for clean URLs
15+
- Serves all content under `/developers/` path
16+
17+
## Building and deploying
18+
19+
Deployment happens automatically via GitHub Actions when changes are merged to `main`:
20+
21+
1. Site is built with Astro
22+
2. Files are synced to `gs://interledger-org-developers-portal/developers`
23+
3. Container is built (fetches content from GCS at build time)
24+
4. New revision is deployed to Cloud Run
25+
26+
Manual deployment:
27+
28+
```bash
29+
cd ci/nginx-rewrite
30+
31+
# Build the container (fetches from GCS during build)
32+
gcloud builds submit --tag gcr.io/interledger-websites/nginx-rewrite:latest .
33+
34+
# Deploy to Cloud Run
35+
gcloud run deploy nginx-rewrite \
36+
--image gcr.io/interledger-websites/nginx-rewrite:latest \
37+
--platform managed \
38+
--region us-central1 \
39+
--allow-unauthenticated \
40+
--port 8080 \
41+
--memory 512Mi \
42+
--cpu 1 \
43+
--min-instances 0 \
44+
--max-instances 10
45+
```
46+
47+
## How it works
48+
49+
### Multi-stage Docker build
50+
51+
1. **Stage 1 (fetcher)**: Uses `google/cloud-sdk:alpine` to fetch content from GCS
52+
- Runs `gsutil rsync` to download `gs://interledger-org-developers-portal/developers/` to `/content/developers/`
53+
2. **Stage 2 (nginx)**: Uses `nginx:alpine` to serve the content
54+
- Copies content from stage 1
55+
- Copies custom `nginx.conf`
56+
- Runs as non-root user (Cloud Run requirement)
57+
58+
### Nginx configuration
59+
60+
Simple configuration in `nginx.conf`:
61+
62+
- **Root**: `/usr/share/nginx/html` (contains the `developers/` folder)
63+
- **Location `/developers/`**: Uses `try_files $uri $uri/ $uri/index.html =404` for index fallback
64+
- **Location `= /developers`**: 301 redirect to `/developers/` for consistency
65+
- **absolute_redirect off**: Ensures redirects use relative paths (important for load balancer)
66+
67+
## Architecture
68+
69+
```
70+
GitHub Actions (on push to main)
71+
72+
1. Build Astro site
73+
2. Sync to GCS bucket
74+
3. Build container (fetches from GCS)
75+
4. Deploy to Cloud Run
76+
77+
Load Balancer (staging.interledger.org)
78+
79+
/developers → nginx-rewrite Cloud Run service
80+
81+
Serves baked-in static files
82+
```

ci/nginx-rewrite/nginx.conf

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
worker_processes auto;
2+
error_log /var/log/nginx/error.log warn;
3+
pid /var/run/nginx.pid;
4+
5+
events {
6+
worker_connections 1024;
7+
}
8+
9+
http {
10+
include /etc/nginx/mime.types;
11+
default_type application/octet-stream;
12+
13+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
14+
'$status $body_bytes_sent "$http_referer" '
15+
'"$http_user_agent" "$http_x_forwarded_for"';
16+
17+
access_log /var/log/nginx/access.log main;
18+
19+
sendfile on;
20+
tcp_nopush on;
21+
tcp_nodelay on;
22+
keepalive_timeout 65;
23+
types_hash_max_size 2048;
24+
gzip on;
25+
26+
# Use relative redirects
27+
absolute_redirect off;
28+
29+
server {
30+
listen 8080;
31+
server_name _;
32+
33+
# Root directory containing the developers folder
34+
root /usr/share/nginx/html;
35+
36+
# Health check endpoint for Cloud Run
37+
location /health {
38+
access_log off;
39+
return 200 "healthy\n";
40+
add_header Content-Type text/plain;
41+
}
42+
43+
# Serve files under /developers/
44+
location /developers/ {
45+
# This will:
46+
# /developers/ -> /developers/index.html
47+
# /developers/foo -> /developers/foo/index.html
48+
# /developers/foo/ -> /developers/foo/index.html
49+
# /developers/foo/bar/ -> /developers/foo/bar/index.html
50+
try_files $uri $uri/ $uri/index.html =404;
51+
}
52+
53+
# If someone hits /developers (no slash), redirect to /developers/
54+
location = /developers {
55+
return 301 /developers/;
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)