Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# VCS / CI
.git
.github
.husky

# Dependencies & build output (rebuilt inside the image)
node_modules
**/node_modules
dist
dist-*

# Local runtime state & secrets (mount these instead)
.env
config/local.json
config/areas.json
server/src/configs/**
!server/src/configs/.gitkeep
server/.cache/**
!server/.cache/.gitkeep
logs/**
!logs/.gitkeep
.eslintcache

# Docs & editor files
docs
*.md
.idea
.vscode
.DS_Store
docker-compose*.yml
Dockerfile
.dockerignore
30 changes: 22 additions & 8 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,44 @@ env:
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4.1.1
- name: Set .gitsha
run: 'echo ${{ github.sha }} > .gitsha'
- name: Set .gitref
run: 'echo ${{ github.ref }} > .gitref'
- uses: actions/checkout@v7

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to the Container registry
uses: docker/login-action@v2
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v7
env:
# keep the log readable: no provenance trace in the printed metadata,
# and no .dockerbuild record artifact on every run (summary stays)
BUILDX_METADATA_PROVENANCE: disabled
DOCKER_BUILD_RECORD_UPLOAD: false
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GIT_SHA=${{ github.sha }}
GIT_REF=${{ github.ref }}
# keep publishing a single image manifest like before (no attestation index)
provenance: false
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ packages/masterfile/lib/data/masterfile.json

# Docker file
/docker-compose.yml
.gitref
.gitsha

# Favicon
public/favicon/*
Expand Down
Empty file removed .gitref
Empty file.
Empty file removed .gitsha
Empty file.
50 changes: 39 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
## Simple Dockerfile to build ReactMap (main branch)
# - Inside the container, the content of this git repo lives in /home/node/
# syntax=docker/dockerfile:1
## ReactMap Docker image
# - Inside the container, the app lives in /home/node/
# - `yarn start` builds the client with your config, then starts the server.
## You have to mount your configs into the container:
# - mount local.json to /home/node/server/src/configs/local.json
# - mount areas.json to /home/node/server/src/configs/areas.json
# - Also mount every other configuration file necessary into the according directory.

FROM node:22.23.4-alpine

ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
ENV PATH=$PATH:/home/node/.npm-global/bin
ARG NODE_IMAGE=node:22.23.4-alpine

# ---- build: full install + production build (verifies the bundle compiles) ----
FROM ${NODE_IMAGE} AS build
WORKDIR /home/node
COPY package.json .
COPY yarn.lock .
RUN apk add git
RUN npm install -g yarn
# git is only needed to fetch git-URL dependencies during install
RUN apk add --no-cache git
ENV HUSKY=0

# Workspace manifests, packages and default config are needed by `postinstall`
COPY package.json yarn.lock ./
COPY packages ./packages
COPY config ./config
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install --frozen-lockfile --network-timeout 600000

COPY . .
RUN yarn install
RUN yarn build

# ---- prune: drop devDependencies in place (native builds + masterfile are kept) ----
FROM build AS prune
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install --production --frozen-lockfile --ignore-scripts --network-timeout 600000

# ---- runtime ----
FROM ${NODE_IMAGE}
# Kept for parity with previous images (global npm installs in derived images)
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
ENV PATH=$PATH:/home/node/.npm-global/bin
WORKDIR /home/node
COPY --from=prune /home/node /home/node

ARG GIT_SHA=""
ARG GIT_REF=""
ENV GIT_SHA=${GIT_SHA} \
GIT_REF=${GIT_REF}

EXPOSE 8080
CMD ["yarn", "start"]
11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,18 @@
"@rm/locales": "*",
"@rm/logger": "*",
"@rm/masterfile": "*",
"@rm/vite-plugins": "*",
"@sentry/node": "^7.65.0",
"@sentry/react": "^8.55.2",
"@sentry/vite-plugin": "4.6.2",
"@turf/boolean-contains": "7.3.5",
"@turf/boolean-overlap": "7.3.5",
"@turf/boolean-point-in-polygon": "7.3.5",
"@turf/center": "7.3.5",
"@turf/destination": "7.3.5",
"@turf/distance": "7.3.5",
"@turf/helpers": "7.3.5",
"@vitejs/plugin-react-swc": "^3.8.1",
"bcrypt": "^5.0.1",
"body-parser": "2.4.0",
"bytes": "3.1.2",
Expand Down Expand Up @@ -175,28 +178,27 @@
"react-leaflet": "5.0.0",
"react-router": "^8.3.0",
"react-virtuoso": "4.18.7",
"rollup-plugin-delete": "^2.0.0",
"rtree": "^1.4.2",
"source-map": "^0.7.4",
"suncalc": "^1.9.0",
"supercluster": "^8.0.1",
"uicons.js": "2.3.0",
"vite": "^6.4.3",
"zustand": "4.5.7"
},
"devDependencies": {
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^19.2.2",
"@rm/types": "*",
"@rm/vite-plugins": "*",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@sentry/vite-plugin": "4.6.2",
"@types/dlv": "^1.1.2",
"@types/leaflet": "^1.9.21",
"@types/node": "^22.20.1",
"@types/node-fetch": "2.6.13",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react-swc": "^3.8.1",
"commitizen": "^4.3.0",
"cz-conventional-commit": "^1.0.6",
"eslint": "^8.44.0",
Expand All @@ -209,13 +211,10 @@
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.1",
"lint-staged": "^15.2.9",
"monaco-editor": "^0.41.0",
"nodemon": "3.1.14",
"prettier": "3.3.3",
"rollup-plugin-delete": "^2.0.0",
"semantic-release": "^22",
"typescript": "5.5.4",
"vite": "^6.4.3",
"vite-plugin-checker": "0.7.2"
},
"resolutions": {
Expand Down
1 change: 1 addition & 0 deletions packages/locales/lib/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// @ts-check
/* eslint-disable no-restricted-syntax */
/* eslint-disable guard-for-in */
/* eslint-disable import/no-extraneous-dependencies */

