Skip to content

Commit 6872abe

Browse files
authored
Merge pull request #99 from refactor-group/82-feature-request-suggestion-create-fe-image-workflow
82 feature request suggestion create fe image workflow
2 parents 3b7d40d + ca9b4db commit 6872abe

File tree

5 files changed

+241
-28
lines changed

5 files changed

+241
-28
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Build and Push Frontend Image
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
types: [opened, synchronize, reopened]
9+
workflow_dispatch:
10+
11+
env:
12+
REGISTRY: ghcr.io
13+
14+
jobs:
15+
build_and_push_frontend:
16+
runs-on: ubuntu-22.04
17+
18+
permissions:
19+
contents: read
20+
packages: write
21+
id-token: write # Required for Cosign OIDC signing
22+
23+
steps:
24+
# Checkout the source code
25+
- uses: actions/checkout@v4
26+
27+
# Setup QEMU for emulating multi-arch (e.g., ARM64 on x86)
28+
- uses: docker/setup-qemu-action@v2
29+
with:
30+
platforms: linux/amd64,linux/arm64
31+
32+
# Setup Buildx for advanced Docker builds (multiarch, caching, sbom)
33+
- uses: docker/setup-buildx-action@v3
34+
with:
35+
install: true
36+
37+
# Login to GHCR (GitHub Container Registry)
38+
- name: Docker login
39+
uses: docker/login-action@v2
40+
with:
41+
registry: ${{ env.REGISTRY }}
42+
username: ${{ github.actor }}
43+
password: ${{ secrets.GITHUB_TOKEN }}
44+
45+
# Dynamically generate image tag and name based on repo/org/branch
46+
- name: Determine Image Tags
47+
id: tags
48+
run: |
49+
BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF##*/}}
50+
ORG_NAME="refactor-group"
51+
REPO_NAME="refactor-platform-fe"
52+
IMAGE="${{ env.REGISTRY }}/${ORG_NAME}/${REPO_NAME}/${BRANCH_NAME}:latest"
53+
echo "tag=$IMAGE" >> $GITHUB_OUTPUT
54+
echo "image=$IMAGE" >> $GITHUB_OUTPUT
55+
56+
# Build, SBOM, and Push the multi-arch Docker image
57+
- name: Build + Push Frontend
58+
uses: docker/build-push-action@v5
59+
with:
60+
context: .
61+
file: ./Dockerfile # Dockerfile is at the root of the repo
62+
target: runner # Your Dockerfile defines this stage
63+
platforms: linux/amd64,linux/arm64
64+
push: true
65+
provenance: true # Enables provenance metadata
66+
sbom: true # Enables SBOM generation
67+
build-args: |
68+
NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL=${{ secrets.BACKEND_SERVICE_PROTOCOL }}
69+
NEXT_PUBLIC_BACKEND_SERVICE_HOST=${{ secrets.BACKEND_SERVICE_HOST }}
70+
NEXT_PUBLIC_BACKEND_SERVICE_PORT=${{ secrets.BACKEND_PORT }}
71+
NEXT_PUBLIC_BACKEND_API_VERSION=${{ secrets.BACKEND_API_VERSION }}
72+
FRONTEND_SERVICE_PORT=${{ secrets.FRONTEND_SERVICE_PORT }}
73+
FRONTEND_SERVICE_INTERFACE=${{ secrets.FRONTEND_SERVICE_INTERFACE }}
74+
tags: ${{ steps.tags.outputs.tag }}
75+
cache-from: type=gha # GitHub-hosted build cache
76+
cache-to: type=gha,mode=max
77+
78+
# Install Cosign CLI for image signing
79+
- name: Install Cosign
80+
uses: sigstore/cosign-installer@v3
81+
82+
# Sign image using GitHub OIDC token (no secrets needed)
83+
- name: Sign image with Cosign
84+
env:
85+
COSIGN_EXPERIMENTAL: "true"
86+
run: |
87+
cosign sign --yes ${{ steps.tags.outputs.image }}
88+
89+
# Output usage instructions
90+
- name: Print Pull & Run Instructions
91+
run: |
92+
echo -e "\033[1;32mFrontend Image Pushed & Signed:\033[0m"
93+
echo " docker pull ${{ steps.tags.outputs.image }}"
94+
echo ""
95+
echo -e "\033[1;36mRun locally or with Compose:\033[0m"
96+
echo " docker run --rm --env-file .env -p 3000:3000 ${{ steps.tags.outputs.image }}"
97+
echo ""
98+
echo -e "\033[1;33mSignature Verification:\033[0m"
99+
echo " cosign verify ${{ steps.tags.outputs.image }}"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,7 @@ yarn-error.log*
3434
# typescript
3535
*.tsbuildinfo
3636
next-env.d.ts
37+
38+
# vscode
39+
.vscode/
40+

