diff --git a/.dockerignore b/.dockerignore index 76b3c7c9f..8d907d9e0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,3 +12,12 @@ node_modules .DS_Store .phpunit.result.cache .git +.codeclimate.yml +.github +.gitignore +.jshintrc +.sonarcloud.properties +ansible.cfg +docker-compose.yml +Vagrantfile +*.md \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6af84f26..1687a6d25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: Install Node uses: actions/setup-node@v3 with: - node-version: 20.x + node-version: 24.x - name: Install JavaScript dependencies run: npm install @@ -90,7 +90,7 @@ jobs: - name: Install Node uses: actions/setup-node@v3 with: - node-version: 20.x + node-version: 24.x - name: Install JavaScript dependencies run: npm install diff --git a/cypress.config.js b/cypress.config.js index 45fe3fdfb..21c9dd3a8 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -3,7 +3,8 @@ const { defineConfig } = require("cypress") module.exports = defineConfig({ projectRoot: "tests", e2e: { - // You also can run like this: npx cypress run --config "baseUrl=http://localhost/Skosmos" + // You also can run like this: npx cypress run --config "baseUrl=http://localhost/Skosmos/" + // !! Tailing slash in baseUrl is important baseUrl: 'http://localhost:9090/', setupNodeEvents(on, config) { on('task', { diff --git a/dockerfiles/Dockerfile.ubuntu b/dockerfiles/Dockerfile.ubuntu index f9aa47507..8f7216fe7 100644 --- a/dockerfiles/Dockerfile.ubuntu +++ b/dockerfiles/Dockerfile.ubuntu @@ -1,5 +1,26 @@ -# Stage 0: install Node dependencies -FROM node:18-alpine AS npm-installer +ARG NODE_VERSION=24 + +# Stage 0a: Install composer packages (production) +FROM composer:latest AS composer-prod +WORKDIR /app +COPY . /app/ +RUN composer install --no-dev \ + --ignore-platform-req=ext-intl \ + --ignore-platform-req=ext-xsl \ + --ignore-platform-req=php + +# Stage 0b: Install composer packages (development with dev dependencies) +FROM composer:latest AS composer-dev +WORKDIR /app +COPY . /app/ +RUN composer install \ + --ignore-platform-req=ext-intl \ + --ignore-platform-req=ext-xsl \ + --ignore-platform-req=php + +# Stage 1a: install Node dependencies (production) +ARG NODE_VERSION +FROM node:${NODE_VERSION}-alpine AS npm-installer WORKDIR /usr/src/app COPY package.json ./ @@ -7,8 +28,18 @@ COPY package.json ./ # install node.js dependencies e.g. Vue (but not the development dependencies) RUN npm install --omit=dev --ignore-scripts -# Stage 1: runtime image -FROM ubuntu:24.04 +# Stage 1b: install Node dependencies (development with dev dependencies) +ARG NODE_VERSION +FROM node:${NODE_VERSION}-alpine AS npm-dev + +WORKDIR /usr/src/app +COPY package.json ./ + +# install all node.js dependencies including dev +RUN npm install --ignore-scripts + +# Stage 2: runtime image +FROM ubuntu:24.04 AS runtime LABEL maintainer="National Library of Finland" LABEL version="0.1" @@ -19,7 +50,6 @@ ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ apache2 \ curl \ - git \ libapache2-mod-php8.3 \ locales \ php8.3 \ @@ -76,23 +106,69 @@ RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf && \ WORKDIR /var/www/html RUN rm index.html -# composer and packages layer -RUN curl -sS https://getcomposer.org/installer | php -COPY composer.json /var/www/html -RUN php composer.phar install --no-dev --no-autoloader +EXPOSE 80 -# skosmos layer -COPY . /var/www/html -RUN php composer.phar install --no-dev +CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] -# install Node modules (from npm-installer stage) -COPY --from=npm-installer /usr/src/app/node_modules /var/www/html/node_modules +FROM runtime AS development + +# Install Node.js matching the version used in npm stages +ARG NODE_VERSION +RUN apt-get update && apt-get install -y \ + ca-certificates \ + curl \ + gnupg \ + && mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + libgtk-3-0t64 \ + libgbm-dev \ + libnotify-dev \ + libnss3 \ + libxss1 \ + libasound2t64 \ + libxtst6 \ + xauth \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +# vendors with dev dependencies +COPY --from=composer-dev /app/vendor /var/www/html/vendor + +# all source files including tests +COPY . /var/www/html/ + +# install Node modules with dev dependencies +COPY --from=npm-dev /usr/src/app/node_modules /var/www/html/node_modules # Configure Skosmos COPY dockerfiles/config/config-docker.ttl /var/www/html/config.ttl -HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost || exit 1 +# install cypress +RUN npx cypress install -EXPOSE 80 +FROM runtime AS production -CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] +# vendors (production only, from composer-prod stage) +COPY --from=composer-prod /app/vendor /var/www/html/vendor + +# sources (only production files) +COPY bin /var/www/html/bin +COPY config /var/www/html/config +COPY custom-templates /var/www/html/custom-templates +COPY plugins /var/www/html/plugins +COPY resource /var/www/html/resource +COPY src /var/www/html/src +COPY .env /var/www/html +COPY .htaccess /var/www/html +COPY composer.json /var/www/html +COPY config.ttl.dist /var/www/html +COPY favicon.ico /var/www/html +COPY swagger.json /var/www/html + +# install Node modules (from npm-installer stage) +COPY --from=npm-installer /usr/src/app/node_modules /var/www/html/node_modules + +HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost || exit 1 diff --git a/src/controller/Controller.php b/src/controller/Controller.php index 962531118..68df74d3a 100644 --- a/src/controller/Controller.php +++ b/src/controller/Controller.php @@ -176,7 +176,7 @@ protected function getGitModifiedDate() protected function executeGitModifiedDateCommand($gitCommand) { $commitDate = null; - $commandOutput = @exec($gitCommand); + $commandOutput = @exec($gitCommand . ' 2>/dev/null'); if ($commandOutput) { $commitDate = new \DateTime(trim($commandOutput)); $commitDate->setTimezone(new \DateTimeZone('UTC')); diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 45de0dfe4..ee7f34ab4 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -37,15 +37,22 @@ public function testConstructor() public function testGetVersion() { $version = $this->model->getVersion(); + // if git is installed on host // make sure that the returned version (which comes from composer.json) // matches the version from git tags - $git_tag = rtrim(shell_exec('git describe --tags --always')); - $this->assertStringStartsWith( - "v" . $version, - $git_tag, - "Composer version '$version' doesn't match git tag '$git_tag'.\n" . - "Please run 'composer update' to update the Composer version." - ); + $shell_result = shell_exec('git describe --tags --always 2>/dev/null'); + if (null !== $shell_result) { + $git_tag = rtrim($shell_result); + $this->assertStringStartsWith( + "v" . $version, + $git_tag, + "Composer version '$version' doesn't match git tag '$git_tag'.\n" . + "Please run 'composer update' to update the Composer version." + ); + } else { + // This assert is to prevent a "risky" flag for no assertion in test + $this->assertNull($shell_result); + } } /** diff --git a/tests/WebControllerTest.php b/tests/WebControllerTest.php index fe8ec663f..7798bbfcd 100644 --- a/tests/WebControllerTest.php +++ b/tests/WebControllerTest.php @@ -142,6 +142,7 @@ public function testGetGitModifiedDate($cacheAvailable, $cachedValue, $modifiedD } /** + * If git is installed on host. * Execute the git command and test that it returns a valid date time. It should be safe to execute this, as * Travis-CI and developer environments should have git installed. */ @@ -151,8 +152,13 @@ public function testExecuteGitModifiedDateCommand() ->shouldAllowMockingProtectedMethods() ->makePartial(); $gitModifiedDate = $controller->executeGitModifiedDateCommand('git log -1 --date=iso --pretty=format:%cd'); - $this->assertInstanceOf('DateTime', $gitModifiedDate); - $this->assertTrue($gitModifiedDate > (new DateTime())->setTimeStamp(1)); + if (null != $gitModifiedDate) { + $this->assertInstanceOf('DateTime', $gitModifiedDate); + $this->assertTrue($gitModifiedDate > (new DateTime())->setTimeStamp(1)); + } else { + // This assert is to prevent a "risky" flag for no assertion in test + $this->assertNull($gitModifiedDate); + } } /** diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 4d3fa9ad9..7cb0ae19c 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -8,6 +8,10 @@ services: file: ../docker-compose.yml service: fuseki-cache skosmos: + build: + context: .. + dockerfile: dockerfiles/Dockerfile.ubuntu + target: development extends: file: ../docker-compose.yml service: skosmos