require('dotenv').config()
const { OpenAI } = require('openai')
Expand Down
5 changes: 3 additions & 2 deletions packages/locales/lib/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// @ts-check
// Consumers such as vite.config.js rely on `.env` being loaded here
// (e.g. NODE_CONFIG_ENV selects the dist-* output dir), so keep this
require('dotenv').config()

const { create } = require('./create')
const { missing } = require('./missing')
const { generate } = require('./generate')
const {
readLocaleDirectory,
writeAll,
Expand All @@ -17,6 +19,5 @@ module.exports.locales = locales
module.exports.status = status
module.exports.create = create
module.exports.missing = missing
module.exports.generate = generate
module.exports.writeAll = writeAll
module.exports.readAndParseJson = readAndParseJson
8 changes: 4 additions & 4 deletions packages/locales/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
"@rm/config": "*",
"@rm/logger": "*",
"dotenv": "^16.3.1",
"gpt-tokenizer": "2.2.1",
"node-fetch": "2.7.0",
"openai": "6.9.1"
"node-fetch": "2.7.0"
},
"devDependencies": {
"@types/node-fetch": "2.6.13"
"@types/node-fetch": "2.6.13",
"gpt-tokenizer": "2.2.1",
"openai": "6.9.1"
}
}
57 changes: 39 additions & 18 deletions server/src/services/checkForUpdates.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
// @ts-check
const { exec } = require('child_process')
const { promisify } = require('util')
const path = require('path')
const fs = require('fs')

const { log, TAGS } = require('@rm/logger')

const execPromise = promisify(exec)

const REPO_API = 'https://api.github.com/repos/WatWowMap/ReactMap'

/**
* Docker images always define `GIT_REF` / `GIT_SHA` (empty when built without
* `--build-arg`) and ship no git, bare-metal installs use the local checkout.
*/
const isDocker = 'GIT_REF' in process.env

/** @returns {Promise<{ branch: string, isDocker: boolean }>} */
async function getCurrentBranch() {
if (isDocker) {
const gitRef = (process.env.GIT_REF || '').trim()
return {
branch: gitRef.startsWith('refs/heads/')
? gitRef.slice('refs/heads/'.length)
: '',
isDocker: true,
}
}
try {
const gitRef = fs
.readFileSync(path.resolve(`${__dirname}/../../../.gitref`), 'utf8')
.trim()
if (gitRef) return { branch: gitRef.split('/')[2].trim(), isDocker: true }

const { stdout } = await execPromise('git branch --show-current')
return { branch: stdout.trim(), isDocker: false }
} catch (e) {
Expand All @@ -29,12 +40,8 @@ async function getCurrentBranch() {
}

async function getCurrentSha() {
if (isDocker) return (process.env.GIT_SHA || '').trim()
try {
const gitSha = fs
.readFileSync(path.resolve(`${__dirname}/../../../.gitsha`), 'utf8')
.trim()
if (gitSha) return gitSha

const { stdout } = await execPromise('git rev-parse HEAD')
return stdout.trim()
} catch (e) {
Expand All @@ -51,10 +58,17 @@ async function getCurrentSha() {
/** @param {string} branch */
async function getRemoteSha(branch) {
try {
const { stdout } = await execPromise(
`git ls-remote https://github.com/WatWowMap/ReactMap/ refs/heads/${branch}`,
const response = await fetch(
`${REPO_API}/commits/${encodeURIComponent(branch)}`,
{
headers: { Accept: 'application/vnd.github.sha' },
signal: AbortSignal.timeout(10_000),
},
)
return stdout.split('\t')[0]
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`)
}
return (await response.text()).trim()
} catch (e) {
log.info(
TAGS.update,
Expand All @@ -70,10 +84,17 @@ async function getRemoteSha(branch) {

async function checkForUpdates() {
try {
const { branch, isDocker } = await getCurrentBranch()
const sha = await getCurrentSha()
const { branch } = await getCurrentBranch()
const sha = branch ? await getCurrentSha() : ''
if (!branch || !sha) {
log.info(
TAGS.update,
'No branch or commit information available, skipping update check',
)
return
}
const remoteSha = await getRemoteSha(branch)
if (remoteSha !== sha) {
if (sha && remoteSha && remoteSha !== sha) {
log.info(
TAGS.update,
'There is a new version available: ',
Expand Down
11 changes: 9 additions & 2 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

const { defineConfig, loadEnv, createLogger } = require('vite')
const { default: react } = require('@vitejs/plugin-react-swc')
const { default: checker } = require('vite-plugin-checker')
const removeFiles = require('rollup-plugin-delete')
const { resolve } = require('path')
const fs = require('fs')
Expand Down Expand Up @@ -74,7 +73,7 @@ const viteConfig = defineConfig(({ mode }) => {
react(),
...(isDevelopment
? [
checker({
require('vite-plugin-checker').default({
overlay: {
initialIsOpen: false,
},
Expand All @@ -94,6 +93,14 @@ const viteConfig = defineConfig(({ mode }) => {
org: sentry.org,
project: sentry.project,
authToken: sentry.authToken,
// Docker images have no git, so fall back to the baked GIT_SHA
// (the plugin would otherwise run `git rev-parse HEAD`)
release: {
name:
process.env.SENTRY_RELEASE ||
process.env.GIT_SHA ||
undefined,
},
}),
]
: []),
Expand Down
Loading
Loading