diff --git a/generators/gae/index.js b/generators/gae/index.js index 9b57b63153de..637846066e42 100644 --- a/generators/gae/index.js +++ b/generators/gae/index.js @@ -41,7 +41,7 @@ module.exports = class extends BaseGenerator { const done = this.async(); shelljs.exec('gcloud version', { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error( "You don't have the Cloud SDK (gcloud) installed. \nDownload it from https://cloud.google.com/sdk/install" ); @@ -66,7 +66,7 @@ module.exports = class extends BaseGenerator { this.log(chalk.bold('\nInstalling App Engine Java SDK')); this.log(`... Running: gcloud components install ${component} --quiet`); shelljs.exec(`gcloud components install ${component} --quiet`, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); done( `Installation failed. \nPlease try to install the app-engine-java component manually via; gcloud components install ${component}` @@ -83,6 +83,7 @@ module.exports = class extends BaseGenerator { const configuration = this.getAllJhipsterConfig(this, true); this.env.options.appPath = configuration.get('appPath') || constants.CLIENT_MAIN_SRC_DIR; this.baseName = configuration.get('baseName'); + this.mainClass = this.getMainClassName(); this.packageName = configuration.get('packageName'); this.packageFolder = configuration.get('packageFolder'); this.cacheProvider = configuration.get('cacheProvider') || configuration.get('hibernateCache') || 'no'; @@ -107,6 +108,10 @@ module.exports = class extends BaseGenerator { this.gaeMaxInstances = configuration.get('gaeMaxInstances'); this.gaeMinInstances = configuration.get('gaeMinInstances'); this.gaeCloudSQLInstanceNeeded = configuration.get('gaeCloudSQLInstanceNeeded'); + this.CLIENT_DIST_DIR = this.getResourceBuildDirectoryForBuildTool(this.config.buildTool) + constants.CLIENT_DIST_DIR; + this.skipClient = this.config.get('skipClient'); + this.clientPackageManager = this.config.get('clientPackageManager'); + this.dasherizedBaseName = _.kebabCase(this.baseName); } }; } @@ -148,15 +153,13 @@ module.exports = class extends BaseGenerator { message: 'Google Cloud Project ID', default: this.defaultProjectId(), validate: input => { - if (input.length === 0) { + if (input.trim().length === 0) { return 'Project ID cannot empty'; } try { shelljs.exec(`gcloud projects describe ${input}`, { silent: true }); - this.gcpProjectIdExists = true; } catch (ex) { - this.gcpProjectIdExists = false; - return `Project ID "${chalk.cyan(input)}" does not exist, please create one first!`; + return `Project ID "${chalk.cyan(input.trim())}" does not exist, please create one first!`; } return true; } @@ -164,7 +167,7 @@ module.exports = class extends BaseGenerator { ]; this.prompt(prompts).then(props => { - this.gcpProjectId = props.gcpProjectId; + this.gcpProjectId = props.gcpProjectId.trim(); done(); }); }, @@ -177,7 +180,7 @@ module.exports = class extends BaseGenerator { `gcloud app describe --format="value(locationId)" --project="${this.gcpProjectId}"`, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { const prompts = [ { type: 'list', @@ -381,7 +384,7 @@ module.exports = class extends BaseGenerator { prompts.push({ type: 'input', name: 'gaeCloudSQLInstanceNeeded', - message: 'Initialize a new Cloud SQL instance (Y/N) ?', + message: 'Use a Cloud SQL instance (Y/N) ?', default: this.gaeCloudSQLInstanceNeeded ? this.gaeCloudSQLInstanceNeeded : 'Y', validate: input => { if (input !== 'Y' && input !== 'N') { @@ -408,9 +411,8 @@ module.exports = class extends BaseGenerator { const cloudSqlInstances = [{ value: '', name: 'New Cloud SQL Instance' }]; shelljs.exec( `gcloud sql instances list --format='value[separator=":"](project,region,name)' --project="${this.gcpProjectId}"`, - { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); } else { _.forEach(stdout.toString().split(os.EOL), instance => { @@ -508,7 +510,7 @@ module.exports = class extends BaseGenerator { `gcloud sql databases list -i ${name} --format='value(name)' --project="${this.gcpProjectId}"`, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); } else { _.forEach(stdout.toString().split(os.EOL), database => { @@ -581,7 +583,7 @@ module.exports = class extends BaseGenerator { `gcloud app create --region="${this.gaeLocation}" --project="${this.gcpProjectId}"`, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); this.abort = true; } @@ -612,15 +614,16 @@ module.exports = class extends BaseGenerator { this.log(chalk.bold(`\n... Running: ${cmd}`)); shelljs.exec(cmd, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.abort = true; this.log.error(err); } - this.gcpCloudSqlInstanceName = shelljs.exec( + const cloudSQLInstanceName = shelljs.exec( `gcloud sql instances describe ${name} --format="value(connectionName)" --project="${this.gcpProjectId}"`, { silent: true } ); + this.gcpCloudSqlInstanceName = cloudSQLInstanceName.trim(); done(); }); @@ -648,7 +651,7 @@ module.exports = class extends BaseGenerator { const cmd = `gcloud sql users create "${this.gcpCloudSqlUserName}" -i "${name}" --host="%" --password="${this.gcpCloudSqlPassword}" --project="${this.gcpProjectId}"`; this.log(chalk.bold(`... Running: ${cmd}`)); shelljs.exec(cmd, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); } done(); @@ -670,7 +673,7 @@ module.exports = class extends BaseGenerator { const cmd = `gcloud sql databases create "${this.gcpCloudSqlDatabaseName}" --charset=utf8 -i "${name}" --project="${this.gcpProjectId}"`; this.log(chalk.bold(`... Running: ${cmd}`)); shelljs.exec(cmd, { silent: true }, (code, stdout, err) => { - if (err) { + if (err && code !== 0) { this.log.error(err); } done(); @@ -705,9 +708,7 @@ module.exports = class extends BaseGenerator { this.log(chalk.bold('\nCreating Google App Engine deployment files')); this.template('app.yaml.ejs', `${constants.MAIN_DIR}/appengine/app.yaml`); - if (this.gaeCloudSQLInstanceNeeded === 'Y') { - this.template('application-prod-gae.yml.ejs', `${constants.SERVER_MAIN_RES_DIR}/config/application-prod-gae.yml`); - } + this.template('application-prod-gae.yml.ejs', `${constants.SERVER_MAIN_RES_DIR}/config/application-prod-gae.yml`); if (this.buildTool === 'gradle') { this.template('gae.gradle.ejs', 'gradle/gae.gradle'); } @@ -719,6 +720,9 @@ module.exports = class extends BaseGenerator { addDependencies() { if (this.abort) return; + if (this.buildTool === 'maven') { + this.addMavenDependency('org.springframework.boot.experimental', 'spring-boot-thin-layout', '1.0.23.RELEASE'); + } if (this.gaeCloudSQLInstanceNeeded === 'N') return; if (this.prodDatabaseType === 'mysql' || this.prodDatabaseType === 'mariadb') { if (this.buildTool === 'maven') { @@ -739,9 +743,8 @@ module.exports = class extends BaseGenerator { addGradlePlugin() { if (this.abort) return; if (this.buildTool === 'gradle') { - if (this.gaeCloudSQLInstanceNeeded === 'Y') { - this.addGradlePlugin('com.google.cloud.tools', 'appengine-gradle-plugin', '2.1.0'); - } + this.addGradlePlugin('com.google.cloud.tools', 'appengine-gradle-plugin', '2.2.0'); + this.addGradlePlugin('org.springframework.boot.experimental', 'spring-boot-thin-gradle-plugin', '1.0.13.RELEASE'); this.applyFromGradleScript('gradle/gae'); } }, @@ -749,14 +752,15 @@ module.exports = class extends BaseGenerator { addMavenPlugin() { if (this.abort) return; if (this.buildTool === 'maven') { - if (this.gaeCloudSQLInstanceNeeded === 'Y') { - this.render('pom-plugin.xml.ejs', rendered => { - this.addMavenPlugin('com.google.cloud.tools', 'appengine-maven-plugin', '2.1.0', rendered.trim()); - }); - } + this.render('pom-plugin.xml.ejs', rendered => { + this.addMavenPlugin('com.google.cloud.tools', 'appengine-maven-plugin', '2.2.0', rendered.trim()); + }); this.render('pom-profile.xml.ejs', rendered => { this.addMavenProfile('prod-gae', ` ${rendered.trim()}`); }); + this.render('pom-gae-build-profile.xml.ejs', rendered => { + this.addMavenProfile('gae', ` ${rendered.trim()}`); + }); } } }; @@ -769,10 +773,16 @@ module.exports = class extends BaseGenerator { if (this.buildTool === 'maven') { this.log(chalk.bold('\nRun App Engine DevServer Locally: ./mvnw package appengine:run -DskipTests')); - this.log(chalk.bold('Deploy to App Engine: ./mvnw package appengine:deploy -DskipTests -Pprod,prod-gae')); + this.log( + chalk.bold('Deploy to App Engine: ./mvnw clean && ./mvnw package appengine:deploy -DskipTests -Pgae,prod-gae') + ); } else if (this.buildTool === 'gradle') { this.log(chalk.bold('\nRun App Engine DevServer Locally: ./gradlew appengineRun')); - this.log(chalk.bold('Deploy to App Engine: ./gradlew appengineDeploy -Pprod -Pprod-gae')); + this.log( + chalk.bold( + 'Deploy to App Engine: ./gradlew thinResolve -Pgae -Pprod-gae && ./gradlew appengineDeploy -Pgae -Pprod-gae' + ) + ); } /* if (this.gcpSkipBuild || this.gcpDeployType === 'git') { diff --git a/generators/gae/templates/app.yaml.ejs b/generators/gae/templates/app.yaml.ejs index f08370907fc5..1da7e840e39d 100644 --- a/generators/gae/templates/app.yaml.ejs +++ b/generators/gae/templates/app.yaml.ejs @@ -1,3 +1,21 @@ +<%# + Copyright 2013-2019 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> # ======================================================================== # App Engine Configuration. # Full reference is available at: @@ -28,3 +46,4 @@ automatic_scaling: max_instances: <%= gaeMaxInstances %> <%_ } _%> <%_ } _%> +entrypoint: java -Dthin.root=. -jar <%= dasherizedBaseName %>-0.0.1-SNAPSHOT.jar diff --git a/generators/gae/templates/application-prod-gae.yml.ejs b/generators/gae/templates/application-prod-gae.yml.ejs index cf68e4a59dd3..f972ee7179ed 100644 --- a/generators/gae/templates/application-prod-gae.yml.ejs +++ b/generators/gae/templates/application-prod-gae.yml.ejs @@ -27,9 +27,11 @@ # Full reference is available at: # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # =================================================================== - +server: + port: ${PORT} +<%_ if (gaeCloudSQLInstanceNeeded === 'Y') { _%> spring: -<%_ if (prodDatabaseType === 'mysql' || prodDatabaseType === 'mariadb') { _%> + <%_ if (prodDatabaseType === 'mysql' || prodDatabaseType === 'mariadb') { _%> datasource: type: com.zaxxer.hikari.HikariDataSource url: "jdbc:mysql://google/<%=gcpCloudSqlDatabaseName%>?cloudSqlInstance=<%=gcpCloudSqlInstanceName%>&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useSSL=false" @@ -37,8 +39,8 @@ spring: password: "<%=gcpCloudSqlPassword%>" hikari: maximumPoolSize: 8 -<%_ } _%> -<%_ if (prodDatabaseType === 'postgresql') { _%> + <%_ } _%> + <%_ if (prodDatabaseType === 'postgresql') { _%> datasource: type: com.zaxxer.hikari.HikariDataSource url: "jdbc:postgresql://google/<%=gcpCloudSqlDatabaseName%>?cloudSqlInstance=<%=gcpCloudSqlInstanceName%>&socketFactory=com.google.cloud.sql.postgres.SocketFactory" @@ -46,4 +48,5 @@ spring: password: "<%=gcpCloudSqlPassword%>" hikari: maximumPoolSize: 8 + <%_ } _%> <%_ } _%> diff --git a/generators/gae/templates/gae.gradle.ejs b/generators/gae/templates/gae.gradle.ejs index 401cab3dc5fe..60b7a61d73ac 100644 --- a/generators/gae/templates/gae.gradle.ejs +++ b/generators/gae/templates/gae.gradle.ejs @@ -20,15 +20,11 @@ apply plugin: "com.google.cloud.tools.appengine" appengine { deploy { - project = "<%=gcpProjectId%>" + projectId = "<%=gcpProjectId%>" version = "1" } } -bootWar { - webXml = file("${project.rootDir}/<%= CLIENT_MAIN_SRC_DIR %>WEB-INF/web.xml") -} - processResources { if (project.hasProperty("prod-gae")) { filesMatching("**/application.yml") { @@ -38,3 +34,10 @@ processResources { } } } + +task copyFolders(type: Copy) { + into "$buildDir/staged-app/" + from "$buildDir/thin/root/" +} + +appengineStage.finalizedBy copyFolders diff --git a/generators/gae/templates/pom-gae-build-profile.xml.ejs b/generators/gae/templates/pom-gae-build-profile.xml.ejs new file mode 100644 index 000000000000..1157c7b34e02 --- /dev/null +++ b/generators/gae/templates/pom-gae-build-profile.xml.ejs @@ -0,0 +1,61 @@ +<%# + Copyright 2013-2019 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.springframework.boot.experimental + spring-boot-thin-layout + 1.0.23.RELEASE + + + + + org.springframework.boot.experimental + spring-boot-thin-maven-plugin + 1.0.23.RELEASE + + ${project.build.directory}/appengine-staging + + + + resolve + + resolve + + false + + + + + pl.project13.maven + git-commit-id-plugin + + + diff --git a/generators/gae/templates/pom-plugin.xml.ejs b/generators/gae/templates/pom-plugin.xml.ejs index 3f0c8cabafd5..04194a83be3e 100644 --- a/generators/gae/templates/pom-plugin.xml.ejs +++ b/generators/gae/templates/pom-plugin.xml.ejs @@ -1,4 +1,22 @@ +<%# + Copyright 2013-2019 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> - <%=gcpProjectId%> + <%=gcpProjectId%> 1 diff --git a/generators/gae/templates/pom-profile.xml.ejs b/generators/gae/templates/pom-profile.xml.ejs index 3712dda60863..27e277d8b38f 100644 --- a/generators/gae/templates/pom-profile.xml.ejs +++ b/generators/gae/templates/pom-profile.xml.ejs @@ -1,3 +1,21 @@ +<%# + Copyright 2013-2019 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> - prod${profile.swagger}${profile.no-liquibase},prod-gae + prod${profile.swagger}<%_ if (databaseType === 'sql') { _%>${profile.no-liquibase},prod-gae<%_ } _%> diff --git a/generators/server/templates/build.gradle.ejs b/generators/server/templates/build.gradle.ejs index ff011d7957e1..3d752192ca6c 100644 --- a/generators/server/templates/build.gradle.ejs +++ b/generators/server/templates/build.gradle.ejs @@ -71,7 +71,7 @@ apply from: "gradle/swagger.gradle" <%_ } _%> //jhipster-needle-gradle-apply-from - JHipster will add additional gradle scripts to be applied here -if (project.hasProperty("prod")) { +if (project.hasProperty("prod") || project.hasProperty("gae")) { apply from: "gradle/profile_prod.gradle" } else { apply from: "gradle/profile_dev.gradle" @@ -81,6 +81,19 @@ if (project.hasProperty("war")) { apply from: "gradle/war.gradle" } +if (project.hasProperty("gae")) { + apply plugin: 'maven' + apply plugin: 'org.springframework.boot.experimental.thin-launcher' + apply plugin: 'io.spring.dependency-management' + + dependencyManagement { + imports { + mavenBom 'io.github.jhipster:jhipster-dependencies:${jhipster_dependencies_version}' + } + } + appengineStage.dependsOn thinResolve +} + <%_ if (serviceDiscoveryType || applicationType === 'gateway' || applicationType === 'microservice' || applicationType === 'uaa') { _%> if (project.hasProperty("zipkin")) { apply from: "gradle/zipkin.gradle" @@ -260,7 +273,9 @@ repositories { dependencies { // import JHipster dependencies BOM - implementation platform("io.github.jhipster:jhipster-dependencies:${jhipster_dependencies_version}" ) + if (!project.hasProperty("gae")) { + implementation platform("io.github.jhipster:jhipster-dependencies:${jhipster_dependencies_version}") + } // Use ", version: jhipster_dependencies_version, changing: true" if you want // to use a SNAPSHOT release instead of a stable release @@ -437,8 +452,13 @@ dependencies { <%_ } _%> <%_ if (authenticationType === 'jwt') { _%> implementation "io.jsonwebtoken:jjwt-api" - runtimeOnly "io.jsonwebtoken:jjwt-impl" - runtimeOnly "io.jsonwebtoken:jjwt-jackson" + if (!project.hasProperty("gae")) { + runtimeOnly "io.jsonwebtoken:jjwt-impl" + runtimeOnly "io.jsonwebtoken:jjwt-jackson" + } else { + implementation "io.jsonwebtoken:jjwt-impl" + implementation "io.jsonwebtoken:jjwt-jackson" + } <%_ } _%> <%_ if (authenticationType === 'uaa') { _%> implementation "org.springframework.security.oauth:spring-security-oauth2" @@ -536,6 +556,18 @@ dependencies { //jhipster-needle-gradle-dependency - JHipster will add additional dependencies here } +if (project.hasProperty("gae")) { + task createPom { + def basePath = 'build/resources/main/META-INF/maven' + doLast { + pom { + withXml(dependencyManagement.pomConfigurer) + }.writeTo("${basePath}/${project.group}/${project.name}/pom.xml") + } + } + bootJar.dependsOn = [createPom] +} + task cleanResources(type: Delete) { delete "build/resources" }