Dockerfile

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,56 @@
1-
FROM node:18-alpine AS base
1+
# Stage 0: Base image
2+
FROM node:22-alpine3.19 AS base
23

3-
# 1. Install dependencies only when needed
4+
# BuildKit Platform Context (used for metadata, not to alter FROM)
5+
ARG BUILDPLATFORM
6+
ARG TARGETPLATFORM
7+
8+
# Optional diagnostics (doesn't affect final image)
9+
RUN echo "Build Platform: ${BUILDPLATFORM} -> Target Platform: ${TARGETPLATFORM}"
10+
11+
# Stage 1: Dependencies
412
FROM base AS deps
5-
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
6-
RUN apk add --no-cache libc6-compat
13+
RUN apk update && apk upgrade --no-cache && apk add --no-cache libc6-compat
714

8-
# Set the working directory
915
WORKDIR /app
10-
11-
# Copy package.json and package-lock.json
1216
COPY package.json ./
1317
COPY package-lock.json ./
14-
1518
RUN npm install
1619

17-
# 2. Rebuild the source code only when needed
20+
# Stage 2: Builder
1821
FROM base AS builder
1922
WORKDIR /app
2023
COPY --from=deps /app/node_modules ./node_modules
2124
COPY . .
2225

23-
# Receive the build args from docker-compose.yaml
26+
# Build-time args from docker-compose or GitHub Actions
2427
ARG NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL
2528
ARG NEXT_PUBLIC_BACKEND_SERVICE_HOST
2629
ARG NEXT_PUBLIC_BACKEND_SERVICE_PORT
2730
ARG NEXT_PUBLIC_BACKEND_API_VERSION
2831
ARG FRONTEND_SERVICE_INTERFACE
2932
ARG FRONTEND_SERVICE_PORT
3033

31-
# And convert them to environment variables for runtime
34+
# Pass them as ENV so Next.js static build can access
3235
ENV NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL=$NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL
3336
ENV NEXT_PUBLIC_BACKEND_SERVICE_HOST=$NEXT_PUBLIC_BACKEND_SERVICE_HOST
3437
ENV NEXT_PUBLIC_BACKEND_SERVICE_PORT=$NEXT_PUBLIC_BACKEND_SERVICE_PORT
3538
ENV NEXT_PUBLIC_BACKEND_API_VERSION=$NEXT_PUBLIC_BACKEND_API_VERSION
3639

