Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a570523
feat: add Docker Compose setup for local development
Eng-Omar-Hussein Mar 30, 2026
93e051b
Modify command for stories_webapp service
Eng-Omar-Hussein Mar 30, 2026
e453497
Refactor docker-compose.yml structure
Eng-Omar-Hussein Mar 30, 2026
8725cd2
Modify docker-compose.yml for workspace variable usage
Eng-Omar-Hussein Mar 30, 2026
9661f36
refactor: streamline Jenkinsfile stages and improve deployment logic
Eng-Omar-Hussein Apr 4, 2026
39b0ee9
Refactor Jenkinsfile
Eng-Omar-Hussein Apr 4, 2026
eb3190c
Apply suggestions from code review
Eng-Omar-Hussein Apr 7, 2026
651f976
refactor: update environment variable handling in Jenkinsfile and doc…
Eng-Omar-Hussein Apr 7, 2026
9720cbb
docs: update Docker Compose usage instructions for user ID requirement
Eng-Omar-Hussein Apr 7, 2026
eaa6216
Merge branch 'jenkins-infra:main' into compose
Eng-Omar-Hussein Apr 7, 2026
96523d5
Merge branch 'compose' of github.com:Eng-Omar-Hussein/stories into co…
Eng-Omar-Hussein Apr 7, 2026
5eba4ae
Merge branch 'compose' of github.com:Eng-Omar-Hussein/stories into co…
Eng-Omar-Hussein Apr 7, 2026
d44b338
refactor: streamline Jenkinsfile and enhance Docker Compose setup for…
Eng-Omar-Hussein Apr 11, 2026
1ab18cd
refactor: simplify directory creation and permissions in Docker Compo…
Eng-Omar-Hussein Apr 12, 2026
f4672ae
refactor: update working directory in .env and optimize Docker Compos…
Eng-Omar-Hussein Apr 13, 2026
7745586
Merge branch 'main' into compose
Eng-Omar-Hussein May 13, 2026
7b4bc81
fix: update jenkins agent image version
Eng-Omar-Hussein May 13, 2026
7993560
Merge branch 'main' into compose
krisstern Jun 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
WORKSPACE_ = "/home/jenkins/agent/workspace"
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
NODE_ENV = 'production'
TZ = "UTC"
# // Amount of available vCPUs, to avoid OOM - https://www.gatsbyjs.com/docs/how-to/performance/resolving-out-of-memory-issues/#try-reducing-the-number-of-cores
# // https://github.com/jenkins-infra/jenkins-infra/tree/production/hieradata/clients/controller.ci.jenkins.io.yaml#L327
GATSBY_CPU_COUNT = "4"
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
# // Added the below to fix permissions issue with the cache
GATSBY_CACHE_DIR = "${WORKSPACE_}/.gatsby-cache"
GATSBY_INTERNAL_CACHE_DIR = "${WORKSPACE_}/.cache"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break the CI. Is it needed with Docker Compose?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not needed in Docker. I initially thought it was important to fix permissions in the CI, so I kept it in Docker. However, after testing, I found a problem that may occur if the user runs it on the host OS using the default user with UID=1000. They will face an error related to cache permissions, because the Jenkins user used in Docker Compose has UID=1001. As a result, the bind-mounted data will have 1000 ownership, and Jenkins will not have permission to write to these files.

Therefore, I should add this to the documentation. For example:

Before running docker compose up, make sure that the user you are using has UID=1001. If not, use the command below.

// for linux and wsl 
sudo chown -R 1001:1001 .

or create and use a user with UID=1001

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not a safe neither a portable solution:

  • UID is different on macOS with Docker Desktop
  • UID is different on Linux with Docker CE

Since there are no reason to share the cache dir with the host, WDYT about setting the env. var in Docker Compose only, to a container-internal directory.

@Eng-Omar-Hussein Eng-Omar-Hussein Apr 10, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that Gatsby is ignoring GATSBY_CACHE_DIR or GATSBY_INTERNAL_CACHE_DIR and still trying to create .cache inside the workspace directory (APP_WORKING_DIR), where the jenkins (UID=1001) user has no write permission because the workspace directory need root ownership to have write access in APP_WORKING_DIR
image

I recently reach to solution:

  • add another service with root user to fix permission for .cash and public e.g
services:
  fix-permissions:
    image: busybox
    user: root
    working_dir: /app
    volumes:
      - .:/app
    command: >
      sh -c "
        if ! [ -d /app/public ]; then
          mkdir -p /app/public
        fi
        if ! [ -d /app/.cache ]; then
          mkdir -p /app/.cache
        fi
        if ! id -u jenkins > /dev/null 2>&1; then
          adduser -u 1001 -D jenkins
        fi
        chown -R jenkins:jenkins /app/public /app/.cache
      "

@Eng-Omar-Hussein Eng-Omar-Hussein Apr 11, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently reach to solution:

  • add another service with root user to fix permission for .cash and public e.g
services:
  fix-permissions:
    image: busybox
    user: root
    working_dir: /app
    volumes:
      - .:/app
    command: >
      sh -c "
        if ! [ -d /app/public ]; then
          mkdir -p /app/public
        fi
        if ! [ -d /app/.cache ]; then
          mkdir -p /app/.cache
        fi
        if ! id -u jenkins > /dev/null 2>&1; then
          adduser -u 1001 -D jenkins
        fi
        chown -R jenkins:jenkins /app/public /app/.cache
      "

The new version of this recommended solution replace chown with chmod to avoid create user on host with UID 1001

  fix-permissions:
    image: busybox
    user: root
    working_dir: /app
    volumes:
      - .:/app

    command: >
      sh -c "
        dirs='public .cache node_modules'
        for d in $$dirs; do
          mkdir -p $$d
          chmod -R 777 $$d
        done
        chmod 666 package-lock.json
      "

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the behavior if you keep sharing the current dir to /app, the 2 nested bind mount from volumes but remove the src mount?

Removing the ./src mount leads to a static build, so the container needs to be rebuilt again to show the effect of the code change. This remove unable the advantage of hot reload feature.

I am pleased to share with you a demonstration to show the behavior if src is removed: https://1drv.ms/v/c/6458a979282b4a55/IQCZ85M2QP7BSYuzT17LDGXyAdJfNMGfmK7U4C4ef9mfCq8?e=rTlINr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. Any reason to mount ./ to $APP_WORKING_DIR/workspace instead of `$APP_WORKING_DIR then? It would solve your issue.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly, Mounting ./ to a subdirectory instead off APP_WORKING_DIR will solve the issue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, then let's roll with mounting ./ to APP_WORKING_DIR and specify the GASTBY_* environment variables to another directories inside the container filesystem

@Eng-Omar-Hussein Eng-Omar-Hussein Apr 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, then let's roll with mounting ./ to APP_WORKING_DIR and specify the GASTBY_* environment variables to another directories inside the container filesystem

Done, this video shows that the GASTBY_* environment variables are ignored and .cache is still used in APP_WORKING_DIR

Video.Project.3.mp4

GATSBY_TELEMETRY_DISABLED = "1"
NODE_OPTIONS = "--no-warnings"
141 changes: 83 additions & 58 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,101 @@ pipeline {
disableConcurrentBuilds(abortPrevious: true)
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '5')
}

agent {
label 'linux-arm64-docker || arm64linux'
}

environment {
NODE_ENV = 'production'
TZ = "UTC"
// Amount of available vCPUs, to avoid OOM - https://www.gatsbyjs.com/docs/how-to/performance/resolving-out-of-memory-issues/#try-reducing-the-number-of-cores
// https://github.com/jenkins-infra/jenkins-infra/tree/production/hieradata/clients/controller.ci.jenkins.io.yaml#L327
GATSBY_CPU_COUNT = "4"
// Added the below to fix permissions issue with the cache
GATSBY_CACHE_DIR = "${env.WORKSPACE}/.gatsby-cache"
GATSBY_INTERNAL_CACHE_DIR = "${env.WORKSPACE}/.cache"
GATSBY_TELEMETRY_DISABLED = "1"
NODE_OPTIONS = "--no-warnings"
}


