summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwdullaer <wdullaer@users.noreply.github.com>2019-12-09 08:42:51 +0700
committerMatan Kushner <hello@matchai.me>2019-12-08 20:42:51 -0500
commit44b37a7baca4d9e50a524c51f9ea305f0b5f60fa (patch)
tree2e215b996415ddf16eb7e7c165eb4c5b64d1d9c5
parentf458a5e8c91bd6fd5b7df905f0b65e0e35034072 (diff)
feat: Implement terraform module with workspace and version (#644)
-rw-r--r--.github/workflows/workflow.yml5
-rw-r--r--docs/config/README.md28
-rw-r--r--src/configs/mod.rs1
-rw-r--r--src/configs/starship_root.rs1
-rw-r--r--src/configs/terraform.rs27
-rw-r--r--src/module.rs1
-rw-r--r--src/modules/mod.rs2
-rw-r--r--src/modules/terraform.rs125
-rw-r--r--tests/Dockerfile12
-rw-r--r--tests/testsuite/main.rs1
-rw-r--r--tests/testsuite/terraform.rs174
11 files changed, 377 insertions, 0 deletions
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 7d3f21530..a3837de64 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -139,6 +139,11 @@ jobs:
- name: Install Mercurial (windows)
if: matrix.os == 'windows-latest'
run: choco install hg
+
+ # Install Terraform at a fixed version
+ - uses: volcano-coffee-company/setup-terraform@v1
+ with:
+ version: "0.12.14"
# Run the ignored tests that expect the above setup
- name: Run all tests
diff --git a/docs/config/README.md b/docs/config/README.md
index f9687e974..176e3c515 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -103,6 +103,7 @@ prompt_order = [
"python",
"ruby",
"rust",
+ "terraform",
"nix_shell",
"conda",
"memory_usage",
@@ -989,6 +990,33 @@ The module will be shown if any of the following conditions are met:
symbol = "⚙️ "
```
+## Terraform
+
+The `terraform` module shows the currently selected terraform workspace and version.
+By default the terraform version is not shown, since this is slow on current versions of terraform when a lot of plugins are in use.
+The module will be shown if any of the following conditions are met:
+
+- The current directory contains a `.terraform` folder
+- Current directory contains a file with the `.tf` extension
+
+### Options
+
+| Variable | Default | Description |
+| -------------- | ------------- | ----------------------------------------------------------- |
+| `symbol` | `"💠 "` | The symbol used before displaying the terraform workspace. |
+| `style` | `"bold 105"` | The style for the module. |
+| `disabled` | `false` | Disables the `terraform` module. |
+| `show_version` | `false` | Shows the terraform version. Very slow on large workspaces. |
+
+### Example
+
+```toml
+# ~/.config/starship.toml
+
+[terraform]
+symbol = "🏎💨 "
+```
+
## Time
The `time` module shows the current **local** time.
diff --git a/src/configs/mod.rs b/src/configs/mod.rs
index c840f3bba..dcf0337f4 100644
--- a/src/configs/mod.rs
+++ b/src/configs/mod.rs
@@ -25,6 +25,7 @@ pub mod python;
pub mod ruby;
pub mod rust;
mod starship_root;
+pub mod terraform;
pub mod time;
pub mod username;
diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs
index 293dcb17c..00a647354 100644
--- a/src/configs/starship_root.rs
+++ b/src/configs/starship_root.rs
@@ -36,6 +36,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> {
"python",
"ruby",
"rust",
+ "terraform",
// ↑ Toolchain version modules ↑
"nix_shell",
"conda",
diff --git a/src/configs/terraform.rs b/src/configs/terraform.rs
new file mode 100644
index 000000000..3d427e5c4
--- /dev/null
+++ b/src/configs/terraform.rs
@@ -0,0 +1,27 @@
+use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
+
+use ansi_term::{Color, Style};
+use starship_module_config_derive::ModuleConfig;
+
+#[derive(Clone, ModuleConfig)]
+pub struct TerraformConfig<'a> {
+ pub symbol: SegmentConfig<'a>,
+ pub workspace: SegmentConfig<'a>,
+ pub version: SegmentConfig<'a>,
+ pub style: Style,
+ pub disabled: bool,
+ pub show_version: bool,
+}
+
+impl<'a> RootModuleConfig<'a> for TerraformConfig<'a> {
+ fn new() -> Self {
+ TerraformConfig {
+ symbol: SegmentConfig::new("💠 "),
+ workspace: SegmentConfig::default(),
+ version: SegmentConfig::default(),
+ style: Color::Fixed(105).bold(),
+ disabled: false,
+ show_version: false,
+ }
+ }
+}
diff --git a/src/module.rs b/src/module.rs
index 71b25627f..873c2e1ad 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -36,6 +36,7 @@ pub const ALL_MODULES: &[&str] = &[
"ruby",
"rust",
"php",
+ "terraform",
"time",
"username",
];
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index 42951324b..a95ea4fd3 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -25,6 +25,7 @@ mod php;
mod python;
mod ruby;
mod rust;
+mod terraform;
mod time;
mod username;
mod utils;
@@ -68,6 +69,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"python" => python::module(context),
"ruby" => ruby::module(context),
"rust" => rust::module(context),
+ "terraform" => terraform::module(context),
"time" => time::module(context),
"username" => username::module(context),
_ => {
diff --git a/src/modules/terraform.rs b/src/modules/terraform.rs
new file mode 100644
index 000000000..60d0b46c9
--- /dev/null
+++ b/src/modules/terraform.rs
@@ -0,0 +1,125 @@
+use super::{Context, Module, RootModuleConfig};
+
+use crate::configs::terraform::TerraformConfig;
+use crate::utils;
+use std::env;
+use std::io;
+use std::path::PathBuf;
+
+/// Creates a module with the current Terraform version and workspace
+///
+/// Will display the Terraform version and workspace if any of the following criteria are met:
+/// - Current directory contains a `.terraform` directory
+/// - Current directory contains a file with the `.tf` extension
+pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
+ let is_terraform_project = context
+ .try_begin_scan()?
+ .set_folders(&[".terraform"])
+ .set_extensions(&["tf"])
+ .is_match();
+
+ if !is_terraform_project {
+ return None;
+ }
+
+ let mut module = context.new_module("terraform");
+ let config: TerraformConfig = TerraformConfig::try_load(module.config);
+
+ module.set_style(config.style);
+ module.create_segment("symbol", &config.symbol);
+
+ if config.show_version {
+ let terraform_version =
+ format_terraform_version(&utils::exec_cmd("terraform", &["version"])?.stdout.as_str())?;
+ module.create_segment("version", &config.version.with_value(&terraform_version));
+ }
+
+ let terraform_workspace = &get_terraform_workspace(&context.current_dir)?;
+ module.create_segment(
+ "workspace",
+ &config.workspace.with_value(&terraform_workspace),
+ );
+
+ Some(module)
+}
+
+// Determines the currently selected workspace (see https://github.com/hashicorp/terraform/blob/master/command/meta.go for the original implementation)
+fn get_terraform_workspace(cwd: &PathBuf) -> Option<String> {
+ // Workspace can be explicitly overwritten by an env var
+ let workspace_override = env::var("TF_WORKSPACE");
+ if workspace_override.is_ok() {
+ return workspace_override.ok();
+ }
+
+ // Data directory containing current workspace can be overwritten by an env var
+ let datadir = match env::var("TF_DATA_DIR") {
+ Ok(s) => PathBuf::from(s),
+ Err(_) => cwd.join(".terraform"),
+ };
+ match utils::read_file(datadir.join("environment")) {
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => Some("default".to_string()),
+ Ok(s) => Some(s),
+ _ => None,
+ }
+}
+
+fn format_terraform_version(version: &str) -> Option<String> {
+ // `terraform version` output looks like this
+ // Terraform v0.12.14
+ // With potential extra output if it detects you are not running the latest version
+ Some(
+ version
+ .lines()
+ .next()?
+ .trim_start_matches("Terraform ")
+ .trim()
+ .to_owned()
+ + " ",
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_format_terraform_version_release() {
+ let input = "Terraform v0.12.14";
+ assert_eq!(
+ format_terraform_version(input),
+ Some("v0.12.14 ".to_string())
+ );
+ }
+
+ #[test]
+ fn test_format_terraform_version_prerelease() {
+ let input = "Terraform v0.12.14-rc1";
+ assert_eq!(
+ format_terraform_version(input),
+ Some("v0.12.14-rc1 ".to_string())
+ );
+ }
+
+ #[test]
+ fn test_format_terraform_version_development() {
+ let input = "Terraform v0.12.14-dev (cca89f74)";
+ assert_eq!(
+ format_terraform_version(input),
+ Some("v0.12.14-dev (cca89f74) ".to_string())
+ );
+ }
+
+ #[test]
+ fn test_format_terraform_version_multiline() {
+ let input = "Terraform v0.12.13
+
+Your version of Terraform is out of date! The latest version
+is 0.12.14. You can update by downloading from www.terraform.io/downloads.html
+
+";
+ assert_eq!(
+ format_terraform_version(input),
+ Some("v0.12.13 ".to_string())
+ );
+ }
+}
diff --git a/tests/Dockerfile b/tests/Dockerfile
index 12372e24f..8043d9dcb 100644
--- a/tests/Dockerfile
+++ b/tests/Dockerfile
@@ -77,6 +77,18 @@ RUN php --version
RUN HGPYTHON3=1 pip install mercurial
# Check that Mercurial was correctly installed
RUN hg --version
+# Install Terraform
+ENV TERRAFORM_HOME /home/nonroot/terraform
+ENV TERRAFORM_VERSION 0.12.14
+ENV PATH ${TERRAFORM_HOME}:${PATH}
+
+RUN mkdir -p ${TERRAFORM_HOME} \
+ && terraform_download="${TERRAFORM_HOME}/terraform.zip" \
+ && curl -SL --output "${terraform_download}" "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" \
+ && unzip "${terraform_download}" -d "${TERRAFORM_HOME}" \
+ && rm "${terraform_download}"
+# Check that terraform was correctly installed
+RUN terraform version
# Create blank project
RUN USER=nonroot cargo new --bin /src/starship
diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs
index 214f8b013..f8192f41f 100644
--- a/tests/testsuite/main.rs
+++ b/tests/testsuite/main.rs
@@ -21,5 +21,6 @@ mod nix_shell;
mod nodejs;
mod python;
mod ruby;
+mod terraform;
mod time;
mod username;
diff --git a/tests/testsuite/terraform.rs b/tests/testsuite/terraform.rs
new file mode 100644
index 000000000..ef6d16b55
--- /dev/null
+++ b/tests/testsuite/terraform.rs
@@ -0,0 +1,174 @@
+use ansi_term::Color;
+use std::fs::{self, File};
+use std::io::{self, Write};
+use tempfile;
+
+use crate::common;
+use crate::common::TestCommand;
+
+#[test]
+fn folder_without_dotterraform() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = "";
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_tf_file() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("main.tf"))?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 default"));
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_workspace_override() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("main.tf"))?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .env_clear()
+ .env("TF_WORKSPACE", "development")
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_datadir_override() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("main.tf"))?;
+
+ let datadir = tempfile::tempdir()?;
+ let mut file = File::create(datadir.path().join("environment"))?;
+ file.write_all(b"development")?;
+ file.sync_all()?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .env_clear()
+ .env("TF_DATA_DIR", datadir.path())
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_dotterraform_no_environment() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let tf_dir = dir.path().join(".terraform");
+ fs::create_dir(&tf_dir)?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 default"));
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_dotterraform_with_environment() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let tf_dir = dir.path().join(".terraform");
+ fs::create_dir(&tf_dir)?;
+ let mut file = File::create(tf_dir.join("environment"))?;
+ file.write_all(b"development")?;
+ file.sync_all()?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_dotterraform_with_version_no_environment() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let tf_dir = dir.path().join(".terraform");
+ fs::create_dir(&tf_dir)?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .use_config(toml::toml! {
+ [terraform]
+ show_version = true
+ })
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!(
+ "via {} ",
+ Color::Fixed(105).bold().paint("💠 v0.12.14 default")
+ );
+ assert_eq!(expected, actual);
+ Ok(())
+}
+
+#[test]
+#[ignore]
+fn folder_with_dotterraform_with_version_with_environment() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let tf_dir = dir.path().join(".terraform");
+ fs::create_dir(&tf_dir)?;
+ let mut file = File::create(tf_dir.join("environment"))?;
+ file.write_all(b"development")?;
+ file.sync_all()?;
+
+ let output = common::render_module("terraform")
+ .arg("--path")
+ .arg(dir.path())
+ .use_config(toml::toml! {
+ [terraform]
+ show_version = true
+ })
+ .output()?;
+ let actual = String::from_utf8(output.stdout).unwrap();
+
+ let expected = format!(
+ "via {} ",
+ Color::Fixed(105).bold().paint("💠 v0.12.14 development")
+ );
+ assert_eq!(expected, actual);
+ Ok(())
+}