diff options
Diffstat (limited to 'cypress/dockerNode.ts')
-rw-r--r-- | cypress/dockerNode.ts | 146 |
1 files changed, 109 insertions, 37 deletions
diff --git a/cypress/dockerNode.ts b/cypress/dockerNode.ts index 38bc86d9c5f..6e21b33101c 100644 --- a/cypress/dockerNode.ts +++ b/cypress/dockerNode.ts @@ -1,23 +1,6 @@ /** - * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* eslint-disable no-console */ /* eslint-disable n/no-unpublished-import */ @@ -25,12 +8,14 @@ import Docker from 'dockerode' import waitOn from 'wait-on' -import tar from 'tar' +import { c as createTar } from 'tar' +import path, { basename } from 'path' import { execSync } from 'child_process' +import { existsSync } from 'fs' export const docker = new Docker() -const CONTAINER_NAME = 'nextcloud-cypress-tests-server' +const CONTAINER_NAME = `nextcloud-cypress-tests_${basename(process.cwd()).replace(' ', '')}` const SERVER_IMAGE = 'ghcr.io/nextcloud/continuous-integration-shallow-server' /** @@ -56,10 +41,6 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc // https://github.com/apocas/dockerode/issues/357 docker.modem.followProgress(stream, onFinished) - /** - * - * @param err - */ function onFinished(err) { if (!err) { resolve(true) @@ -68,6 +49,10 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc reject(err) } })) + + const digest = await (await docker.getImage(SERVER_IMAGE).inspect()).RepoDigests.at(0) + const sha = digest?.split('@').at(1) + console.log('├─ Using image ' + sha) console.log('└─ Done') } catch (e) { console.log('└─ Failed to pull images') @@ -97,14 +82,33 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc Image: SERVER_IMAGE, name: CONTAINER_NAME, HostConfig: { - Binds: [], + Mounts: [{ + Target: '/var/www/html/data', + Source: '', + Type: 'tmpfs', + ReadOnly: false, + }], + PortBindings: { + '80/tcp': [{ + HostIP: '0.0.0.0', + HostPort: '8083', + }], + }, + // If running the setup tests, let's bind to host + // to communicate with the github actions DB services + NetworkMode: process.env.SETUP_TESTING === 'true' ? await getGithubNetwork() : undefined, }, Env: [ `BRANCH=${branch}`, + 'APCU=1', ], }) await container.start() + // Set proper permissions for the data folder + await runExec(container, ['chown', '-R', 'www-data:www-data', '/var/www/html/data'], false, 'root') + await runExec(container, ['chmod', '0770', '/var/www/html/data'], false, 'root') + // Get container's IP const ip = await getContainerIP(container) @@ -133,6 +137,29 @@ export const configureNextcloud = async function() { await runExec(container, ['php', 'occ', 'config:system:set', 'force_locale', '--value', 'en_US'], true) await runExec(container, ['php', 'occ', 'config:system:set', 'enforce_theme', '--value', 'light'], true) + // Speed up test and make them less flaky. If a cron execution is needed, it can be triggered manually. + await runExec(container, ['php', 'occ', 'background:cron'], true) + + // Checking apcu + const distributed = await runExec(container, ['php', 'occ', 'config:system:get', 'memcache.distributed']) + const local = await runExec(container, ['php', 'occ', 'config:system:get', 'memcache.local']) + const hashing = await runExec(container, ['php', 'occ', 'config:system:get', 'hashing_default_password']) + + console.log('├─ Checking APCu configuration... 👀') + if (!distributed.trim().includes('Memcache\\APCu') + || !local.trim().includes('Memcache\\APCu') + || !hashing.trim().includes('true')) { + console.log('└─ APCu is not properly configured 🛑') + throw new Error('APCu is not properly configured') + } + console.log('│ └─ OK !') + + // Saving DB state + console.log('├─ Creating init DB snapshot...') + await runExec(container, ['cp', '/var/www/html/data/owncloud.db', '/var/www/html/data/owncloud.db-init'], true) + console.log('├─ Creating init data backup...') + await runExec(container, ['tar', 'cf', 'data-init.tar', 'admin'], true, undefined, '/var/www/html/data') + console.log('└─ Nextcloud is now ready to use 🎉') } @@ -144,7 +171,6 @@ export const configureNextcloud = async function() { */ export const applyChangesToNextcloud = async function() { console.log('\nApply local changes to nextcloud...') - const container = docker.getContainer(CONTAINER_NAME) const htmlPath = '/var/www/html' const folderPaths = [ @@ -156,6 +182,7 @@ export const applyChangesToNextcloud = async function() { './ocs', './ocs-provider', './resources', + './tests', './console.php', './cron.php', './index.php', @@ -164,14 +191,27 @@ export const applyChangesToNextcloud = async function() { './remote.php', './status.php', './version.php', - ] + ].filter((folderPath) => { + const fullPath = path.resolve(__dirname, '..', folderPath) - folderPaths.forEach((path) => { - console.log(`├─ Copying ${path}`) + if (existsSync(fullPath)) { + console.log(`├─ Copying ${folderPath}`) + return true + } + return false }) + // Don't try to apply changes, when there are none. Otherwise we + // still execute the 'chown' command, which is not needed. + if (folderPaths.length === 0) { + console.log('└─ No local changes found to apply') + return + } + + const container = docker.getContainer(CONTAINER_NAME) + // Tar-streaming the above folders into the container - const serverTar = tar.c({ gzip: false }, folderPaths) + const serverTar = createTar({ gzip: false }, folderPaths) await container.putArchive(serverTar, { path: htmlPath, }) @@ -209,11 +249,16 @@ export const getContainerIP = async function( while (ip === '' && tries < 10) { tries++ - await container.inspect(function(err, data) { + container.inspect(function(err, data) { if (err) { throw err } - ip = data?.NetworkSettings?.IPAddress || '' + + if (data?.HostConfig.PortBindings?.['80/tcp']?.[0]?.HostPort) { + ip = `localhost:${data.HostConfig.PortBindings['80/tcp'][0].HostPort}` + } else { + ip = data?.NetworkSettings?.IPAddress || '' + } }) if (ip !== '') { @@ -250,15 +295,18 @@ const runExec = async function( command: string[], verbose = false, user = 'www-data', -) { + workdir?: string, +): Promise<string> { const exec = await container.exec({ Cmd: command, + WorkingDir: workdir, AttachStdout: true, AttachStderr: true, User: user, }) return new Promise((resolve, reject) => { + let output = '' exec.start({}, (err, stream) => { if (err) { reject(err) @@ -266,11 +314,17 @@ const runExec = async function( if (stream) { stream.setEncoding('utf-8') stream.on('data', str => { - if (verbose && str.trim() !== '') { - console.log(`├─ ${str.trim().replace(/\n/gi, '\n├─ ')}`) + str = str.trim() + // Remove non printable characters + .replace(/[^\x0A\x0D\x20-\x7E]+/g, '') + // Remove non alphanumeric leading characters + .replace(/^[^a-z]/gi, '') + output += str + if (verbose && str !== '') { + console.log(`├─ ${str.replace(/\n/gi, '\n├─ ')}`) } }) - stream.on('end', resolve) + stream.on('end', () => resolve(output)) } }) }) @@ -283,3 +337,21 @@ const sleep = function(milliseconds: number) { const getCurrentGitBranch = function() { return execSync('git rev-parse --abbrev-ref HEAD').toString().trim() || 'master' } + +/** + * Get the network name of the github actions network + * This is used to connect to the database services + * started by github actions + */ +const getGithubNetwork = async function(): Promise<string|undefined> { + console.log('├─ Looking for github actions network... 🔍') + const networks = await docker.listNetworks() + const network = networks.find((network) => network.Name.startsWith('github_network')) + if (network) { + console.log('│ └─ Found github actions network: ' + network.Name) + return network.Name + } + + console.log('│ └─ No github actions network found') + return undefined +} |