diff --git a/.gitignore b/.gitignore index f841722..34ab755 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /build/ *.ipr *.iws +out diff --git a/README.md b/README.md index 3b6dcdc..17211c4 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,24 @@ miniconda { If you need to customize where the Miniconda installer script is downloaded from, you can add your artifact to the `minicondaInstaller` configuration. The default location where it is downloaded from is: [`https://repo.continuum.io`](https://repo.continuum.io). +Tasks +----- + +### bootstrapPython +Installs Miniconda. + +### setupPython +Sets up conda environment in the project running `conda create`. + +### condaBuildSetup +Installs conda-build using `conda install conda-build`. + +### condaBuildCheck +Only checks (validates) the recipe. + +### condaBuild +Builds and packages the project using `conda build`. + License ------- diff --git a/build.gradle b/build.gradle index 38b86fb..80dfe9b 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,8 @@ dependencies { testCompile('com.netflix.nebula:nebula-test:4.2.2') { exclude group: 'org.codehaus.groovy' } + + testCompile 'com.google.code.findbugs:annotations:3.0.1' } pluginBundle { @@ -68,8 +70,3 @@ pluginBundle { } } } - -test { - maxParallelForks = 10 - forkEvery = 1 -} diff --git a/circle.yml b/circle.yml index 42f4b90..6625335 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: java: - version: oraclejdk7 + version: oraclejdk8 environment: TERM: dumb @@ -14,7 +14,7 @@ dependencies: test: override: - - ./gradlew build + - ./gradlew build --info post: - mkdir -p "$CIRCLE_TEST_REPORTS/junit/" - cp build/test-results/test/*.xml "$CIRCLE_TEST_REPORTS/junit/" diff --git a/src/main/java/com/palantir/python/miniconda/AfterEvaluateAction.java b/src/main/java/com/palantir/python/miniconda/AfterEvaluateAction.java index 426616f..d7f56dd 100644 --- a/src/main/java/com/palantir/python/miniconda/AfterEvaluateAction.java +++ b/src/main/java/com/palantir/python/miniconda/AfterEvaluateAction.java @@ -17,6 +17,9 @@ package com.palantir.python.miniconda; import com.palantir.python.miniconda.tasks.BootstrapPython; +import com.palantir.python.miniconda.tasks.CondaBuild; +import com.palantir.python.miniconda.tasks.CondaBuildCheck; +import com.palantir.python.miniconda.tasks.SetupCondaBuild; import com.palantir.python.miniconda.tasks.SetupPython; import org.gradle.api.Action; import org.gradle.api.Project; @@ -35,16 +38,25 @@ public class AfterEvaluateAction implements Action { private final Configuration configuration; private final BootstrapPython bootstrapPython; private final SetupPython setupPython; + private final SetupCondaBuild setupCondaBuild; + private final CondaBuildCheck condaBuildCheck; + private final CondaBuild condaBuild; public AfterEvaluateAction( OperatingSystem os, Configuration configuration, BootstrapPython bootstrapPython, - SetupPython setupPython) { + SetupPython setupPython, + SetupCondaBuild setupCondaBuild, + CondaBuildCheck condaBuildCheck, + CondaBuild condaBuild) { this.os = os; this.configuration = configuration; this.bootstrapPython = bootstrapPython; this.setupPython = setupPython; + this.setupCondaBuild = setupCondaBuild; + this.condaBuildCheck = condaBuildCheck; + this.condaBuild = condaBuild; } @Override @@ -55,6 +67,9 @@ public void execute(Project project) { addMinicondaInstallerDependency(project, miniconda); bootstrapPython.configureAfterEvaluate(miniconda, configuration.getSingleFile(), os); setupPython.configureAfterEvaluate(miniconda); + setupCondaBuild.configureAfterEvaluate(miniconda); + condaBuildCheck.configureAfterEvaluate(miniconda); + condaBuild.configureAfterEvaluate(miniconda); } private void addMinicondaInstallerDependency(final Project project, final MinicondaExtension miniconda) { diff --git a/src/main/java/com/palantir/python/miniconda/MinicondaExtension.java b/src/main/java/com/palantir/python/miniconda/MinicondaExtension.java index 57d0676..224e469 100644 --- a/src/main/java/com/palantir/python/miniconda/MinicondaExtension.java +++ b/src/main/java/com/palantir/python/miniconda/MinicondaExtension.java @@ -18,6 +18,7 @@ import java.io.File; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -38,6 +39,7 @@ public class MinicondaExtension { private static final File DEFAULT_BOOTSTRAP_DIRECTORY_PREFIX = new File(System.getProperty("user.home"), ".miniconda-bootstrap"); private static final String DEFAULT_BUILD_ENVIRONMENT_DIRECTORY = "build/miniconda"; + private static final String DEFAULT_META_YAML_DIR = "conda_recipe/"; private static final int DEFAULT_PYTHON_VERSION = 2; private final Project project; @@ -46,6 +48,8 @@ public class MinicondaExtension { private int pythonVersion = DEFAULT_PYTHON_VERSION; private File bootstrapDirectoryPrefix = DEFAULT_BOOTSTRAP_DIRECTORY_PREFIX; private File buildEnvironmentDirectory = null; + private File buildOutputDirectory = null; + private Path metaYaml = null; private List packages = new ArrayList<>(); private List channels = new ArrayList<>(Collections.singletonList(DEFAULT_CHANNEL)); @@ -102,6 +106,29 @@ public File getBuildEnvironmentDirectory() { return buildEnvironmentDirectory; } + public File getBuildOutputDirectory() { + return buildOutputDirectory; + } + + public void setMetaYaml(Path metaYaml) { + this.metaYaml = metaYaml; + } + + public void setMetaYaml(File metaYaml) { + setMetaYaml(metaYaml.toPath()); + } + + public void setMetaYaml(String metaYaml) { + setMetaYaml(Paths.get(metaYaml)); + } + + public Path getMetaYaml() { + if (metaYaml == null) { + return project.file(DEFAULT_META_YAML_DIR).toPath(); + } + return metaYaml; + } + public void setBuildEnvironmentDirectory(String buildEnvironmentDirectory) { setBuildEnvironmentDirectory(new File(buildEnvironmentDirectory)); } @@ -114,6 +141,18 @@ public void setBuildEnvironmentDirectory(File buildEnvironmentDirectory) { this.buildEnvironmentDirectory = buildEnvironmentDirectory; } + public void setBuildOutputDirectory(String buildOutputDirectory) { + setBuildOutputDirectory(new File(buildOutputDirectory)); + } + + public void setBuildOutputDirectory(Path buildOutputDirectory) { + setBuildOutputDirectory(buildOutputDirectory.toFile()); + } + + public void setBuildOutputDirectory(File buildOutputDirectory) { + this.buildOutputDirectory = buildOutputDirectory; + } + public String getMinicondaVersion() { return minicondaVersion; } diff --git a/src/main/java/com/palantir/python/miniconda/MinicondaPlugin.java b/src/main/java/com/palantir/python/miniconda/MinicondaPlugin.java index 948610c..39484a5 100644 --- a/src/main/java/com/palantir/python/miniconda/MinicondaPlugin.java +++ b/src/main/java/com/palantir/python/miniconda/MinicondaPlugin.java @@ -18,6 +18,9 @@ import com.palantir.python.miniconda.tasks.BootstrapPython; import com.palantir.python.miniconda.tasks.CleanTaskUtils; +import com.palantir.python.miniconda.tasks.CondaBuild; +import com.palantir.python.miniconda.tasks.CondaBuildCheck; +import com.palantir.python.miniconda.tasks.SetupCondaBuild; import com.palantir.python.miniconda.tasks.SetupPython; import org.gradle.api.Action; import org.gradle.api.Plugin; @@ -51,6 +54,9 @@ public void apply(Project project) { TaskContainer tasks = project.getTasks(); BootstrapPython bootstrapPython = BootstrapPython.createTask(tasks); SetupPython setupPython = SetupPython.createTask(tasks, bootstrapPython); + SetupCondaBuild setupCondaBuild = SetupCondaBuild.createTask(tasks, setupPython); + CondaBuildCheck condaBuildCheck = CondaBuildCheck.createTask(tasks, setupCondaBuild); + CondaBuild condaBuild = CondaBuild.createTask(tasks, condaBuildCheck); Task cleanBootstrapPython = project.getTasks().getByName(CleanTaskUtils.getCleanTaskName(bootstrapPython)); Task cleanSetupPython = project.getTasks().getByName(CleanTaskUtils.getCleanTaskName(setupPython)); @@ -60,7 +66,9 @@ public void apply(Project project) { LOG.debug("MinicondaPlugin tasks created."); Configuration configuration = project.getConfigurations().create(CONFIGURATION_NAME); - project.afterEvaluate(new AfterEvaluateAction(OS, configuration, bootstrapPython, setupPython)); + project.afterEvaluate( + new AfterEvaluateAction( + OS, configuration, bootstrapPython, setupPython, setupCondaBuild, condaBuildCheck, condaBuild)); } private static void createIvyRepository(Project project) { diff --git a/src/main/java/com/palantir/python/miniconda/MinicondaUtils.java b/src/main/java/com/palantir/python/miniconda/MinicondaUtils.java new file mode 100644 index 0000000..315cca5 --- /dev/null +++ b/src/main/java/com/palantir/python/miniconda/MinicondaUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * 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. + */ + +package com.palantir.python.miniconda; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class MinicondaUtils { + + public static List convertChannelsToArgs(List channels) { + List args = new ArrayList<>(); + for (String channel : channels) { + args.add("--channel"); + args.add(channel); + } + return Collections.unmodifiableList(args); + } + + private MinicondaUtils() {} +} diff --git a/src/main/java/com/palantir/python/miniconda/tasks/CondaBuild.java b/src/main/java/com/palantir/python/miniconda/tasks/CondaBuild.java new file mode 100644 index 0000000..45d568e --- /dev/null +++ b/src/main/java/com/palantir/python/miniconda/tasks/CondaBuild.java @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * 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. + */ + +package com.palantir.python.miniconda.tasks; + +import com.palantir.python.miniconda.MinicondaExtension; +import com.palantir.python.miniconda.MinicondaUtils; +import java.util.Objects; +import org.gradle.api.tasks.AbstractExecTask; +import org.gradle.api.tasks.TaskContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Task which builds and packages a Conda package. + * + * @author jakobjuelich + */ +public class CondaBuild extends AbstractExecTask { + private static final Logger LOG = LoggerFactory.getLogger(CondaBuild.class); + + private static final String DEFAULT_GROUP = "build"; + private static final String DEFAULT_DESCRIPTION = "Builds conda package using conda-build."; + + public static CondaBuild createTask(TaskContainer tasks, CondaBuildCheck condaBuildCheck) { + Objects.requireNonNull(tasks, "tasks must not be null"); + Objects.requireNonNull(condaBuildCheck, "condaBuildCheck must not be null"); + + CondaBuild task = tasks.create("condaBuild", CondaBuild.class); + task.setGroup(DEFAULT_GROUP); + task.setDescription(DEFAULT_DESCRIPTION); + task.dependsOn(condaBuildCheck); + + CleanTaskUtils.createCleanupTask(tasks, task); + return task; + } + + public CondaBuild() { + super(CondaBuild.class); + } + + public void configureAfterEvaluate(final MinicondaExtension miniconda) { + Objects.requireNonNull(miniconda, "miniconda must not be null"); + + executable(miniconda.getBuildEnvironmentDirectory().toPath().resolve("bin/conda")); + args("build", miniconda.getMetaYaml()); + args("--override-channels"); + args("--no-anaconda-upload"); + args(MinicondaUtils.convertChannelsToArgs(miniconda.getChannels())); + if (miniconda.getBuildOutputDirectory() != null) { + args("--output-folder", miniconda.getBuildOutputDirectory().getAbsolutePath()); + } + + LOG.info("{} configured to execute {}", getName(), getCommandLine()); + } + +} diff --git a/src/main/java/com/palantir/python/miniconda/tasks/CondaBuildCheck.java b/src/main/java/com/palantir/python/miniconda/tasks/CondaBuildCheck.java new file mode 100644 index 0000000..d5c1ff2 --- /dev/null +++ b/src/main/java/com/palantir/python/miniconda/tasks/CondaBuildCheck.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * 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. + */ + +package com.palantir.python.miniconda.tasks; + +import com.palantir.python.miniconda.MinicondaExtension; +import java.util.Objects; +import org.gradle.api.tasks.AbstractExecTask; +import org.gradle.api.tasks.TaskContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Task to run conda build --check to only check (validate) the recipe. + * + * @author jakobjuelich + */ +public class CondaBuildCheck extends AbstractExecTask { + private static final Logger LOG = LoggerFactory.getLogger(CondaBuildCheck.class); + + private static final String DEFAULT_GROUP = "build"; + private static final String DEFAULT_DESCRIPTION = "Runs `conda build --check` to only check (validate) the recipe."; + + public static CondaBuildCheck createTask(TaskContainer tasks, SetupCondaBuild setupCondaBuild) { + Objects.requireNonNull(tasks, "tasks must not be null"); + Objects.requireNonNull(setupCondaBuild, "setupCondaBuild must not be null"); + + CondaBuildCheck task = tasks.create("condaBuildCheck", CondaBuildCheck.class); + task.setGroup(DEFAULT_GROUP); + task.setDescription(DEFAULT_DESCRIPTION); + task.dependsOn(setupCondaBuild); + + CleanTaskUtils.createCleanupTask(tasks, task); + return task; + } + + public CondaBuildCheck() { + super(CondaBuildCheck.class); + } + + public void configureAfterEvaluate(final MinicondaExtension miniconda) { + Objects.requireNonNull(miniconda, "miniconda must not be null"); + + executable(miniconda.getBuildEnvironmentDirectory().toPath().resolve("bin/conda")); + args("build", miniconda.getMetaYaml()); + args("--check"); + + LOG.info("{} configured to execute {}", getName(), getCommandLine()); + } + +} diff --git a/src/main/java/com/palantir/python/miniconda/tasks/SetupCondaBuild.java b/src/main/java/com/palantir/python/miniconda/tasks/SetupCondaBuild.java new file mode 100644 index 0000000..f0b6b62 --- /dev/null +++ b/src/main/java/com/palantir/python/miniconda/tasks/SetupCondaBuild.java @@ -0,0 +1,85 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * 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. + */ + +package com.palantir.python.miniconda.tasks; + +import com.palantir.python.miniconda.MinicondaExtension; +import com.palantir.python.miniconda.MinicondaUtils; +import java.nio.file.Path; +import java.util.Objects; +import org.gradle.api.Task; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.AbstractExecTask; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.process.internal.ExecAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Installs conda-build. + * + * This can not be installed in an environment so we need a separate task for it. + * + * Created by jakobjuelich on 3/7/17. + */ +public class SetupCondaBuild extends AbstractExecTask { + + private static final Logger LOG = LoggerFactory.getLogger(SetupCondaBuild.class); + + private static final String DEFAULT_GROUP = "build"; + private static final String DEFAULT_DESCRIPTION = "Installs conda-build."; + + public static SetupCondaBuild createTask(TaskContainer tasks, SetupPython setupPython) { + Objects.requireNonNull(tasks, "tasks must not be null"); + Objects.requireNonNull(setupPython, "setupPython must not be null"); + + SetupCondaBuild task = tasks.create("setupCondaBuild", SetupCondaBuild.class); + task.setGroup(DEFAULT_GROUP); + task.setDescription(DEFAULT_DESCRIPTION); + task.dependsOn(setupPython); + + CleanTaskUtils.createCleanupTask(tasks, task); + return task; + } + + public SetupCondaBuild() { + super(SetupCondaBuild.class); + } + + public void configureAfterEvaluate(final MinicondaExtension miniconda) { + Objects.requireNonNull(miniconda, "miniconda must not be null"); + + final Path condaExec = miniconda.getBootstrapDirectory().toPath().resolve("bin/conda"); + executable(condaExec); + args("install", "--quiet", "--yes", "conda-build"); + args("--override-channels"); + args(MinicondaUtils.convertChannelsToArgs(miniconda.getChannels())); + + LOG.info("{} configured to execute {}", getName(), getCommandLine()); + + this.getOutputs().upToDateWhen(new Spec() { + @Override + public boolean isSatisfiedBy(Task task) { + ExecAction execAction = SetupCondaBuild.this.getExecActionFactory().newExecAction(); + execAction.executable(condaExec); + execAction.args("build", "-V"); // will error if build is not installed, otherwise just print stuff + execAction.setIgnoreExitValue(true); + + return 0 == execAction.execute().getExitValue(); + } + }); + } +} diff --git a/src/main/java/com/palantir/python/miniconda/tasks/SetupPython.java b/src/main/java/com/palantir/python/miniconda/tasks/SetupPython.java index e632074..027e624 100644 --- a/src/main/java/com/palantir/python/miniconda/tasks/SetupPython.java +++ b/src/main/java/com/palantir/python/miniconda/tasks/SetupPython.java @@ -17,9 +17,7 @@ package com.palantir.python.miniconda.tasks; import com.palantir.python.miniconda.MinicondaExtension; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import com.palantir.python.miniconda.MinicondaUtils; import java.util.Objects; import org.gradle.api.Action; import org.gradle.api.Task; @@ -61,10 +59,11 @@ public void configureAfterEvaluate(final MinicondaExtension miniconda) { getInputs().property("packages", miniconda.getPackages()); getOutputs().dir(miniconda.getBuildEnvironmentDirectory()); + executable(miniconda.getBootstrapDirectory().toPath().resolve("bin/conda")); args("create", "--yes", "--quiet", "-p", miniconda.getBuildEnvironmentDirectory()); args("--override-channels"); - args(convertChannelsToArgs(miniconda.getChannels())); + args(MinicondaUtils.convertChannelsToArgs(miniconda.getChannels())); args(miniconda.getPackages()); doFirst(new Action() { @@ -78,13 +77,4 @@ public void execute(Task task) { }); LOG.info("{} configured to execute {}", getName(), getCommandLine()); } - - private static List convertChannelsToArgs(List channels) { - List args = new ArrayList<>(); - for (String channel : channels) { - args.add("--channel"); - args.add(channel); - } - return Collections.unmodifiableList(args); - } } diff --git a/src/test/groovy/com/palantir/python/miniconda/MinicondaBuildTest.groovy b/src/test/groovy/com/palantir/python/miniconda/MinicondaBuildTest.groovy new file mode 100644 index 0000000..fa1f855 --- /dev/null +++ b/src/test/groovy/com/palantir/python/miniconda/MinicondaBuildTest.groovy @@ -0,0 +1,110 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * 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. + */ + +package com.palantir.python.miniconda + +import static groovy.test.GroovyAssert.shouldFail + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings +import java.nio.file.Files +import java.nio.file.Paths +import org.apache.commons.io.FileUtils +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import spock.lang.Specification + +/** + * Functional tests for the Miniconda build command. + * + * @author jakobjuelich + */ +class MinicondaBuildTest extends Specification { + private static final Logger LOG = LoggerFactory.getLogger(MinicondaBuildTest.class); + + // use a temp dir with a short name, because miniconda complains + // when PREFIX is longer than 128 chars + private File tempDirectory = Files.createTempDirectory("miniconda").toFile() + + def setup() { + tempDirectory.mkdirs() + FileUtils.copyDirectory(Paths.get("src/test/resources/test-project").toFile(), tempDirectory) + } + + def cleanup() { + tempDirectory.deleteDir() + } + + def 'build package'() { + when: + def runner = GradleRunner.create() + .forwardOutput() + .withProjectDir(tempDirectory) + .withArguments("--info", "--stacktrace", ":condaBuild") + .withPluginClasspath() + + BuildResult result = runner.build() + LOG.info(result.getOutput()) + + then: + result.task(":setupPython").outcome == TaskOutcome.SUCCESS + result.task(":condaBuild").outcome == TaskOutcome.SUCCESS + + FileUtils.listFiles( + tempDirectory.toPath().resolve("build/output").toFile(), ["tar.bz2"] as String[], true).size() == 1 + } + + @SuppressFBWarnings + def 'build with broken recipe should fail'() { + when: + // `conda build` exits with 0, even if meta.yaml doesn't exist. CondaBuildCheck should catch this. + tempDirectory.toPath().resolve("conda_recipe/meta.yaml").toFile().delete(); + + def runner = GradleRunner.create() + .forwardOutput() + .withProjectDir(tempDirectory) + .withArguments("--info", "--stacktrace", ":condaBuild") + .withPluginClasspath() + + shouldFail(UnexpectedBuildFailure) { + runner.build() + } + + then: + 1 == 1 + } + + def 'second setupCondaBuild up to date'() { + when: + def runner = GradleRunner.create() + .forwardOutput() + .withProjectDir(tempDirectory) + .withArguments("--info", "--stacktrace", ":setupCondaBuild") + .withPluginClasspath() + BuildResult firstResult = runner.build() + LOG.info(firstResult.getOutput()) + + BuildResult secondResult = runner.build() + LOG.info(secondResult.getOutput()) + + then: + firstResult.task(":setupCondaBuild").outcome == TaskOutcome.SUCCESS + secondResult.task(":setupCondaBuild").outcome == TaskOutcome.UP_TO_DATE + } +} diff --git a/src/test/groovy/com/palantir/python/miniconda/MinicondaFunctionalTest.groovy b/src/test/groovy/com/palantir/python/miniconda/MinicondaFunctionalTest.groovy index 43e2714..6e2565d 100644 --- a/src/test/groovy/com/palantir/python/miniconda/MinicondaFunctionalTest.groovy +++ b/src/test/groovy/com/palantir/python/miniconda/MinicondaFunctionalTest.groovy @@ -62,8 +62,9 @@ class MinicondaFunctionalTest extends Specification { when: def runner = GradleRunner.create() + .forwardOutput() .withProjectDir(tempDirectory) - .withArguments(":setupPython") + .withArguments("--info", "--stacktrace", ":setupPython") .withPluginClasspath() BuildResult result = runner.build(); diff --git a/src/test/resources/test-project/build.gradle b/src/test/resources/test-project/build.gradle new file mode 100644 index 0000000..03ac66d --- /dev/null +++ b/src/test/resources/test-project/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'com.palantir.python.miniconda' +} + +miniconda { + minicondaVersion = '3.18.3' + packages = ['python'] + buildOutputDirectory = project.getBuildDir().toPath().resolve("output") +} diff --git a/src/test/resources/test-project/conda_recipe/build.sh b/src/test/resources/test-project/conda_recipe/build.sh new file mode 100644 index 0000000..5a5aeeb --- /dev/null +++ b/src/test/resources/test-project/conda_recipe/build.sh @@ -0,0 +1 @@ +$PYTHON setup.py install diff --git a/src/test/resources/test-project/conda_recipe/meta.yaml b/src/test/resources/test-project/conda_recipe/meta.yaml new file mode 100644 index 0000000..13df78f --- /dev/null +++ b/src/test/resources/test-project/conda_recipe/meta.yaml @@ -0,0 +1,17 @@ +package: + name: example_project + version: "1.0.0" + +source: + path: .. + +requirements: + build: + - python + - setuptools + run: + - coverage + +test: + imports: + - example_project diff --git a/src/test/resources/test-project/example_project/__init__.py b/src/test/resources/test-project/example_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/test/resources/test-project/setup.cfg b/src/test/resources/test-project/setup.cfg new file mode 100644 index 0000000..a8a7bac --- /dev/null +++ b/src/test/resources/test-project/setup.cfg @@ -0,0 +1,3 @@ +[tool:pytest] +minversion = 3.0 + diff --git a/src/test/resources/test-project/setup.py b/src/test/resources/test-project/setup.py new file mode 100755 index 0000000..e42567e --- /dev/null +++ b/src/test/resources/test-project/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +from setuptools import find_packages, setup + +setup( + name='example_project', + description='Amazing thing', + author='Me', + packages=find_packages(), + + install_requires=[], + tests_require=['tox', 'pytest', 'pytest-cov', 'coverage'], +)