Skip to content

Containerize App - #16

Open
Wonno wants to merge 3 commits into
cityssm:mainfrom
Wonno:feature/containerize
Open

Wonno wants to merge 3 commits into
cityssm:mainfrom
Wonno:feature/containerize

Conversation

@Wonno

@Wonno Wonno commented Jul 24, 2025

Copy link
Copy Markdown
Contributor

Put the app into a container as a first draft.

@cityssm-bot

cityssm-bot commented Jul 24, 2025

Copy link
Copy Markdown
Member

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@sonarqubecloud

Copy link
Copy Markdown

@deepsource-io

deepsource-io Bot commented Jul 24, 2025

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in b19242c...9cbb965 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Mar 20, 2026 10:26a.m. Review ↗

@dangowans

Copy link
Copy Markdown
Member

Thanks for the pull request @Wonno !

I am not really familiar at all with Docker, but I don't see an issue with anything here. Looks like it's just configuration files.

However, I would hate to break something on you by accident, so I'm wondering if we can put together a simple test in a GitHub Action.

Essentially,

  • Install the app.
  • Start the app inside the Docker container.
  • Run one of the Cypress tests on it.
    ex. npx cypress run --config-file cypress.config.js --spec "cypress/e2e/03-readOnly/readOnlyUser.cy.js"
  • Shutdown the app.

Do you know the Docker commands that would be included in such a test?

@dangowans dangowans added the enhancement New feature or request label Jul 25, 2025
@dangowans dangowans added the help wanted Extra attention is needed label Aug 11, 2025
@sonarqubecloud

Copy link
Copy Markdown

@dangowans
dangowans requested a review from Copilot October 16, 2025 17:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

First draft to containerize Sunrise CMS with a Dockerfile, Docker linting config, and a Docker build context filter.

  • Add a Dockerfile to build and run the app in a container with a healthcheck and declared volumes
  • Add hadolint configuration
  • Add a .dockerignore to reduce the Docker build context

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
Dockerfile New container build and runtime definition, healthcheck, ports, and volumes.
.hadolint.yaml Adds configuration for Dockerfile linting.
.dockerignore Excludes dev and tooling files from the Docker build context.

Comment thread Dockerfile
EXPOSE 9000/tcp

HEALTHCHECK --interval=1m --timeout=3s --retries=3 \
CMD curl -f http://localhost:9000 || exit 1

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

