From 3189da940073489f581d553ad0c0d110e4b3dbe5 Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Wed, 23 Oct 2019 09:57:15 +0200 Subject: [PATCH] Download the cx-server from github.com (#9) * Download the cx-server from github.com * Remove unused variable * Add missing await * Download cx-server.bat when running on windows * Use Promise.resolve/reject for copyLocal --- src/commands/add-approuter.ts | 2 +- src/commands/add-cx-server.ts | 31 ++++++-- src/commands/init.ts | 2 +- .../add-cx-server/cx-server/cx-server | 32 --------- .../add-cx-server/cx-server/cx-server.bat | 36 ---------- .../add-cx-server/cx-server/server.cfg | 70 ------------------- src/utils/copy-list.ts | 2 +- src/utils/templates.ts | 57 +++++++++++---- test/add-approuter.spec.ts | 2 +- test/add-cx-server.spec.ts | 20 +++++- 10 files changed, 93 insertions(+), 161 deletions(-) delete mode 100644 src/templates/add-cx-server/cx-server/cx-server delete mode 100644 src/templates/add-cx-server/cx-server/cx-server.bat delete mode 100644 src/templates/add-cx-server/cx-server/server.cfg diff --git a/src/commands/add-approuter.ts b/src/commands/add-approuter.ts index 69e8a735..5221a78b 100644 --- a/src/commands/add-approuter.ts +++ b/src/commands/add-approuter.ts @@ -39,7 +39,7 @@ export default class AddApprouter extends Command { cli.action.stop(); cli.action.start('Creating files'); - copyFiles(files, await this.getOptions(), this.error); + await copyFiles(files, await this.getOptions()).catch(e => this.error(e, { exit: 2 })); cli.action.stop(); this.printSuccessMessage(); diff --git a/src/commands/add-cx-server.ts b/src/commands/add-cx-server.ts index 46dca227..76703aa5 100644 --- a/src/commands/add-cx-server.ts +++ b/src/commands/add-cx-server.ts @@ -3,9 +3,13 @@ */ import { Command, flags } from '@oclif/command'; +import { OutputFlags } from '@oclif/parser'; import cli from 'cli-ux'; import * as path from 'path'; -import { copyFiles, findConflicts, readTemplates } from '../utils/templates'; +import { CopyDescriptor } from '../utils/copy-list'; +import { copyFiles, findConflicts } from '../utils/templates'; + +type Flags = OutputFlags; export default class AddCxServer extends Command { static description = 'Add the scripts to set up a Jenkins master for CI/CD of your project'; @@ -21,6 +25,11 @@ export default class AddCxServer extends Command { hidden: true, description: 'Overwrite files without asking if conflicts are found.' }), + platform: flags.string({ + hidden: true, + default: process.platform, + description: 'The currently running OS.' + }), help: flags.help({ char: 'h' }) }; @@ -29,22 +38,34 @@ export default class AddCxServer extends Command { const options = await this.getOptions(); try { - cli.action.start('Reading templates'); - const files = readTemplates({ from: [path.resolve(__dirname, '..', 'templates', 'add-cx-server')], to: flags.projectDir }); - cli.action.stop(); + const files = [this.copyFromGithub('cx-server', flags), this.copyFromGithub('server.cfg', flags)]; + + if (flags.platform === 'win32') { + files.push(this.copyFromGithub('cx-server.bat', flags)); + } cli.action.start('Finding potential conflicts'); await findConflicts(files, flags.force, this.error); cli.action.stop(); cli.action.start('Creating files'); - copyFiles(files, options, this.error); + await copyFiles(files, options).catch(e => this.error(e, { exit: 2 })); cli.action.stop(); } catch (error) { this.error(error, { exit: 1 }); } } + private copyFromGithub(fileName: string, flags: Flags): CopyDescriptor { + const githubPrefix = 'https://raw.githubusercontent.com/SAP/devops-docker-cx-server/master/cx-server-companion/life-cycle-scripts/'; + + return { + sourcePath: new URL(fileName, githubPrefix), + targetFolder: path.resolve(flags.projectDir, 'cx-server'), + fileName: path.resolve(flags.projectDir, 'cx-server', fileName) + }; + } + private getOptions() { return {}; } diff --git a/src/commands/init.ts b/src/commands/init.ts index 933dbe29..07b015c7 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -79,7 +79,7 @@ export default class Init extends Command { cli.action.stop(); cli.action.start('Creating files'); - copyFiles(files, options, this.error); + await copyFiles(files, options).catch(e => this.error(e, { exit: 2 })); cli.action.stop(); cli.action.start('Adding scripts for CI/CD and dependencies to package.json'); diff --git a/src/templates/add-cx-server/cx-server/cx-server b/src/templates/add-cx-server/cx-server/cx-server deleted file mode 100644 index d74f4ba6..00000000 --- a/src/templates/add-cx-server/cx-server/cx-server +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Ensure that docker is installed -command -v docker > /dev/null 2>&1 || { echo >&2 "Docker does not seem to be installed. Please install docker and ensure that the docker command is included in \$PATH."; exit 1; } - -source server.cfg - -if [ -z "${DEVELOPER_MODE}" ]; then - docker pull s4sdk/cxserver-companion:latest -fi - -# Run Docker without '-it' flag on Jenkins to prevent our integration test from getting stuck -if docker run --rm -it hello-world > /dev/null 2>&1 ; then - DOCKER_IT_FLAG='-it' -else - echo No interactive terminal, run Docker without '-it' flag -fi - -# These braces ensure that the block in them cannot be overridden by updating this file on the fly. -# The exit inside protects against unintended effect when the new script file is bigger than its old version. -# See: https://stackoverflow.com/questions/3398258/edit-shell-script-while-its-running -{ - docker run --rm ${DOCKER_IT_FLAG} \ - --mount source="${PWD},target=/cx-server/mount,type=bind" \ - --workdir /cx-server/mount \ - --volume /var/run/docker.sock:/var/run/docker.sock \ - --env DEVELOPER_MODE \ - --env host_os=unix \ - --env cx_server_path="${PWD}" \ - s4sdk/cxserver-companion /cx-server/cx-server-companion.sh "$@" - exit $? -} diff --git a/src/templates/add-cx-server/cx-server/cx-server.bat b/src/templates/add-cx-server/cx-server/cx-server.bat deleted file mode 100644 index 12b5ec59..00000000 --- a/src/templates/add-cx-server/cx-server/cx-server.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -ECHO Please note that Cx Server on Windows is for convenient development and evaluation. -ECHO For running an productive Cx Server, please use a Linux System. - -IF "%~1"==backup GOTO NOT_SUPPORTED -IF "%~1"==restore GOTO NOT_SUPPORTED -IF "%~1"==update ( - IF "%~2"=="image" GOTO NOT_SUPPORTED -) - -IF "%CD%"=="" GOTO WORKING_DIR_EMPTY - -IF "%DEVELOPER_MODE%"=="" docker pull s4sdk/cxserver-companion - -( - docker run --rm -it --workdir /cx-server/mount --volume //var/run/docker.sock:/var/run/docker.sock --mount source="%CD%",target=/cx-server/mount,type=bind --env DEVELOPER_MODE --env host_os=windows --env cx_server_path="%CD%" s4sdk/cxserver-companion /cx-server/cx-server-companion.sh %~1 %~2 - IF NOT %ERRORLEVEL% == 0 GOTO RUN_ERROR - GOTO END -) - -:NOT_SUPPORTED -ECHO "backup", "restore" and "update image" are currently not supported on Windows. -GOTO END - -:RUN_ERROR -ECHO Could not run the Cx Server Docker container. -ECHO Please ensure that docker is running (with "docker run hello-world") -ECHO Also, please make sure that the Windows drive where your project is located (usually C:) is shared with Docker as described in https://docs.docker.com/docker-for-windows/#shared-drives. -GOTO END - -:WORKING_DIR_EMPTY -ECHO The environment variable CD is not set. It is required that this variable points to the directory where cx-server.bat resides. -GOTO END - -:END diff --git a/src/templates/add-cx-server/cx-server/server.cfg b/src/templates/add-cx-server/cx-server/server.cfg deleted file mode 100644 index 8ead5c83..00000000 --- a/src/templates/add-cx-server/cx-server/server.cfg +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# This file configures the SAP Cloud SDK CI/CD Server (cx-server). -# Changes become effective after removing and then restarting the server. -# More information on getting started with Continuous Delivery can be found in the following locations: -# - GitHub repository: https://github.com/SAP/cloud-s4-sdk-pipeline -# - Operations Guide: https://github.com/SAP/cloud-s4-sdk-pipeline/blob/master/doc/operations/operations-guide.md - - -#---------------------------------------------# -#-- General configuration --------------------# -#---------------------------------------------# - -#>> Specify effective value of 'http_proxy' / 'https_proxy' environment variables. -#>> The specified proxy will also be used for the Jenkins and Nexus (Download Cache) processes. -#>> Jenkins only supports one proxy server. In this case, https_proxy will have precedence over http_proxy. -# http_proxy="http://username:password@proxy:8080" -# https_proxy="http://username:password@proxy:8080" - -#>> Specify effective value of 'no_proxy' environment variable (overwrites value in base image) -# no_proxy="localhost,.corp" - -#>> Specify additional no values of 'no_proxy' environment variable (appended to value in base image) -# x_no_proxy="localhost,.corp" - -#---------------------------------------------# -#-- Build server configuration ---------------# -#---------------------------------------------# - -#>> Address of the used docker registry. Override if you do not want to use Docker's default registry. -# docker_registry="your-custom-registry.corp" - -#>> Name of the used docker image -# docker_image="s4sdk/jenkins-master:latest" - -#>> Enable TLS encryption -# tls_enabled=true - -#>> Port on which jenkins will be reachable via http. -#>> This port will be only opened when TLS is not active. -# http_port="80" - -#>> Port on which jenkins will be reachable via https when TLS is activated. -# https_port="443" - -#>> Name of the docker volume holding the jenkins_home folder. -# jenkins_home="jenkins_home_volume" - -#>> Name of the backup file in backup directory. -# backup_file_name="jenkins_home_$(date -u +%Y-%m-%dT%H%M%Z).tar.gz" - -#>> Additional JAVA_OPTS for the jenkins container. The value will be appended to the standard value. -# x_java_opts="-Xmx1024m" - - -#---------------------------------------------# -#-- Download cache configuration -------------# -#---------------------------------------------# - -#>> Toggle for turning the download cache on and off. -# cache_enabled=false - -#>> Maven repository that will be cached via the download cache (default is maven central) -# mvn_repository_url="https://your-local-maven-repo.corp/maven2/" - -#>> npm repository that will be cached via the download cache (default is central npm registy) -# npm_registry_url="https://your-local-npm-registry.corp/" - -#>> Additional JAVA_OPTS for the download cache server. The value will be appended to the standard value. -# x_nexus_java_opts="-Xmx1024m" diff --git a/src/utils/copy-list.ts b/src/utils/copy-list.ts index 154485c5..52c231b0 100644 --- a/src/utils/copy-list.ts +++ b/src/utils/copy-list.ts @@ -3,7 +3,7 @@ */ export interface CopyDescriptor { - sourcePath: string; + sourcePath: string | URL; targetFolder: string; fileName: string; } diff --git a/src/utils/templates.ts b/src/utils/templates.ts index 81cc0681..455ed3a5 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -5,6 +5,7 @@ import cli from 'cli-ux'; import * as fs from 'fs'; import { compile } from 'handlebars'; +import * as https from 'https'; import * as path from 'path'; import { CopyDescriptor } from './copy-list'; @@ -55,24 +56,56 @@ export async function findConflicts(files: CopyDescriptor[], force: boolean, std } } -export function copyFiles(files: CopyDescriptor[], options: { [key: string]: any }, stderr: stderr) { - for (const file of files) { +export async function copyFiles(files: CopyDescriptor[], options: { [key: string]: any }) { + return Promise.all( + files.map(file => { + const { sourcePath, targetFolder, fileName } = file; + + if (sourcePath instanceof URL) { + return copyRemote(sourcePath, targetFolder, fileName); + } else { + return copyLocal(sourcePath, targetFolder, fileName, options); + } + }) + ); +} + +function copyRemote(sourcePath: URL, targetFolder: string, fileName: string) { + return new Promise((resolve, reject) => { + https + .get(sourcePath, response => { + if (response.statusCode && (response.statusCode < 200 || response.statusCode > 299)) { + reject(new Error('Failed to load page, status code: ' + response.statusCode)); + } + + response.on('data', content => { + fs.mkdirSync(targetFolder, { recursive: true }); + fs.writeFileSync(fileName, content); + resolve(); + }); + }) + .on('error', e => { + reject(e); + }); + }); +} + +function copyLocal(sourcePath: string, targetFolder: string, fileName: string, options: { [key: string]: any }) { + try { let content: string; - if (path.extname(file.sourcePath) === '.mu') { - const template = compile(fs.readFileSync(file.sourcePath, { encoding: 'utf8' })); + if (path.extname(sourcePath) === '.mu') { + const template = compile(fs.readFileSync(sourcePath, { encoding: 'utf8' })); content = template(options); } else { - content = fs.readFileSync(file.sourcePath, { encoding: 'utf8' }); - } - - try { - fs.mkdirSync(file.targetFolder, { recursive: true }); - } catch (error) { - stderr(error.message); + content = fs.readFileSync(sourcePath, { encoding: 'utf8' }); } - fs.writeFileSync(file.fileName, content); + fs.mkdirSync(targetFolder, { recursive: true }); + fs.writeFileSync(fileName, content); + return Promise.resolve(); + } catch (e) { + return Promise.reject(e); } } diff --git a/test/add-approuter.spec.ts b/test/add-approuter.spec.ts index d36c79f4..e1f9e698 100644 --- a/test/add-approuter.spec.ts +++ b/test/add-approuter.spec.ts @@ -22,7 +22,7 @@ import * as path from 'path'; import AddApprouter from '../src/commands/add-approuter'; describe('Add Approuter', () => { - const pathPrefix = path.resolve(__dirname, __filename.replace('.', '-')); + const pathPrefix = path.resolve(__dirname, __filename.replace(/\./g, '-')).replace('-ts', ''); beforeAll(() => { if (!fs.existsSync(pathPrefix)) { diff --git a/test/add-cx-server.spec.ts b/test/add-cx-server.spec.ts index d550ce57..53bf973d 100644 --- a/test/add-cx-server.spec.ts +++ b/test/add-cx-server.spec.ts @@ -20,7 +20,7 @@ import * as path from 'path'; import AddCxServer from '../src/commands/add-cx-server'; describe('Add CX Server', () => { - const pathPrefix = path.resolve(__dirname, __filename.replace('.', '-')); + const pathPrefix = path.resolve(__dirname, __filename.replace(/\./g, '-')).replace('-ts', ''); beforeAll(() => { if (!fs.existsSync(pathPrefix)) { @@ -44,6 +44,23 @@ describe('Add CX Server', () => { const files = fs.readdirSync(projectDir); expect(files).toContain('cx-server'); + const approuterFiles = fs.readdirSync(path.resolve(projectDir, 'cx-server')); + expect(approuterFiles).toContain('cx-server'); + expect(approuterFiles).toContain('server.cfg'); + }, 30000); + + it('should add the necessary files on windows', async () => { + const projectDir = path.resolve(pathPrefix, 'add-cx-server'); + if (fs.existsSync(projectDir)) { + fs.removeSync(projectDir); + } + + const argv = [`--projectDir=${projectDir}`, '--platform=win32']; + await AddCxServer.run(argv); + + const files = fs.readdirSync(projectDir); + expect(files).toContain('cx-server'); + const approuterFiles = fs.readdirSync(path.resolve(projectDir, 'cx-server')); expect(approuterFiles).toContain('cx-server'); expect(approuterFiles).toContain('cx-server.bat'); @@ -66,7 +83,6 @@ describe('Add CX Server', () => { const approuterFiles = fs.readdirSync(path.resolve(projectDir, 'cx-server')); expect(approuterFiles).toContain('cx-server'); - expect(approuterFiles).toContain('cx-server.bat'); expect(approuterFiles).toContain('server.cfg'); }, 30000);