diff --git a/changelog/dub-run.dd b/changelog/dub-run.dd new file mode 100644 index 0000000..d66cf35 --- /dev/null +++ b/changelog/dub-run.dd @@ -0,0 +1,45 @@ +`dub run` will now automatically fetch a package if it's not found locally + +Starting with this release, `dub run ` makes sure that the package is available locally. +This means that now a `dub fetch ` is no longer required and all a user of a library needs to run your dub package is `dub run`: + +$(CONSOLE +> dub run gitcompatibledubpackage +gitcompatibledubpackage wasn't found locally, but it's available online: +--- +Description: Example of a DUB package also usable as git submodule. For DUB test suite. +Version: 1.0.4 +--- +Do you want to fetch gitcompatibledubpackage? [Y/n]: +) + +An optional `--yes` (`-y`) flag is provided for non-interactive use: + +$(CONSOLE +> dub run --yes gitcompatibledubpackage +gitcompatibledubpackage wasn't found locally, but it's available online: +--- +Description: Example of a DUB package also usable as git submodule. For DUB test suite. +Version: 1.0.4 +--- +Fetching gitcompatibledubpackage 1.0.4... +Building package gitcompatibledubpackage in /home/seb/.dub/packages/gitcompatibledubpackage-1.0.4/gitcompatibledubpackage/ +Performing "debug" build using dmd for x86_64. +gitcompatibledubpackage 1.0.4: building configuration "exe"... +Linking... +Running ../../.dub/packages/gitcompatibledubpackage-1.0.4/gitcompatibledubpackage/gitcompatibledubpackage +Hello DUB +) + +If one wants to run a specific version of a package, it can be passed to `dub run` too: + +$(CONSOLE +> dub run gitcompatibledubpackage@1.0.3 +Fetching gitcompatibledubpackage 1.0.3... +Building package gitcompatibledubpackage in /home/seb/.dub/packages/gitcompatibledubpackage-1.0.3/gitcompatibledubpackage/ +Performing "debug" build using dmd for x86_64. +gitcompatibledubpackage 1.0.3: building configuration "exe"... +Linking... +Running ../../.dub/packages/gitcompatibledubpackage-1.0.3/gitcompatibledubpackage/gitcompatibledubpackage +Hello DUB +) diff --git a/source/dub/commandline.d b/source/dub/commandline.d index a420aea..c210f4e 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -517,7 +517,7 @@ free_args = free_args[1 .. $]; } - string input(string caption, string default_value) + static string input(string caption, string default_value) { writef("%s [%s]: ", caption, default_value); auto inp = readln(); @@ -829,6 +829,7 @@ } class BuildCommand : GenerateCommand { + bool m_yes; // automatic yes to prompts; this() { this.name = "build"; @@ -848,12 +849,70 @@ args.getopt("f|force", &m_force, [ "Forces a recompilation even if the target is up to date" ]); + args.getopt("y|yes", &m_yes, [ + `Automatic yes to prompts. Assume "yes" as answer to all prompts and run non-interactively.` + ]); super.prepare(args); m_generator = "build"; } override int execute(Dub dub, string[] free_args, string[] app_args) { + // single package files don't need to be downloaded, they are on the disk. + if (free_args.length < 1 || m_single) + return super.execute(dub, free_args, app_args); + + const package_parts = splitPackageName(free_args[0]); + const package_name = package_parts.name; + + static bool input(string caption, bool default_value = true) { + writef("%s [%s]: ", caption, default_value ? "Y/n" : "y/N"); + auto inp = readln(); + string userInput = "y"; + if (inp.length > 1) + userInput = inp[0 .. $ - 1].toLower; + + switch (userInput) { + case "no", "n", "0": + return false; + case "yes", "y", "1": + default: + return true; + } + } + + Dependency dep; + + if (package_parts.version_.length > 0) { + // the user provided a version manually + free_args[0] = package_name; + dep = Dependency(package_parts.version_); + } else { + const pack = dub.packageManager.getFirstPackage(package_name); + if (pack) + return super.execute(dub, free_args, app_args); + + // search for the package and filter versions for exact matches + auto search = dub.searchPackages(package_name) + .map!(tup => tup[1].find!(p => p.name == package_name)) + .filter!(ps => !ps.empty); + if (search.empty) + return 2; + + const p = search.front.front; + logInfo("%s wasn't found locally, but it's available online:", package_name); + logInfo("---"); + logInfo("Description: %s", p.description); + logInfo("Version: %s", p.version_); + logInfo("---"); + + const answer = m_yes ? true : input("Do you want to fetch %s?".format(package_name)); + if (!answer) + return 0; + dep = Dependency(p.version_); + } + + dub.fetch(package_name, dep, dub.defaultPlacementLocation, FetchOptions.none); return super.execute(dub, free_args, app_args); } } diff --git a/test/issue877-auto-fetch-package-on-run.sh b/test/issue877-auto-fetch-package-on-run.sh new file mode 100755 index 0000000..a131426 --- /dev/null +++ b/test/issue877-auto-fetch-package-on-run.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eu -o pipefail +set -x +$DUB remove --version="*" gitcompatibledubpackage || true + +# check whether the interactive run mode works +echo "y" | $DUB run gitcompatibledubpackage | grep "Hello DUB" +$DUB remove gitcompatibledubpackage + +! (echo "n" | $DUB run gitcompatibledubpackage | grep "Hello DUB") +! $DUB remove gitcompatibledubpackage + +# check -y +$DUB run --yes gitcompatibledubpackage | grep "Hello DUB" +$DUB remove gitcompatibledubpackage + +# check --yes +$DUB run -y gitcompatibledubpackage | grep "Hello DUB" +$DUB remove gitcompatibledubpackage + +# check supplying versions directly +dub_log="$($DUB run gitcompatibledubpackage@1.0.3)" +echo "$dub_log" | grep "Hello DUB" +echo "$dub_log" | grep "Fetching.*1.0.3" +$DUB remove gitcompatibledubpackage + +# check supplying an invalid version +(! $DUB run gitcompatibledubpackage@0.42.43) 2>&1 | \ + grep 'No package gitcompatibledubpackage was found matching the dependency 0[.]42[.]43' + +! $DUB remove gitcompatibledubpackage