The healthcheck uses curl, which is not installed in the base node image by default; the healthcheck will fail with exit code 127. Either install curl in the image (e.g., RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/*) or replace the healthcheck with a Node-based check, for example: HEALTHCHECK CMD node -e "require('http').get('http://localhost:9000', r => { process.exit(r.statusCode >= 400 ? 1 : 0) }).on('error', () => process.exit(1))".

Suggested change
CMD curl -f http://localhost:9000 || exit 1
CMD node -e "require('http').get('http://localhost:9000', r => { process.exit(r.statusCode >= 400 ? 1 : 0) }).on('error', () => process.exit(1))"

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
RUN npm ci && npm cache clean --force

COPY ./ .

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

The container runs as root; switch to a non-root user to reduce risk. After copying files and installing dependencies, create required directories, chown them to the node user, and add USER node, e.g., RUN mkdir -p /app/data/backups /app/data/sessions /app/data/database /app/public-internal && chown -R node:node /app followed by USER node.

Suggested change
# Create required directories and set ownership to node user
RUN mkdir -p /app/data/backups /app/data/sessions /app/data/database /app/public-internal && chown -R node:node /app
USER node

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
Comment on lines +1 to +16
FROM node:24 AS base

# Usage example:
# docker build --no-cache --tag sunrise-cms:local .
# docker run --rm --detach --name sunrise-cms --publish 9000:9000 --mount type=bind,src=%CD%/data/testing.config.js,dst=/app/data/config.js sunrise-cms:local
# docker rm sunrise-cms --force

WORKDIR /app
COPY package*.json ./

# for production builds - but since we have dev dependencies, we need to use npm ci without --omit=dev
#RUN npm ci --omit=dev && npm cache clean --force
RUN npm ci && npm cache clean --force

COPY ./ .

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

Installing devDependencies in the runtime image significantly increases image size and attack surface. Use a multi-stage build (builder installs devDependencies and compiles; final stage runs npm ci --omit=dev and copies only the compiled output and needed assets) so the final image contains only production dependencies and runtime files.

Suggested change
FROM node:24 AS base
# Usage example:
# docker build --no-cache --tag sunrise-cms:local .
# docker run --rm --detach --name sunrise-cms --publish 9000:9000 --mount type=bind,src=%CD%/data/testing.config.js,dst=/app/data/config.js sunrise-cms:local
# docker rm sunrise-cms --force
WORKDIR /app
COPY package*.json ./
# for production builds - but since we have dev dependencies, we need to use npm ci without --omit=dev
#RUN npm ci --omit=dev && npm cache clean --force
RUN npm ci && npm cache clean --force
COPY ./ .
# ----------- Builder stage -----------
FROM node:24 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci && npm cache clean --force
COPY ./ .
# If you have a build step (e.g., TypeScript, Webpack), add it here:
# RUN npm run build
# ----------- Production stage -----------
FROM node:24 AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
# Copy only the built output and needed assets from builder
# Adjust the following COPY commands to match your build output and assets
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
COPY --from=builder /app/data ./data
# If you need other files (e.g., config, views), copy them as needed:
# COPY --from=builder /app/views ./views
# COPY --from=builder /app/config.js ./config.js

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
VOLUME /app/data/database
VOLUME /app/public-internal

ENTRYPOINT ["npm" ,"start"]

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Running the app via npm as PID 1 can interfere with signal handling and graceful shutdown, and ENTRYPOINT makes overriding at runtime harder. Prefer CMD and execute Node directly (or use tini), for example: CMD ["node", "bin/www.js"] (adjust path to your actual start script) or add --init when running the container.

Suggested change
ENTRYPOINT ["npm" ,"start"]
CMD ["node", "bin/www.js"]

Copilot uses AI. Check for mistakes.
Comment thread .dockerignore
Dockerfile
*.md
**/*.env
eslint.config.ts

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider excluding runtime data and SQLite artifacts to keep the build context small and avoid baking data into images. Add ignores like data/**/*.db, data/backups/, data/sessions/, and data/config.js (if you plan to bind-mount configuration at runtime).

Suggested change
eslint.config.ts
eslint.config.ts
# Exclude runtime data and SQLite artifacts
data/**/*.db
data/backups/
data/sessions/
data/config.js

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
@@ -0,0 +1,27 @@
FROM node:24 AS base

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

The hadolint config enables strict-labels, but the Dockerfile defines no OCI labels. Add standard labels for metadata and supply chain (e.g., LABEL org.opencontainers.image.title="Sunrise CMS" org.opencontainers.image.source="https://github.com/cityssm/sunrise-cms\" org.opencontainers.image.licenses="MIT" org.opencontainers.image.version="$VERSION").

Suggested change
FROM node:24 AS base
FROM node:24 AS base
LABEL org.opencontainers.image.title="Sunrise CMS" \
org.opencontainers.image.source="https://github.com/cityssm/sunrise-cms" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.version="${VERSION:-local}"

Copilot uses AI. Check for mistakes.
Comment thread .hadolint.yaml
# Haskell Dockerfile Linter 2.12.0
# docker run --rm -i -v %CD%/.hadolint.yaml:/.config/hadolint.yaml hadolint/hadolint < Dockerfile

failure-threshold: none

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] With failure-threshold set to none, hadolint will never fail the build, which reduces its usefulness in CI. Consider setting failure-threshold to warning or error so meaningful issues cause CI to fail.

Suggested change
failure-threshold: none
failure-threshold: warning

Copilot uses AI. Check for mistakes.
Comment thread .hadolint.yaml
format: tty
ignored: [ ]
no-color: false
no-fail: false

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] With failure-threshold set to none, hadolint will never fail the build, which reduces its usefulness in CI. Consider setting failure-threshold to warning or error so meaningful issues cause CI to fail.

Copilot uses AI. Check for mistakes.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request help wanted Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants