diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f0fbe40d..ae423f459 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,31 +9,46 @@ on: jobs: build_mac: - runs-on: macOS-latest + runs-on: macos-latest timeout-minutes: 20 env: CI: '' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v2 with: - node-version: 16 + node-version: '14.x' - - name: Install dependencies - run: pnpm i --frozen-lockfile + # React + - name: React - Install dependencies + run: yarn install --frozen-lockfile --network-timeout 300000 + working-directory: ./client - - name: Build project packages - run: turbo build + - name: React - Build project + run: yarn build + working-directory: ./client + # Node server + - name: Server - Install dependencies + run: yarn install --frozen-lockfile --production --network-timeout 300000 + working-directory: ./server/src + + # App + - name: Electron - Install dependencies + shell: bash + run: yarn install --frozen-lockfile --network-timeout 300000 && yarn setdb + working-directory: ./server - name: Electron - Build app - run: turbo dist-mac + run: yarn dist-mac + working-directory: ./server + # Release - name: Release uses: softprops/action-gh-release@v1 with: - files: ./electron/dist/ontime-macOS.dmg + files: ./server/dist/ontime-macOS.dmg env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -44,26 +59,40 @@ jobs: CI: '' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v1 with: - node-version: 16 + node-version: '14.x' - - name: Install dependencies - run: pnpm i --frozen-lockfile + # React + - name: React - Install dependencies + run: yarn install --frozen-lockfile --network-timeout 300000 + working-directory: ./client - - name: Build project packages - run: turbo build + - name: React - Build project + run: yarn build + working-directory: ./client + # Node server + - name: Server - Install dependencies + run: yarn install --frozen-lockfile --production --network-timeout 300000 + working-directory: ./server/src + + # App + - name: Electron - Install dependencies + shell: bash + run: yarn install --frozen-lockfile --network-timeout 300000 && yarn setdb + working-directory: ./server - name: Electron - Build app - run: turbo dist-win + run: yarn dist-win + working-directory: ./server # Release - name: Release uses: softprops/action-gh-release@v1 with: - files: ./electron/dist/ontime-win64.exe + files: ./server/dist/ontime-win64.exe env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -74,71 +103,39 @@ jobs: CI: '' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v1 with: - node-version: 16 + node-version: '14.x' - - name: Install dependencies - run: pnpm i --frozen-lockfile + # React + - name: React - Install dependencies + run: yarn install --frozen-lockfile --network-timeout 300000 + working-directory: ./client - - name: Build project packages - run: turbo build + - name: React - Build project + run: yarn build + working-directory: ./client + # Node server + - name: Server - Install dependencies + run: yarn install --frozen-lockfile --production --network-timeout 300000 + working-directory: ./server/src + + # App + - name: Electron - Install dependencies + shell: bash + run: yarn install --frozen-lockfile --network-timeout 300000 && yarn setdb + working-directory: ./server - name: Electron - Build app - run: turbo dist-linux + run: yarn dist-linux + working-directory: ./server # Release - name: Release uses: softprops/action-gh-release@v1 with: - files: ./electron/dist/ontime-linux.AppImage + files: ./server/dist/ontime-linux.AppImage env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish_docker: - runs-on: ubuntu-latest - timeout-minutes: 20 - env: - CI: '' - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - - steps: - - uses: actions/checkout@v3 - - name: Setup env - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Install dependencies - run: pnpm i --frozen-lockfile - - - name: Build project packages - run: turbo build - - - name: Electron - Build app - run: turbo dist-mac - - # Login to docker - - name: Login to Docker Hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Prepare builder - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - # Build and push - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - file: ./Dockerfile - push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/ontime:${{ env.RELEASE_VERSION }} , ${{ secrets.DOCKERHUB_USERNAME }}/ontime:latest - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/build_docker_v2.yml b/.github/workflows/build_docker_v2.yml new file mode 100644 index 000000000..f42834bf7 --- /dev/null +++ b/.github/workflows/build_docker_v2.yml @@ -0,0 +1,69 @@ +name: Docker Image CI Ontime V2 + +on: + push: + tags: [ "v2.*.*" ] + workflow_dispatch: + +jobs: + + publish_docker: + runs-on: ubuntu-latest + env: + CI: '' + + steps: + - uses: actions/checkout@v3 + - name: Setup env + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Setup Node.js environment + uses: actions/setup-node@v3.6.0 + with: + # File containing the version Spec of the version to use. Examples: .nvmrc, .node-version, .tool-versions. + version: 16.16.0 + + - name: Setup pnpm + # You may pin to the exact commit or the version. + # uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd + uses: pnpm/action-setup@v2.2.4 + with: + # Version of pnpm to install + version: 7.26.3 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build project packages + run: pnpm turbo build:docker + + - name: Docker Login + # You may pin to the exact commit or the version. + # uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + uses: docker/login-action@v2.1.0 + with: + # Username used to log against the Docker registry + username: ${{ secrets.DOCKERHUB_USERNAME }} + # Password or personal access token used to log against the Docker registry + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Setup Buildx + # You may pin to the exact commit or the version. + # uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c + uses: docker/setup-buildx-action@v2.5.0 + + - name: Build and push Docker images + # You may pin to the exact commit or the version. + # uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + uses: docker/build-push-action@v4.0.0 + with: + # Build's context is the set of files located in the specified PATH or URL + context: . + # Path to the Dockerfile + file: ./Dockerfile + # List of target platforms for build + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 + # Push is a shorthand for --output=type=registry + push: true + # List of tags + tags: ${{ secrets.DOCKERHUB_USERNAME }}/ontime:beta_${{ env.RELEASE_VERSION }} , ${{ secrets.DOCKERHUB_USERNAME }}/ontime:beta_v2 + diff --git a/Dockerfile b/Dockerfile index dec3dd0b1..65df28fe3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,27 @@ FROM node:16-alpine -WORKDIR /apps/server +# Set environment variables +# Environment Variable to signal that we are running production +ENV NODE_ENV=docker +# Ontime Data path +ENV ONTIME_DATA=/external/ + +WORKDIR /app/ # Prepare UI -COPY /apps/client/build ../client/build +COPY /apps/client/build ./client/ # Prepare Backend -COPY /apps/server ./ +COPY /apps/server/dist/ ./server/ +COPY /demo-db/ ./preloaded-db/ +COPY /apps/server/src/external/ ./external/ -# Export default ports Main - OSC IN -EXPOSE 4001/tcp 8888/udp -ENV NODE_ENV=production -ENV ONTIME_DATA=/server/ +# Export default ports +EXPOSE 4001/tcp 8888/udp 9999/udp -CMD ["npm", "start:headless"] +CMD ["node", "server/docker.cjs"] # Build and run commands # !!! Note that this command needs pre-build versions of the UI and server apps -# docker build -t getontime/ontime . -# docker run -p 4001:4001 -p 10.0.0.12:8888:8888/udp --mount type=bind,source="$(pwd)/ontime-db",target=/server/preloaded-db getontime/ontime +# docker buildx build . -t getontime/ontime +# docker run -p 4001:4001 -p 8888:8888/udp -p 9999:9999/udp -v ./ontime-db:/external/db/ -v ./ontime-styles:/external/styles/ getontime/ontime diff --git a/apps/client/package.json b/apps/client/package.json index 3340ba824..167f798a4 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -38,6 +38,7 @@ "postinstall": "pnpm addversion", "dev": "set BROWSER=none&&vite", "build": "vite build", + "build:docker": "vite build", "lint": "eslint .", "stylelint": "npx stylelint \"**/*.scss\"\n", "test": "vitest", diff --git a/apps/electron/main.js b/apps/electron/main.js index 821d67e73..e9ca29eb3 100644 --- a/apps/electron/main.js +++ b/apps/electron/main.js @@ -36,10 +36,10 @@ let tray = null; } try { - const ontimeServer = require(nodePath); - const { startDb, startServer, startOSCServer, startIntegrations } = ontimeServer; + const ontimeServer = require(nodePath) + const { initAssets, startServer, startOSCServer, startIntegrations } = ontimeServer; - await startDb(); + await initAssets(); loaded = await startServer(); await startOSCServer(); diff --git a/apps/server/package.json b/apps/server/package.json index 8482f4d42..b86b68ce9 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -48,6 +48,7 @@ "setdb": "shx cp ../../demo-db/db.json src/preloaded-db/db.json", "prebuild": "pnpm setdb", "build": "pnpm prebuild && esbuild src/app.ts --log-level=error --platform=node --format=cjs --bundle --minify --outfile=dist/index.cjs", + "build:docker": "pnpm prebuild && esbuild src/index.ts --log-level=error --platform=node --format=cjs --minify --bundle --outfile=dist/docker.cjs", "build:debug": "pnpm prebuild && esbuild src/app.ts --platform=node --format=cjs --bundle --outfile=dist/index.cjs", "lint": "eslint .", "test": "vitest", diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 514106a2f..629039164 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -29,6 +29,7 @@ import { eventLoader } from './classes/event-loader/EventLoader.js'; import { integrationService } from './services/integration-service/IntegrationService.js'; import { logger } from './classes/Logger.js'; import { oscIntegration } from './services/integration-service/OscIntegration.js'; +import { populateStyles } from './modules/loadStyles.js'; console.log(`Starting Ontime version ${ONTIME_VERSION}`); @@ -95,19 +96,19 @@ app.use((error, response) => { enum OntimeStartOrder { Error, - InitDB, + InitAssets, InitServer, InitIO, } -let step = OntimeStartOrder.InitDB; +let step = OntimeStartOrder.InitAssets; let expressServer = null; let oscServer = null; const checkStart = (currentState: OntimeStartOrder) => { if (step !== currentState) { step = OntimeStartOrder.Error; - throw new Error('Init order error: startDb > startServer > startOsc > startIntegrations'); + throw new Error('Init order error: initAssets > startServer > startOsc > startIntegrations'); } else { if (step === 1 || step === 2) { step = step + 1; @@ -115,9 +116,10 @@ const checkStart = (currentState: OntimeStartOrder) => { } }; -export const startDb = async () => { - checkStart(OntimeStartOrder.InitDB); +export const initAssets = async () => { + checkStart(OntimeStartOrder.InitAssets); await dbLoadingProcess; + populateStyles(); }; /** diff --git a/apps/server/src/config/config.js b/apps/server/src/config/config.js index 1a4915d25..57f9af2c0 100644 --- a/apps/server/src/config/config.js +++ b/apps/server/src/config/config.js @@ -1,8 +1,12 @@ export const config = { database: { testdb: 'test-db', - directory: 'preloaded-db', + directory: 'db', filename: 'db.json', tablename: 'events', }, + styles: { + directory: 'styles', + filename: 'override.css', + }, }; diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 68f4c4df6..de8a9b3d7 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,10 +1,15 @@ -import { startDb, startIntegrations, startOSCServer, startServer } from './app.js'; +import { initAssets, startIntegrations, startOSCServer, startServer } from './app.js'; async function startOntime() { try { - await startDb(); + console.log('Starting Ontime'); + console.log('Loading Assets'); + await initAssets(); + console.log('Starting Server'); await startServer(); + console.log('Starting OSC Server'); await startOSCServer(); + console.log('Starting Integrations'); await startIntegrations(); } catch (error) { console.log('Error starting Ontime'); diff --git a/apps/server/src/modules/loadStyles.ts b/apps/server/src/modules/loadStyles.ts new file mode 100644 index 000000000..fe4338fb5 --- /dev/null +++ b/apps/server/src/modules/loadStyles.ts @@ -0,0 +1,24 @@ +import { copyFileSync, existsSync } from 'fs'; +import { pathToStartStyles, resolveStylesDirectory, resolveStylesPath } from '../setup.js'; +import { ensureDirectory } from '../utils/fileManagement.js'; +import { reportSentryException } from './sentry.js'; + +/** + * @description ensures directories exist and populates stylesheet + * @return {string} - path to stylesheet file + */ +export const populateStyles = () => { + const stylesInDisk = resolveStylesPath; + ensureDirectory(resolveStylesDirectory); + + // if stylesInDisk doesn't exist we want to use startup stylesheet + if (!existsSync(stylesInDisk)) { + try { + copyFileSync(pathToStartStyles, stylesInDisk); + } catch (error) { + reportSentryException(error); + } + } + + return stylesInDisk; +}; diff --git a/apps/server/src/setup.ts b/apps/server/src/setup.ts index 65177a088..d936300ed 100644 --- a/apps/server/src/setup.ts +++ b/apps/server/src/setup.ts @@ -36,14 +36,24 @@ const env = process.env.NODE_ENV || 'production'; export const isTest = Boolean(process.env.IS_TEST); export const environment = isTest ? 'test' : env; -export const isProduction = env === 'production' && !isTest; +export const isProduction = env === ('production' || 'docker') && !isTest; +export const isDocker = env === 'docker'; // ================================================= // resolve path to external const productionPath = '../../Resources/extraResources/client'; const devPath = '../../client/build/'; +const dockerPath = 'client/'; -export const resolvedPath = (): string => (isProduction ? productionPath : devPath); +export const resolvedPath = (): string => { + if (isProduction) { + return productionPath; + } + if (isDocker) { + return dockerPath; + } + return devPath; +}; // resolve file URL in both CJS and ESM (build and dev) if (import.meta.url) { @@ -61,4 +71,10 @@ export const resolveDbPath = join(resolveDbDirectory, config.database.filename); export const pathToStartDb = isTest ? join(currentDirectory, '../', config.database.testdb, config.database.filename) - : join(currentDirectory, config.database.directory, config.database.filename); + : join(currentDirectory, '/preloaded-db/', config.database.filename); + +// path to public styles +export const resolveStylesDirectory = join(appPath, config.styles.directory); +export const resolveStylesPath = join(resolveStylesDirectory, config.styles.filename); + +export const pathToStartStyles = join(currentDirectory, '/external/styles/', config.styles.filename); diff --git a/docker-compose.yml b/docker-compose.yml index 6c6571a6d..9cd538113 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,13 +3,14 @@ version: "3" services: ontime: container_name: ontime - image: getontime/ontime:latest + image: getontime/ontime:beta_v2 ports: - "4001:4001/tcp" - - "127.0.0.1:8888:8888/udp" + - "8888:8888/udp" + - "9999:9999/udp" volumes: - - "./ontime-db:/server/preloaded-db" - - "./ontime-external:/server/external" + - "./ontime-db:/external/db/" + - "./ontime-styles:/external/styles/" environment: - TZ=Asia/Singapore restart: unless-stopped diff --git a/turbo.json b/turbo.json index 2050f0efd..8a85a9cf9 100644 --- a/turbo.json +++ b/turbo.json @@ -3,6 +3,7 @@ "pipeline":{ "dev": {}, "build": {}, + "build:docker": {}, "test": {}, "test:pipeline": {}, "e2e": {