summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarie <tabmeier12@gmail.com>2022-12-17 13:32:40 +0100
committerGitHub <noreply@github.com>2022-12-17 13:32:40 +0100
commit220844daa014046bedbc9ce703f8b18fbe267e3c (patch)
tree7ed760cebf744b2feb7897454956d78887a131e0
parent1f7d1e39c6b298ec6d309b894a4ad740f6021ce1 (diff)
feat(gradle): add gradle module (#4423)
* docs: document gradle module * implement gradle module * gradle-module: add test for wrapper properties case * docs: improve gradle module documentation * fix: fix gradle module wrapper properties test * drop gradle executable strategy * apply suggested stuff * Fix config schema
-rw-r--r--.github/config-schema.json79
-rw-r--r--docs/.vuepress/public/presets/toml/bracketed-segments.toml3
-rw-r--r--docs/.vuepress/public/presets/toml/no-runtime-versions.toml3
-rw-r--r--docs/.vuepress/public/presets/toml/pastel-powerline.toml5
-rw-r--r--docs/.vuepress/public/presets/toml/plain-text-symbols.toml3
-rw-r--r--docs/config/README.md37
-rw-r--r--src/configs/gradle.rs36
-rw-r--r--src/configs/mod.rs3
-rw-r--r--src/configs/starship_root.rs1
-rw-r--r--src/module.rs1
-rw-r--r--src/modules/gradle.rs220
-rw-r--r--src/modules/mod.rs3
12 files changed, 394 insertions, 0 deletions
diff --git a/.github/config-schema.json b/.github/config-schema.json
index 2cb9bb5f1..404cdd6aa 100644
--- a/.github/config-schema.json
+++ b/.github/config-schema.json
@@ -621,6 +621,29 @@
}
]
},
+ "gradle": {
+ "default": {
+ "detect_extensions": [
+ "gradle",
+ "gradle.kts"
+ ],
+ "detect_files": [],
+ "detect_folders": [
+ "gradle"
+ ],
+ "disabled": false,
+ "format": "via [$symbol($version )]($style)",
+ "recursive": false,
+ "style": "bold bright-cyan",
+ "symbol": "🅶 ",
+ "version_format": "v${raw}"
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/GradleConfig"
+ }
+ ]
+ },
"guix_shell": {
"default": {
"disabled": false,
@@ -3189,6 +3212,62 @@
},
"additionalProperties": false
},
+ "GradleConfig": {
+ "type": "object",
+ "properties": {
+ "format": {
+ "default": "via [$symbol($version )]($style)",
+ "type": "string"
+ },
+ "version_format": {
+ "default": "v${raw}",
+ "type": "string"
+ },
+ "symbol": {
+ "default": "🅶 ",
+ "type": "string"
+ },
+ "style": {
+ "default": "bold bright-cyan",
+ "type": "string"
+ },
+ "disabled": {
+ "default": false,
+ "type": "boolean"
+ },
+ "recursive": {
+ "default": false,
+ "type": "boolean"
+ },
+ "detect_extensions": {
+ "default": [
+ "gradle",
+ "gradle.kts"
+ ],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "detect_files": {
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "detect_folders": {
+ "default": [
+ "gradle"
+ ],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false
+ },
"GuixShellConfig": {
"type": "object",
"properties": {
diff --git a/docs/.vuepress/public/presets/toml/bracketed-segments.toml b/docs/.vuepress/public/presets/toml/bracketed-segments.toml
index f871774dd..c86ed0479 100644
--- a/docs/.vuepress/public/presets/toml/bracketed-segments.toml
+++ b/docs/.vuepress/public/presets/toml/bracketed-segments.toml
@@ -58,6 +58,9 @@ format = '([\[$all_status$ahead_behind\]]($style))'
[golang]
format = '\[[$symbol($version)]($style)\]'
+[gradle]
+format = '\[[$symbol($version)]($style)\]'
+
[guix_shell]
format = '\[[$symbol]($style)\]'
diff --git a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
index 28ffb7fca..97b6a3ed7 100644
--- a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
+++ b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
@@ -37,6 +37,9 @@ format = 'via [$symbol]($style)'
[golang]
format = 'via [$symbol]($style)'
+[gradle]
+format = 'via [$symbol]($style)'
+
[haxe]
format = 'via [$symbol]($style)'
diff --git a/docs/.vuepress/public/presets/toml/pastel-powerline.toml b/docs/.vuepress/public/presets/toml/pastel-powerline.toml
index 6c240d063..f5cee6ecc 100644
--- a/docs/.vuepress/public/presets/toml/pastel-powerline.toml
+++ b/docs/.vuepress/public/presets/toml/pastel-powerline.toml
@@ -12,6 +12,7 @@ $c\
$elixir\
$elm\
$golang\
+$gradle\
$haskell\
$java\
$julia\
@@ -97,6 +98,10 @@ symbol = " "
style = "bg:#86BBD8"
format = '[ $symbol ($version) ]($style)'
+[gradle]
+style = "bg:#86BBD8"
+format = '[ $symbol ($version) ]($style)'
+
[haskell]
symbol = " "
style = "bg:#86BBD8"
diff --git a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
index bf6591980..1efccd243 100644
--- a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
+++ b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
@@ -64,6 +64,9 @@ symbol = "git "
[golang]
symbol = "go "
+[gradle]
+symbol = "gradle "
+
[guix_shell]
symbol = "guix "
diff --git a/docs/config/README.md b/docs/config/README.md
index 0d4d13ac8..4d1a7161f 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -291,6 +291,7 @@ $helm\
$java\
$julia\
$kotlin\
+$gradle\
$lua\
$nim\
$nodejs\
@@ -1927,6 +1928,42 @@ disabled = true
format = 'via [🐂](yellow bold) '
```
+## Gradle
+
+The `gradle` module shows the version of the [Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html)
+currently used in the project directory.
+
+By default the module will be shown if any of the following conditions are met:
+
+- The current directory contains a `gradle/wrapper/gradle-wrapper.properties` directory.
+- The current directory contains a file ending with `.gradle` or `.gradle.kts`.
+
+The `gradle` module is only able to read your Gradle Wrapper version from your config file, we don't execute your wrapper, because of the security concerns.
+
+### Options
+
+| Option | Default | Description |
+| ------------------- | ------------------------------------ | ------------------------------------------------------------------------- |
+| `format` | `"via [$symbol($version )]($style)"` | The format for the module. |
+| `version_format` | `"v${raw}"` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` |
+| `symbol` | `"🅶 "` | A format string representing the symbol of Gradle. |
+| `detect_extensions` | `["gradle", "gradle.kts"]` | Which extensions should trigger this module. |
+| `detect_files` | `[]` | Which filenames should trigger this module. |
+| `detect_folders` | `["gradle"]` | Which folders should trigger this module. |
+| `style` | `"bold bright-cyan"` | The style for the module. |
+| `disabled` | `false` | Disables the `gradle` module. |
+| `recursive` | `false` | Enables recursive finding for the `gradle` directory. |
+
+### Variables
+
+| Variable | Example | Description |
+| -------- | -------- | ------------------------------------ |
+| version | `v7.5.1` | The version of `gradle` |
+| symbol | | Mirrors the value of option `symbol` |
+| style* | | Mirrors the value of option `style` |
+
+*: This variable can only be used as a part of a style string
+
## Haskell
The `haskell` module finds the current selected GHC version and/or the selected Stack snapshot.
diff --git a/src/configs/gradle.rs b/src/configs/gradle.rs
new file mode 100644
index 000000000..5026f83e5
--- /dev/null
+++ b/src/configs/gradle.rs
@@ -0,0 +1,36 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Deserialize, Serialize)]
+#[cfg_attr(
+ feature = "config-schema",
+ derive(schemars::JsonSchema),
+ schemars(deny_unknown_fields)
+)]
+#[serde(default)]
+pub struct GradleConfig<'a> {
+ pub format: &'a str,
+ pub version_format: &'a str,
+ pub symbol: &'a str,
+ pub style: &'a str,
+ pub disabled: bool,
+ pub recursive: bool,
+ pub detect_extensions: Vec<&'a str>,
+ pub detect_files: Vec<&'a str>,
+ pub detect_folders: Vec<&'a str>,
+}
+
+impl<'a> Default for GradleConfig<'a> {
+ fn default() -> Self {
+ GradleConfig {
+ format: "via [$symbol($version )]($style)",
+ version_format: "v${raw}",
+ symbol: "🅶 ",
+ style: "bold bright-cyan",
+ disabled: false,
+ recursive: false,
+ detect_extensions: vec!["gradle", "gradle.kts"],
+ detect_files: vec![],
+ detect_folders: vec!["gradle"],
+ }
+ }
+}
diff --git a/src/configs/mod.rs b/src/configs/mod.rs
index a7717c3ac..88d1d8d97 100644
--- a/src/configs/mod.rs
+++ b/src/configs/mod.rs
@@ -33,6 +33,7 @@ pub mod git_metrics;
pub mod git_state;
pub mod git_status;
pub mod go;
+pub mod gradle;
pub mod guix_shell;
pub mod haskell;
pub mod haxe;
@@ -164,6 +165,8 @@ pub struct FullConfig<'a> {
#[serde(borrow)]
golang: go::GoConfig<'a>,
#[serde(borrow)]
+ gradle: gradle::GradleConfig<'a>,
+ #[serde(borrow)]
guix_shell: guix_shell::GuixShellConfig<'a>,
#[serde(borrow)]
haskell: haskell::HaskellConfig<'a>,
diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs
index fd67bc65c..3fdd06f68 100644
--- a/src/configs/starship_root.rs
+++ b/src/configs/starship_root.rs
@@ -58,6 +58,7 @@ pub const PROMPT_ORDER: &[&str] = &[
"elm",
"erlang",
"golang",
+ "gradle",
"haskell",
"haxe",
"helm",
diff --git a/src/module.rs b/src/module.rs
index f0d1a0e0d..64518afd6 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -41,6 +41,7 @@ pub const ALL_MODULES: &[&str] = &[
"git_state",
"git_status",
"golang",
+ "gradle",
"guix_shell",
"haskell",
"haxe",
diff --git a/src/modules/gradle.rs b/src/modules/gradle.rs
new file mode 100644
index 000000000..e19c9a4c9
--- /dev/null
+++ b/src/modules/gradle.rs
@@ -0,0 +1,220 @@
+use crate::{
+ config::ModuleConfig,
+ configs::gradle::GradleConfig,
+ context::Context,
+ formatter::{StringFormatter, VersionFormatter},
+ module::Module,
+ utils,
+};
+
+pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
+ let mut module = context.new_module("gradle");
+ let config = GradleConfig::try_load(module.config);
+ let is_gradle_project = context
+ .try_begin_scan()?
+ .set_files(&config.detect_files)
+ .set_extensions(&config.detect_extensions)
+ .set_folders(&config.detect_folders)
+ .is_match();
+
+ if !is_gradle_project {
+ return None;
+ }
+
+ let parsed = StringFormatter::new(config.format).and_then(|formatter| {
+ formatter
+ .map_meta(|var, _| match var {
+ "symbol" => Some(config.symbol),
+ _ => None,
+ })
+ .map_style(|variable| match variable {
+ "style" => Some(Ok(config.style)),
+ _ => None,
+ })
+ .map(|variable| match variable {
+ "version" => {
+ let gradle_version = {
+ let properties = get_wrapper_properties_file(context, config.recursive)?;
+ parse_gradle_version_from_properties(&properties)?
+ };
+ VersionFormatter::format_module_version(
+ module.get_name(),
+ &gradle_version,
+ config.version_format,
+ )
+ .map(Ok)
+ }
+ _ => None,
+ })
+ .parse(None, Some(context))
+ });
+
+ module.set_segments(match parsed {
+ Ok(segments) => segments,
+ Err(error) => {
+ log::warn!("Error in module `gradle`:\n{}", error);
+ return None;
+ }
+ });
+
+ Some(module)
+}
+
+fn parse_gradle_version_from_properties(wrapper_properties: &str) -> Option<String> {
+ // example gradle.properties content
+ /*
+ distributionBase=GRADLE_USER_HOME
+ distributionPath=wrapper/dists
+ distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+ zipStoreBase=GRADLE_USER_HOME
+ zipStorePath=wrapper/dists
+ */
+ let version = wrapper_properties
+ .lines()
+ .find(|line| line.starts_with("distributionUrl="))?
+ .rsplit_once('/')?
+ .1
+ .strip_prefix("gradle-")?
+ .split_once('-')?
+ .0;
+ Some(version.to_string())
+}
+
+/// Tries to find the gradle-wrapper.properties file.
+fn get_wrapper_properties_file(context: &Context, recursive: bool) -> Option<String> {
+ let mut properties = None;
+ if context
+ .try_begin_scan()?
+ .set_folders(&["gradle"])
+ .is_match()
+ {
+ properties = utils::read_file(
+ &context
+ .current_dir
+ .join("gradle/wrapper/gradle-wrapper.properties"),
+ )
+ .ok();
+ };
+ if recursive && properties.is_none() {
+ for dir in context.current_dir.ancestors().skip(1) {
+ properties =
+ utils::read_file(&dir.join("gradle/wrapper/gradle-wrapper.properties")).ok();
+ if properties.is_some() {
+ break;
+ }
+ }
+ }
+ properties
+}
+
+#[cfg(test)]
+mod tests {
+ use nu_ansi_term::Color;
+
+ use super::*;
+ use crate::test::ModuleRenderer;
+ use std::fs::{self, File};
+ use std::io::{self, Write};
+
+ #[test]
+ fn folder_without_gradle_files() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+
+ let actual = ModuleRenderer::new("gradle").path(dir.path()).collect();
+
+ let expected = None;
+ assert_eq!(expected, actual);
+ dir.close()
+ }
+
+ #[test]
+ fn folder_with_gradle_wrapper_properties() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let properties = dir
+ .path()
+ .join("gradle")
+ .join("wrapper")
+ .join("gradle-wrapper.properties");
+ // create gradle/wrapper/ directories
+ fs::create_dir_all(properties.parent().unwrap())?;
+ // create build.gradle file to mark it as a gradle project
+ File::create(dir.path().join("build.gradle"))?.sync_all()?;
+ let mut file = File::create(properties)?;
+ file.write_all(
+ b"\
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists",
+ )?;
+ file.sync_all()?;
+
+ let actual = ModuleRenderer::new("gradle").path(dir.path()).collect();
+
+ let expected = Some(format!(
+ "via {}",
+ Color::LightCyan.bold().paint("🅶 v7.5.1 ")
+ ));
+ assert_eq!(expected, actual);
+ dir.close()
+ }
+
+ #[test]
+ fn gradle_wrapper_recursive() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let properties = dir
+ .path()
+ .join("gradle")
+ .join("wrapper")
+ .join("gradle-wrapper.properties");
+ // create gradle/wrapper/ directories
+ fs::create_dir_all(properties.parent().unwrap())?;
+ // create build.gradle file to mark it as a gradle project
+ File::create(dir.path().join("build.gradle"))?.sync_all()?;
+ let mut file = File::create(properties)?;
+ file.write_all(
+ b"\
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists",
+ )?;
+ file.sync_all()?;
+
+ let target_dir = dir.path().join("working_dir");
+ fs::create_dir(&target_dir)?;
+ File::create(target_dir.join("build.gradle.kts"))?.sync_all()?;
+
+ let actual = ModuleRenderer::new("gradle")
+ .config(toml::toml! {
+ [gradle]
+ recursive = true
+ })
+ .path(target_dir)
+ .collect();
+
+ let expected = Some(format!(
+ "via {}",
+ Color::LightCyan.bold().paint("🅶 v7.5.1 ")
+ ));
+ assert_eq!(expected, actual);
+ dir.close()
+ }
+
+ #[test]
+ fn test_format_wrapper_properties() {
+ let input = "\
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+ ";
+ assert_eq!(
+ parse_gradle_version_from_properties(input),
+ Some("7.5.1".to_string())
+ );
+ }
+}
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index 12b7f8ddc..8e463aa55 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -30,6 +30,7 @@ mod git_metrics;
mod git_state;
mod git_status;
mod golang;
+mod gradle;
mod guix_shell;
mod haskell;
mod haxe;
@@ -129,6 +130,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"git_state" => git_state::module(context),
"git_status" => git_status::module(context),
"golang" => golang::module(context),
+ "gradle" => gradle::module(context),
"guix_shell" => guix_shell::module(context),
"haskell" => haskell::module(context),
"haxe" => haxe::module(context),
@@ -239,6 +241,7 @@ pub fn description(module: &str) -> &'static str {
"git_state" => "The current git operation, and it's progress",
"git_status" => "Symbol representing the state of the repo",
"golang" => "The currently installed version of Golang",
+ "gradle" => "The currently installed version of Gradle",
"guix_shell" => "The guix-shell environment",
"haskell" => "The selected version of the Haskell toolchain",
"haxe" => "The currently installed version of Haxe",