diff --git a/.gitignore b/.gitignore index 0e251b8..c3436b1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ build/ .idea/ *.iml -examples/ \ No newline at end of file +examples/ + +# This file is automatically generated by Gradle +src/main/resources/heroku-gradle.properties diff --git a/README.md b/README.md index 78fa087..70dda02 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Heroku Gradle Plugin [![Build Status](https://travis-ci.org/heroku/heroku-gradle.svg?branch=master)](https://travis-ci.org/heroku/heroku-gradle) [ ![Download](https://api.bintray.com/packages/heroku/maven/gradle/images/download.svg) ](https://bintray.com/heroku/maven/gradle/_latestVersion) +# Heroku Gradle Plugin [![Build Status](https://travis-ci.com/heroku/heroku-gradle.svg?branch=master)](https://travis-ci.com/heroku/heroku-gradle) This plugin is used to deploy Gradle based JVM applications directly to Heroku without pushing to a Git repository. This can be useful when deploying from a CI server. @@ -8,11 +8,11 @@ Add the plugin to your `build.gradle`: ``` plugins { - id "com.heroku.sdk.heroku-gradle" version "1.0.4" + id "com.heroku.sdk.heroku-gradle" version "2.0.0" } ``` -Create a Heroku app using the [Heroku CLI](https://toolbelt.heroku.com): +Create a Heroku app using the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli): ``` $ heroku create @@ -67,7 +67,7 @@ You can explicitly define the required jdk version (in system.properties) ``` heroku { - jdkVersion = 12 + jdkVersion = 11 } ``` diff --git a/build.gradle b/build.gradle index b0648a0..11c6472 100644 --- a/build.gradle +++ b/build.gradle @@ -1,93 +1,52 @@ plugins { - id 'com.gradle.plugin-publish' version '0.10.0' - id 'com.jfrog.bintray' version '1.8.4' - - id 'eclipse' - id 'groovy' - id 'idea' - id 'maven-publish' + id 'groovy' + id 'java-gradle-plugin' + id "com.gradle.plugin-publish" version "0.12.0" } -task createClasspathManifest { - def outputDir = file("$buildDir/$name") - - inputs.files sourceSets.main.runtimeClasspath - outputs.dir outputDir +version '2.0.0-SNAPSHOT' - doLast { - outputDir.mkdirs() - file("$outputDir/plugin-classpath.txt").text = sourceSets.main.runtimeClasspath.join("\n") - } +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } repositories { - jcenter() - mavenCentral() + jcenter() + mavenCentral() } dependencies { - compile 'com.heroku.sdk:heroku-deploy:2.0.6' - compile 'com.google.guava:guava:18.0' - compile gradleApi() - - testCompile gradleTestKit() - testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { - exclude module: 'groovy-all' - } - testCompile files(createClasspathManifest) + implementation 'com.heroku.sdk:heroku-deploy:3.0.3' + testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5' } -group = 'com.heroku.sdk' -version = "1.0.5-SNAPSHOT" - -task sourceJar(type: Jar) { - from sourceSets.main.allSource - classifier 'sources' -} - -publishing { - publications { - bintray(MavenPublication) { - from components.java - artifact(sourceJar) - artifact(publishPluginJavaDocsJar) +gradlePlugin { + plugins { + herokuGradle { + id = 'com.heroku.sdk.heroku-gradle' + implementationClass = 'com.heroku.sdk.HerokuGradlePlugin' + } } - } -} - -bintray { - user = System.env.BINTRAY_USER - key = System.env.BINTRAY_KEY - publish = true - pkg { - repo = 'maven' - name = 'gradle' - userOrg = 'heroku' - licenses = ['MIT'] - publications = ['bintray'] - } -} - -bintrayUpload.dependsOn 'generatePomFileForBintrayPublication', 'sourceJar', 'build' - -bintrayUpload.onlyIf { - System.env.BINTRAY_USER && System.env.BINTRAY_KEY && project.version ==~ /\d+\.\d+\.\d+/ } pluginBundle { - website = 'https://github.com/heroku/heroku-gradle' - vcsUrl = 'https://github.com/heroku/heroku-gradle' - description = 'A Gradle plugin for deploying to Heroku.' - tags = ['heroku', 'deployment'] - - plugins { - herokuPlugin { - id = 'com.heroku.sdk.heroku-gradle' - displayName = 'Heroku Gradle' + website = 'https://github.com/heroku/heroku-gradle' + vcsUrl = 'https://github.com/heroku/heroku-gradle.git' + description = 'A Gradle plugin for deploying to Heroku.' + tags = ['heroku', 'deployment'] + + plugins { + herokuGradle { + displayName = 'Heroku Gradle' + } } - } } -publishPlugins.onlyIf { - project.version ==~ /\d+\.\d+\.\d+/ +// To determine the plugin version at runtime, we package a properties file with the current version string: +task writeVersionPropertiesFile(type: WriteProperties) { + outputFile = file('src/main/resources/heroku-gradle.properties') + property 'version', project.version } + +assemble.dependsOn writeVersionPropertiesFile diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 27768f1..62d4c05 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 949819d..8ef6974 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon Jul 20 17:02:45 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index cccdd3d..fbd7c51 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d..a9f778a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -65,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/release.sh b/release.sh deleted file mode 100644 index ade0867..0000000 --- a/release.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail -set -eu - -if cat build.gradle | grep -q '^version = .*-SNAPSHOT'; then - echo "ERROR ! Must set version to non-snapshot first." - exit 1 -fi - -get_property() { - local propFile=$1 - local propName=$2 - local propDefault=${3:-""} - - if [ -f ${propFile} ]; then - local propValue=$(sed '/^\#/d' ${propFile} | grep "${propName}" | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - echo "${propValue:-$propDefault}" - else - echo "${propDefault}" - fi -} - -export BINTRAY_USER="$(get_property ~/.bintray/.credentials user)" -export BINTRAY_KEY="$(get_property ~/.bintray/.credentials password)" - -./gradlew clean build bintrayUpload publishPlugins -i -x groovydoc diff --git a/src/main/groovy/com/heroku/sdk/gradle/DeployHerokuTask.groovy b/src/main/groovy/com/heroku/sdk/gradle/DeployHerokuTask.groovy deleted file mode 100644 index f942159..0000000 --- a/src/main/groovy/com/heroku/sdk/gradle/DeployHerokuTask.groovy +++ /dev/null @@ -1,46 +0,0 @@ -package com.heroku.sdk.gradle - -import javax.inject.Inject - -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction - -/** - * Gradle Task that deploys Gradle based JVM applications directly to Heroku - * without pushing to a Git repository. - */ -class DeployHerokuTask extends DefaultTask { - - /** - * The heroku {} block in the build script. - */ - HerokuPluginExtension ext; - - @Inject - DeployHerokuTask(HerokuPluginExtension ext) { - description = "Deploys JVM web-application directly to Heroku without pushing to a Git repository." - group = "Heroku" - this.ext = ext - } - - @TaskAction - void deploy() { - File includeRootDir = project.heroku.includeRootDir ?: project.rootDir - List files = ext.getIncludedFiles(includeRootDir) - if (project.heroku.includeBuildDir) files << project.buildDir - - GradleApp app = new GradleApp( - (String) project.heroku.appName, - includeRootDir, - (File) project.buildDir, - (List) project.heroku.buildpacks, - project.logger) - - app.deploy( - (List) files, - (Map) project.heroku.configVars, - (String) project.heroku.jdkVersion, - (Map) project.heroku.processTypes, - (String) project.heroku.slugFilename) - } -} diff --git a/src/main/groovy/com/heroku/sdk/gradle/GradleApp.groovy b/src/main/groovy/com/heroku/sdk/gradle/GradleApp.groovy deleted file mode 100644 index 3bd76cc..0000000 --- a/src/main/groovy/com/heroku/sdk/gradle/GradleApp.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package com.heroku.sdk.gradle - -import com.heroku.sdk.deploy.App -import org.slf4j.Logger - -class GradleApp extends App { - - Logger log - - GradleApp(String appName, File rootDir, File targetDir, List buildpacks, Logger logger) { - super("heroku-gradle", appName, rootDir, targetDir, buildpacks) - this.log = logger - } - - @Override - void logInfo(String message) { - log.quiet(message) - } - - @Override - void logDebug(String message) { - log.debug(message) - } - - @Override - void logWarn(String message) { - log.warn(message) - } -} diff --git a/src/main/groovy/com/heroku/sdk/gradle/HerokuPlugin.groovy b/src/main/groovy/com/heroku/sdk/gradle/HerokuPlugin.groovy deleted file mode 100644 index 78364f0..0000000 --- a/src/main/groovy/com/heroku/sdk/gradle/HerokuPlugin.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.heroku.sdk.gradle - -import org.gradle.api.Plugin -import org.gradle.api.Project - -class HerokuPlugin implements Plugin { - @Override - void apply(Project project) { - HerokuPluginExtension ext = project.extensions.create('heroku', HerokuPluginExtension) - - project.afterEvaluate { - ext.resolvePathsAndValidate() - } - - project.tasks.create('deployHeroku', DeployHerokuTask.class, ext) - } -} diff --git a/src/main/groovy/com/heroku/sdk/gradle/HerokuPluginExtension.groovy b/src/main/groovy/com/heroku/sdk/gradle/HerokuPluginExtension.groovy deleted file mode 100644 index c04b750..0000000 --- a/src/main/groovy/com/heroku/sdk/gradle/HerokuPluginExtension.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package com.heroku.sdk.gradle - -import org.apache.commons.io.FileUtils -import org.apache.commons.io.filefilter.WildcardFileFilter - -import java.util.regex.Pattern - -class HerokuPluginExtension { - - String appName = System.getProperty("heroku.appName", null) - - String jdkVersion - - String slugFilename = "build.tgz" - - Boolean includeBuildDir = true - - Map processTypes = [:] - - Map configVars = [:] - - List includes = [] - - List buildpacks = [] - - // All files will have this path stripped from their path in the slug - File includeRootDir - - List getIncludedFiles(File gradleDir) { - List files = new ArrayList(includes.size()) - - for (String s : includes) { - if (s.contains("*")) { - String[] dirs = s.split(Pattern.quote(File.separator)) - String pattern = dirs[dirs.length-1] - File basedir = new File(gradleDir, s.replace(pattern, "")) - Collection listFiles = FileUtils.listFiles(basedir, new WildcardFileFilter(pattern), null) - files.addAll(listFiles) - } else { - files.add(new File(s)) - } - } - - return files - } - - void resolvePathsAndValidate() { - //Preconditions.checkArgument(!Strings.isNullOrEmpty(appName), "heroku.appName is required.") - } -} diff --git a/src/main/java/com/heroku/sdk/GradleOutputAdapter.java b/src/main/java/com/heroku/sdk/GradleOutputAdapter.java new file mode 100644 index 0000000..d5d9a89 --- /dev/null +++ b/src/main/java/com/heroku/sdk/GradleOutputAdapter.java @@ -0,0 +1,40 @@ +package com.heroku.sdk; + +import com.heroku.sdk.deploy.lib.OutputAdapter; +import org.gradle.api.logging.Logger; + +public class GradleOutputAdapter implements OutputAdapter { + private Logger logger; + + public GradleOutputAdapter(Logger logger) { + this.logger = logger; + } + + @Override + public void logInfo(String message) { + // By default, Gradle does not show INFO log messages to the user. heroku-deploy assumes that the INFO log + // level is visible to the user and outputs deployment status information at that log level. This translates + // to Gradle's LIFECYCLE log level and is therefore used here instead. + logger.lifecycle(message); + } + + @Override + public void logDebug(String message) { + logger.debug(message); + } + + @Override + public void logWarn(String message) { + logger.warn(message); + } + + @Override + public void logError(String message) { + logger.error(message); + } + + @Override + public void logUploadProgress(long uploaded, long contentLength) { + logger.debug("Upload progress: {} bytes out of {}", uploaded, contentLength); + } +} diff --git a/src/main/java/com/heroku/sdk/HerokuExtension.java b/src/main/java/com/heroku/sdk/HerokuExtension.java new file mode 100644 index 0000000..5b93538 --- /dev/null +++ b/src/main/java/com/heroku/sdk/HerokuExtension.java @@ -0,0 +1,69 @@ +package com.heroku.sdk; + +import java.util.*; + +public class HerokuExtension { + private boolean includeBuildDir = true; + private List includes = new ArrayList<>(); + private String appName; + private String jdkVersion; + private Map processTypes = new HashMap<>(); + private Map configVars = new HashMap<>(); + private List buildpacks = Collections.singletonList("heroku/jvm"); + + public boolean isIncludeBuildDir() { + return includeBuildDir; + } + + public void setIncludeBuildDir(boolean includeBuildDir) { + this.includeBuildDir = includeBuildDir; + } + + public List getIncludes() { + return Collections.unmodifiableList(includes); + } + + public void setIncludes(List includes) { + this.includes = includes; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getJdkVersion() { + return jdkVersion; + } + + public void setJdkVersion(String jdkVersion) { + this.jdkVersion = jdkVersion; + } + + public Map getProcessTypes() { + return Collections.unmodifiableMap(processTypes); + } + + public void setProcessTypes(Map processTypes) { + this.processTypes = processTypes; + } + + public Map getConfigVars() { + return Collections.unmodifiableMap(configVars); + } + + public void setConfigVars(Map configVars) { + this.configVars = configVars; + } + + public List getBuildpacks() { + return Collections.unmodifiableList(buildpacks); + } + + public void setBuildpacks(List buildpacks) { + this.buildpacks = buildpacks; + } +} diff --git a/src/main/java/com/heroku/sdk/HerokuGradlePlugin.java b/src/main/java/com/heroku/sdk/HerokuGradlePlugin.java new file mode 100644 index 0000000..eb36ef2 --- /dev/null +++ b/src/main/java/com/heroku/sdk/HerokuGradlePlugin.java @@ -0,0 +1,112 @@ +package com.heroku.sdk; + +import com.heroku.sdk.deploy.lib.OutputAdapter; +import com.heroku.sdk.deploy.lib.deploymemt.Deployer; +import com.heroku.sdk.deploy.lib.deploymemt.DeploymentDescriptor; +import com.heroku.sdk.deploy.lib.resolver.ApiKeyResolver; +import com.heroku.sdk.deploy.lib.resolver.AppNameResolver; +import com.heroku.sdk.deploy.lib.sourceblob.JvmProjectSourceBlobCreator; +import com.heroku.sdk.deploy.lib.sourceblob.SourceBlobDescriptor; +import com.heroku.sdk.deploy.lib.sourceblob.SourceBlobPackager; +import com.heroku.sdk.deploy.util.Procfile; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskExecutionException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class HerokuGradlePlugin implements Plugin { + + public void apply(Project project) { + project.getExtensions().add("heroku", HerokuExtension.class); + + project.getTasks().register("deployHeroku", task -> { + task.setDescription("Deploys JVM web-application directly to Heroku without pushing to a Git repository."); + task.setGroup("Heroku"); + + HerokuExtension herokuExtension = project.getExtensions().getByType(HerokuExtension.class); + + List includedPaths = herokuExtension + .getIncludes() + .stream() + .map(Paths::get) + .collect(Collectors.toList()); + + if (herokuExtension.isIncludeBuildDir()) { + includedPaths.add(Paths.get("build/")); + } + + task.doLast(s -> { + Path projectRootDirPath = project.getRootDir().toPath(); + + try { + OutputAdapter outputAdapter = new GradleOutputAdapter(project.getLogger()); + + Supplier> customJdkResolver = + () -> Optional.ofNullable(herokuExtension.getJdkVersion()); + + Supplier customProcfileResolver = + () -> new Procfile(herokuExtension.getProcessTypes()); + + Supplier> customAppNameResolver = + () -> Optional.ofNullable(herokuExtension.getAppName()); + + SourceBlobDescriptor sourceBlobDescriptor = JvmProjectSourceBlobCreator.create( + projectRootDirPath, + "heroku-gradle", + includedPaths, + customProcfileResolver, + Procfile.empty(), + customJdkResolver, + outputAdapter); + + Path sourceBlobPath = SourceBlobPackager.pack(sourceBlobDescriptor, outputAdapter); + + String appName = AppNameResolver + .resolve(projectRootDirPath, customAppNameResolver) + .orElseThrow(() -> new IllegalArgumentException("Could not resolve app name!")); + + String apiKey = ApiKeyResolver + .resolve(projectRootDirPath) + .orElseThrow(() -> new IllegalArgumentException("Could not resolve API key.")); + + String deployedVersionString = project.getVersion().toString(); + + DeploymentDescriptor deploymentDescriptor + = new DeploymentDescriptor(appName, herokuExtension.getBuildpacks(), herokuExtension.getConfigVars(), sourceBlobPath, deployedVersionString); + + boolean deployResult = Deployer.deploy(apiKey, "heroku-gradle", getPluginVersion(), deploymentDescriptor, outputAdapter); + + if (!deployResult) { + // heroku-deploy reports errors directly to the user using the OutputAdapter. We have to throw + // this exception here to mark this task as failed. + throw new TaskExecutionException(task, new RuntimeException("Deployment failed.")); + } + } catch (IOException | InterruptedException e) { + throw new TaskExecutionException(task, e); + } + }); + }); + } + + private static String getPluginVersion() throws IOException { + String filename = "heroku-gradle.properties"; + InputStream propertiesInputStream = HerokuGradlePlugin.class.getClassLoader().getResourceAsStream(filename); + + if (propertiesInputStream != null) { + Properties properties = new Properties(); + properties.load(propertiesInputStream); + return properties.getProperty("version"); + } + + return "unknown"; + } +} diff --git a/src/main/resources/META-INF/gradle-plugins/com.heroku.sdk.heroku-gradle.properties b/src/main/resources/META-INF/gradle-plugins/com.heroku.sdk.heroku-gradle.properties deleted file mode 100644 index 6c9b974..0000000 --- a/src/main/resources/META-INF/gradle-plugins/com.heroku.sdk.heroku-gradle.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class = com.heroku.sdk.gradle.HerokuPlugin \ No newline at end of file diff --git a/src/test/groovy/com/heroku/sdk/gradle/HerokuPluginTest.groovy b/src/test/groovy/com/heroku/sdk/gradle/HerokuPluginTest.groovy index 0b419ff..707e822 100644 --- a/src/test/groovy/com/heroku/sdk/gradle/HerokuPluginTest.groovy +++ b/src/test/groovy/com/heroku/sdk/gradle/HerokuPluginTest.groovy @@ -16,11 +16,10 @@ class HerokuPluginTest extends Specification { File projectDir File buildFile String appName - List pluginClasspath GradleRunner with(String... tasks) { GradleRunner.create() - .withPluginClasspath(pluginClasspath) + .withPluginClasspath() .withProjectDir(projectDir) .withArguments(tasks) } @@ -44,31 +43,23 @@ class HerokuPluginTest extends Specification { def setup() { projectDir = temporaryFolder.root buildFile = temporaryFolder.newFile('build.gradle') + temporaryFolder.newFile("README.md") File buildDir = temporaryFolder.newFolder('build') FileUtils.copyFile( - new File("src/test/resources/sample-jar.jar"), - new File(buildDir, "sample-jar.jar")) + new File("src/test/resources/sample-jar.jar"), + new File(buildDir, "sample-jar.jar")) appName = "gradle-test-" + UUID.randomUUID().toString().substring(0,12); if (execCond("heroku create -n ${appName}")) { - println("Created ${appName}") + println("Created ${appName}") } else { - throw RuntimeException("Failed to create app: ${appName}"); + throw RuntimeException("Failed to create app: ${appName}"); } - - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines() - .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths - .collect { new File(it) } } def cleanup() { - println(exec("heroku destroy ${appName} --confirm ${appName}")) + println(exec("heroku destroy ${appName} --confirm ${appName}")) } def 'fail when missing app name'() { @@ -83,7 +74,7 @@ class HerokuPluginTest extends Specification { BuildResult buildResult = with('deployHeroku').buildAndFail() then: - buildResult.output.contains("Could not find app name: Git repo not found.") + buildResult.output.contains("Could not resolve app name!") } def 'fail when app does not exist'() { @@ -105,7 +96,7 @@ class HerokuPluginTest extends Specification { BuildResult buildResult = with('deployHeroku').buildAndFail() then: - buildResult.output.contains("Couldn't find that app.") + buildResult.output.contains("Could not find application! Make sure you configured your application name correctly.") } def 'success on happy path'() { @@ -128,12 +119,16 @@ class HerokuPluginTest extends Specification { then: buildResult.task(':deployHeroku').outcome == TaskOutcome.SUCCESS - buildResult.output.contains("app: ${ appName }") + buildResult.output.contains(appName) buildResult.output.contains("including: build/") buildResult.output.contains("- success") buildResult.output.contains("Installing JDK 1.8") buildResult.output.contains("Done") + // Deployment is not immediate, it takes a short amount of time to take effect. + // To alleviate the chance of flappy tests, we generously sleep here. + sleep(5000) + exec("curl -L http://${appName}.herokuapp.com").contains("Hello from Java!") } @@ -157,13 +152,17 @@ class HerokuPluginTest extends Specification { then: buildResult.task(':deployHeroku').outcome == TaskOutcome.SUCCESS - buildResult.output.contains("app: ${ appName }") + buildResult.output.contains(appName) buildResult.output.contains("including: build/") buildResult.output.contains("- success") buildResult.output.contains("Installing JDK 1.8") !buildResult.output.contains("No processTypes specified!") buildResult.output.contains("Done") + // Deployment is not immediate, it takes a short amount of time to take effect. + // To alleviate the chance of flappy tests, we generously sleep here. + sleep(5000) + exec("curl -L http://${appName}.herokuapp.com").contains("Hello from Java!") } @@ -185,12 +184,12 @@ class HerokuPluginTest extends Specification { then: buildResult.task(':deployHeroku').outcome == TaskOutcome.SUCCESS - buildResult.output.contains("app: ${ appName }") + buildResult.output.contains(appName) buildResult.output.contains("including: build/") buildResult.output.contains("- success") buildResult.output.contains("Installing JDK 1.8") buildResult.output.contains("Done") - exec("heroku run cat ${exec("pwd").trim()}README.md -a ${appName}").contains("README.md") + exec("heroku run ls -a ${appName}").contains("README.md") } }