37-
RUN echo "NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL: ${NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL}"
38-
RUN echo "NEXT_PUBLIC_BACKEND_SERVICE_HOST: ${NEXT_PUBLIC_BACKEND_SERVICE_HOST}"
39-
RUN echo "NEXT_PUBLIC_BACKEND_SERVICE_HOST: ${NEXT_PUBLIC_BACKEND_SERVICE_PORT}"
40-
RUN echo "NEXT_PUBLIC_BACKEND_SERVICE_HOST: ${NEXT_PUBLIC_BACKEND_API_VERSION}"
41-
RUN echo "FRONTEND_SERVICE_INTERFACE: ${FRONTEND_SERVICE_INTERFACE}}"
42-
RUN echo "FRONTEND_SERVICE_PORT: ${FRONTEND_SERVICE_PORT}}"
43-
40+
# Optional: Print the values to verify they are set correctly
41+
RUN echo "Building with the following environment variables:" && \
42+
echo "NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL: ${NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL}" && \
43+
echo "NEXT_PUBLIC_BACKEND_SERVICE_HOST: ${NEXT_PUBLIC_BACKEND_SERVICE_HOST}" && \
44+
echo "NEXT_PUBLIC_BACKEND_SERVICE_PORT: ${NEXT_PUBLIC_BACKEND_SERVICE_PORT}" && \
45+
echo "NEXT_PUBLIC_BACKEND_API_VERSION: ${NEXT_PUBLIC_BACKEND_API_VERSION}" && \
46+
echo "FRONTEND_SERVICE_INTERFACE: ${FRONTEND_SERVICE_INTERFACE}" && \
47+
echo "FRONTEND_SERVICE_PORT: ${FRONTEND_SERVICE_PORT}"
48+
49+
# Build the Next.js application
50+
# Note: Use `next build` to build the application for production
4451
RUN npm run build
4552

46-
# 3. Production image, copy all the files and run next
53+
# Stage 3: Runtime Image
4754
FROM base AS runner
4855
WORKDIR /app
4956

@@ -53,21 +60,24 @@ RUN addgroup -g 1001 -S nodejs
5360
RUN adduser -S nextjs -u 1001
5461

5562
COPY --from=builder /app/public ./public
56-
57-
# Automatically leverage output traces to reduce image size
58-
# https://nextjs.org/docs/advanced-features/output-file-tracing
5963
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
6064
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
61-
6265
COPY --from=builder /app/package.json ./
6366

6467
USER nextjs
6568

66-
# Expose the port Next.js runs on
67-
EXPOSE $FRONTEND_SERVICE_PORT
69+
# Expose the port
70+
EXPOSE 3000
6871

72+
ARG FRONTEND_SERVICE_INTERFACE
73+
ARG FRONTEND_SERVICE_PORT
74+
75+
# Runtime ENV for Compose
6976
ENV HOSTNAME=$FRONTEND_SERVICE_INTERFACE
7077
ENV PORT=$FRONTEND_SERVICE_PORT
7178

72-
# Run the app using JSON array notation
73-
CMD ["node", "server.js"]
79+
# executable that will run the application
80+
ENTRYPOINT ["node"]
81+
82+
# default args to the ENTRYPOINT that can be overridden at runtime
83+
CMD ["server.js"]

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Refactor Coaching & Mentoring Platform
2-
### Frontend (currently web browser-only)
2+
3+
## Frontend (currently web browser-only)
34