stages {
stage('Check for typos') {
stage('load env file') {
steps {
// Load environment variables from .env file.
sh '''
curl -qsL https://github.com/crate-ci/typos/releases/download/v1.33.1/typos-v1.33.1-x86_64-unknown-linux-musl.tar.gz | tar xvzf - ./typos
./typos --format sarif > typos.sarif || true
set -a
source ./.env
set +a
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
'''
}
post {
always {
recordIssues(tools: [sarif(id: 'typos', name: 'Typos', pattern: 'typos.sarif')])
}
}
}

stage('Install Dependencies') {
environment {
NODE_ENV = 'development'
}
steps {
sh 'asdf install'
sh 'npm install'
}
}

stage('Lint and Test') {
environment {
NODE_ENV = "development"
}
steps {
sh 'npm run lint && npx eslint --format checkstyle > eslint.xml'
}
post {
always {
recordIssues(tools: [checkStyle(pattern: 'eslint.xml')])

stage('Parallel Stage') {
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
parallel {
stage('Branch A') {
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
stages {
stage('Check for typos') {
steps {
sh '''
curl - qsL https: //github.com/crate-ci/typos/releases/download/v1.33.1/typos-v1.33.1-x86_64-unknown-linux-musl.tar.gz | tar xvzf - ./typos
. / typos--format sarif > typos.sarif || true
'''
}
post {
always {
recordIssues(tools: [sarif(id: 'typos', name: 'Typos', pattern: 'typos.sarif')])
}
}
}

stage('Install Dependencies') {
environment {
NODE_ENV = 'development'
}
steps {
sh 'asdf install'
sh 'npm install'
}
}

stage('Lint and Test') {
environment {
NODE_ENV = "development"
}
steps {
sh 'npm run lint && npx eslint --format checkstyle > eslint.xml'
}
post {
always {
recordIssues(tools: [checkStyle(pattern: 'eslint.xml')])
}
}
}

stage('Build PR') {
when {
changeRequest()
}
environment {
NODE_ENV = 'development'
}
steps {
sh 'npm run build'
}
}
}
}

stage('Branch B') {
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
agent {
label 'linux-arm64-docker || arm64linux'
}
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
steps {
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
echo 'Test Docker Compose'
sh 'docker compose up --detach --wait'
sh 'docker compose run --rm stories_webapp env'
sh 'docker compose down'
}
post {
always {
sh 'docker compose down || true'
}
}
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated
}
}
}

stage('Build PR') {
when { changeRequest() }
environment {
NODE_ENV = 'development'
}
steps {
sh 'npm run build'
}
}


stage('Deploy PR to preview site') {
when {
allOf{
allOf {
changeRequest target: 'main'
// Only deploy to production from infra.ci.jenkins.io
expression { infra.isInfra() }
Expand All @@ -95,7 +120,7 @@ pipeline {
}
}
}
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.

stage('Build Production') {
when {
branch "main"
Expand All @@ -104,7 +129,7 @@ pipeline {
sh 'npm run build'
}
}

stage('Deploy Production') {
when {
allOf {
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ npm run develop

Open [http://localhost:8000](http://localhost:8000) on your browser to see the result

## Alternative Development Setup: Docker Compose

You can also use Docker Compose for local development.

### How to Use

1. Ensure you have [Docker](https://www.docker.com/products/docker-desktop/) and [Docker Compose](https://docs.docker.com/compose/) installed.
2. In the project **root**, run:
```bash
docker compose up
```
3. Open [http://localhost:8000](http://localhost:8000) in your browser.

## Code Quality Tools

### Formatting
Expand Down
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
services:
stories_webapp:
# TODO: track version with updatecli - ref. https://github.com/jenkins-infra/jenkins-infra/blob/efe90908529525bb3c9e61c2eb920ada3b968f1a/updatecli/updatecli.d/jenkinscontroller-tools-maven.yaml#L17-L22
image: jenkinsciinfra/jenkins-agent-ubuntu-22.04:2.91.1

ports:
- "8000:8000"

# Load environment variables
env_file:
- .env
# Add docker-compose-specific env vars
environment:
CHOKIDAR_USEPOLLING: "true"
PATH: /home/jenkins/.asdf/shims:/home/jenkins/.asdf/bin:$PATH
NODE_ENV: development

volumes:
- .:${WORKSPACE_}

working_dir: ${WORKSPACE_}

user: jenkins

entrypoint: []
command: bash -c "asdf install && asdf current && npm install && npm run develop"

networks:
- stories_network

networks:
stories_network:
driver: bridge
Comment thread
Eng-Omar-Hussein marked this conversation as resolved.
Outdated