45
![377960688-0b5292b0-6ec7-4774-984e-8e99e503d26c](https://github.com/user-attachments/assets/5dcdee09-802e-4b25-aa58-757d607ce7bc)
56
A preview of the main coaching session page (rapidly evolving)
@@ -43,3 +44,4 @@ npm run dev
4344

4445
Open [http://localhost:3000](http://localhost:3000) with your browser to log in to the platform.
4546

47+
#### For Working with and Running the Application in Docker, navigate to the [Container-README](./docs/runbooks/Container-README.md)

docs/runbooks/Container-README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Refactor Platform Frontend Docker Image Management
2+
3+
## Managing the Project with Docker & GitHub Actions (CI/CD)
4+
5+
This project builds and pushes a multi-arch Docker image to [GHCR](https://github.com/orgs/refactor-group/packages) and triggers a build workflow on GitHub automatically on pushes to branches with open pull requests.
6+
7+
**Key Steps:**
8+
9+
1. **Develop Your Changes:**
10+
- Work on your feature or fix in your branch.
11+
2. **Open a Pull Request:**
12+
- When you open a PR, GitHub Actions triggers the build workflow.
13+
3. **Automated Build & Push:**
14+
- The workflow builds a multi-arch Docker image and pushes it to GHCR.
15+
- Verify the build status in GitHub Actions.
16+
- The images are named as follows: `ghcr.io/refactor-group/refactor-platform-fe/<branch>:latest`
17+
4. **Local Testing (Optional):**
18+
- For local Docker testing, note that the Docker Compose file and environment variables are maintained in the backend repository. Make sure you have access to that repo for configuration details.
19+
20+
---
21+
22+
## Manual Docker Image Management
23+
24+
### If you plan on working with the Docker Image Locally as well the following section acts as a quickstart guide for managing the Docker images manually
25+
26+
#### Prerequisites
27+
28+
- Before running any Docker commands, ensure you are logged into GHCR using your GitHub personal access token (PAT):
29+
30+
```bash
31+
docker login ghcr.io -u <your_github_username> -p <your_PAT>
32+
```
33+
34+
- Ensure you have Containerd installed and running
35+
- Ensure you have Docker Buildx installed and configured
36+
- Ensure you are using the builder instance you create using steps 1-4 below
37+
- Ensure you have Docker installed and running
38+
**Image Naming Convention:**
39+
The Docker images follow the naming convention:
40+
`ghcr.io/refactor-group/refactor-platform-fe/<branch>:latest`
41+
42+
Where:
43+
44+
- `refactor-group` is your GitHub organization.
45+
- `refactor-platform-fe` is the repository name.
46+
- `<branch>` is the name of the branch, with underscores converted to dashes.
47+
- `latest` is the tag.
48+
49+
**Useful Commands:**
50+
51+
```bash
52+
# Docker Buildx: Enhanced Image Management
53+
54+
# 1. Inspect Docker Buildx
55+
docker buildx version # Verify Docker Buildx is installed
56+
57+
# 2. Create a new builder instance (if needed)
58+
docker buildx create --name mybuilder --driver docker-container # Creates a builder instance named 'mybuilder' using the docker-container driver
59+
60+
# 3. Use the builder
61+
docker buildx use mybuilder # Sets 'mybuilder' as the current builder
62+
63+
# 4. Inspect the builder
64+
docker buildx inspect --bootstrap # Displays details and ensures the builder is running
65+
66+
# 5. Login to GHCR
67+
docker login ghcr.io -u <your_github_username> -p <your_PAT> # Authenticates with GitHub Container Registry using your username and PAT
68+
69+
# 6. Build the image for multiple architectures and push to registry
70+
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/refactor-group/refactor-platform-fe:<branch> --push . # Builds for specified architectures and pushes to GHCR
71+
72+
# 7. Build the image for multiple architectures and load to local docker daemon
73+
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/refactor-group/refactor-platform-fe:<branch> --load . # Builds for specified architectures and loads to local docker daemon
74+
75+
# 8. Tag the image
76+
docker tag ghcr.io/refactor-group/refactor-platform-fe:<branch> ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Creates an additional tag 'latest' for the image
77+
78+
# 9. Push the image
79+
docker push ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Uploads the image to the container registry
80+
81+
# 10. Pull the image
82+
docker pull ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Downloads the image from the container registry
83+
84+
# 11. Run the image
85+
docker run -p 3000:3000 ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Starts a container from the image, mapping port 3000
86+
87+
# 12. Inspect the image
88+
docker inspect ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Shows detailed information about the image
89+
90+
# 13. Remove the image
91+
docker rmi ghcr.io/refactor-group/refactor-platform-fe:<branch>:latest # Deletes the image from the local machine
92+
```
93+
94+
### Important Notes
95+
96+
- Always use the `latest` tag for the most recent version (it defaults to `latest`)
97+
- Ensure your branch is up to date with the main branch before opening a PR
98+
- For backend-specific configuration (Docker Compose & env vars), refer to the backend repository documentation

0 commit comments

Comments
 (0)