mirror of
https://github.com/starship/starship.git
synced 2024-12-04 22:32:33 +01:00
Merge branch 'master' into conditional-style
This commit is contained in:
commit
de14c8101e
@ -41,9 +41,9 @@
|
||||
"target/"
|
||||
],
|
||||
"plugins": [
|
||||
"https://github.com/dprint/dprint-plugin-typescript/releases/download/0.86.2/plugin.wasm",
|
||||
"https://github.com/dprint/dprint-plugin-typescript/releases/download/0.87.1/plugin.wasm",
|
||||
"https://github.com/dprint/dprint-plugin-json/releases/download/0.17.4/plugin.wasm",
|
||||
"https://github.com/dprint/dprint-plugin-markdown/releases/download/0.16.0/plugin.wasm",
|
||||
"https://github.com/dprint/dprint-plugin-markdown/releases/download/0.16.1/plugin.wasm",
|
||||
"https://github.com/dprint/dprint-plugin-toml/releases/download/0.5.4/plugin.wasm"
|
||||
]
|
||||
}
|
||||
|
101
.github/config-schema.json
vendored
101
.github/config-schema.json
vendored
@ -527,6 +527,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"fossil_metrics": {
|
||||
"default": {
|
||||
"added_style": "bold green",
|
||||
"deleted_style": "bold red",
|
||||
"disabled": true,
|
||||
"format": "([+$added]($added_style) )([-$deleted]($deleted_style) )",
|
||||
"only_nonzero_diffs": true
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/FossilMetricsConfig"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gcloud": {
|
||||
"default": {
|
||||
"detect_env_vars": [],
|
||||
@ -791,6 +805,7 @@
|
||||
},
|
||||
"hostname": {
|
||||
"default": {
|
||||
"detect_env_vars": [],
|
||||
"disabled": false,
|
||||
"format": "[$ssh_symbol$hostname]($style) in ",
|
||||
"ssh_only": true,
|
||||
@ -899,6 +914,7 @@
|
||||
"kubernetes": {
|
||||
"default": {
|
||||
"context_aliases": {},
|
||||
"contexts": [],
|
||||
"detect_extensions": [],
|
||||
"detect_files": [],
|
||||
"detect_folders": [],
|
||||
@ -3150,6 +3166,32 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"FossilMetricsConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"default": "([+$added]($added_style) )([-$deleted]($deleted_style) )",
|
||||
"type": "string"
|
||||
},
|
||||
"added_style": {
|
||||
"default": "bold green",
|
||||
"type": "string"
|
||||
},
|
||||
"deleted_style": {
|
||||
"default": "bold red",
|
||||
"type": "string"
|
||||
},
|
||||
"only_nonzero_diffs": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"disabled": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"GcloudConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -3781,6 +3823,13 @@
|
||||
"default": ".",
|
||||
"type": "string"
|
||||
},
|
||||
"detect_env_vars": {
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"default": "[$ssh_symbol$hostname]($style) in ",
|
||||
"type": "string"
|
||||
@ -4055,6 +4104,58 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"contexts": {
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/KubernetesContextConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"KubernetesContextConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_pattern": {
|
||||
"default": "",
|
||||
"type": "string"
|
||||
},
|
||||
"user_pattern": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"symbol": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"style": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"context_alias": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"user_alias": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
4
.github/workflows/format-workflow.yml
vendored
4
.github/workflows/format-workflow.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Docs | Format
|
||||
uses: dprint/check@v2.2
|
||||
|
||||
@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Install | Taplo
|
||||
run: cargo install --debug --locked --version 0.8.1 taplo-cli
|
||||
- name: Presets | Validate with schema
|
||||
|
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Node
|
||||
uses: actions/setup-node@v3
|
||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -89,7 +89,7 @@ jobs:
|
||||
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
@ -178,7 +178,7 @@ jobs:
|
||||
KEYCHAIN_ENTRY: AC_PASSWORD
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
with:
|
||||
# Required to include the recently merged Crowdin PR
|
||||
ref: master
|
||||
@ -284,7 +284,7 @@ jobs:
|
||||
if: ${{ needs.release_please.outputs.release_created == 'true' }}
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -298,7 +298,7 @@ jobs:
|
||||
needs: [release_please, upload_artifacts]
|
||||
if: ${{ needs.release_please.outputs.release_created == 'true' }}
|
||||
steps:
|
||||
- uses: mislav/bump-homebrew-formula-action@v2.2
|
||||
- uses: mislav/bump-homebrew-formula-action@v2.3
|
||||
with:
|
||||
formula-name: starship
|
||||
tag-name: ${{ needs.release_please.outputs.tag_name }}
|
||||
@ -329,7 +329,7 @@ jobs:
|
||||
if: ${{ needs.release_please.outputs.release_created == 'true' }}
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Setup | Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- run: pwsh ./install/windows/choco/update.ps1
|
||||
@ -345,7 +345,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Merge | Merge Crowdin PR
|
||||
run: gh pr merge i18n_master --squash --repo=starship/starship
|
||||
env:
|
||||
@ -357,7 +357,7 @@ jobs:
|
||||
needs: merge_crowdin_pr
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Trigger workflow dispatch
|
||||
run: gh workflow run publish-docs.yml
|
||||
env:
|
||||
|
2
.github/workflows/security-audit.yml
vendored
2
.github/workflows/security-audit.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Test | Security Audit
|
||||
uses: EmbarkStudios/cargo-deny-action@v1.5.4
|
||||
with:
|
||||
|
4
.github/workflows/spell-check.yml
vendored
4
.github/workflows/spell-check.yml
vendored
@ -6,5 +6,5 @@ jobs:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: crate-ci/typos@v1.16.8
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- uses: crate-ci/typos@v1.16.11
|
||||
|
16
.github/workflows/workflow.yml
vendored
16
.github/workflows/workflow.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -62,7 +62,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -80,7 +80,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -120,7 +120,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
- name: Setup | Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@ -132,7 +132,7 @@ jobs:
|
||||
run: cargo run --locked --features config-schema -- config-schema > .github/config-schema.json
|
||||
|
||||
- name: Check | Detect Changes
|
||||
uses: reviewdog/action-suggester@v1.7.4
|
||||
uses: reviewdog/action-suggester@v1.8.0
|
||||
with:
|
||||
tool_name: starship config-schema
|
||||
filter_mode: nofilter
|
||||
@ -156,7 +156,7 @@ jobs:
|
||||
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
||||
steps:
|
||||
- name: Setup | Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
|
||||
# Install all the required dependencies for testing
|
||||
- name: Setup | Rust
|
||||
|
503
Cargo.lock
generated
503
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
28
Cargo.toml
@ -42,16 +42,16 @@ gix-max-perf = ["gix-features/zlib-ng", "gix/fast-sha1"]
|
||||
gix-faster = ["gix-features/zlib-stock", "gix/fast-sha1"]
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.26", default-features = false, features = ["clock", "std", "wasmbind"] }
|
||||
clap = { version = "4.4.1", features = ["derive", "cargo", "unicode"] }
|
||||
clap_complete = "4.4.0"
|
||||
chrono = { version = "0.4.30", default-features = false, features = ["clock", "std", "wasmbind"] }
|
||||
clap = { version = "4.4.3", features = ["derive", "cargo", "unicode"] }
|
||||
clap_complete = "4.4.1"
|
||||
dirs-next = "2.0.0"
|
||||
dunce = "1.0.4"
|
||||
gethostname = "0.4.3"
|
||||
# default feature restriction addresses https://github.com/starship/starship/issues/4251
|
||||
gix = { version = "0.52.0", default-features = false, features = ["max-performance-safe"] }
|
||||
gix-features = { version = "0.33.0", optional = true }
|
||||
indexmap = { version = "1.9.3", features = ["serde"] }
|
||||
gix = { version = "0.53.1", default-features = false, features = ["max-performance-safe", "revision"] }
|
||||
gix-features = { version = "0.34.0", optional = true }
|
||||
indexmap = { version = "2.0.0", features = ["serde"] }
|
||||
log = { version = "0.4.20", features = ["std"] }
|
||||
# notify-rust is optional (on by default) because the crate doesn't currently build for darwin with nix
|
||||
# see: https://github.com/NixOS/nixpkgs/issues/160876
|
||||
@ -62,16 +62,16 @@ open = "5.0.0"
|
||||
# update os module config and tests when upgrading os_info
|
||||
os_info = "3.7.0"
|
||||
path-slash = "0.2.1"
|
||||
pest = "2.7.2"
|
||||
pest_derive = "2.7.2"
|
||||
pest = "2.7.3"
|
||||
pest_derive = "2.7.3"
|
||||
quick-xml = "0.30.0"
|
||||
rand = "0.8.5"
|
||||
rayon = "1.7.0"
|
||||
regex = { version = "1.9.4", default-features = false, features = ["perf", "std", "unicode-perl"] }
|
||||
regex = { version = "1.9.5", default-features = false, features = ["perf", "std", "unicode-perl"] }
|
||||
rust-ini = "0.19.0"
|
||||
semver = "1.0.18"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0.105"
|
||||
serde_json = "1.0.107"
|
||||
sha1 = "0.10.5"
|
||||
shadow-rs = { version = "0.23.0", default-features = false }
|
||||
# battery is optional (on by default) because the crate doesn't currently build for Termux
|
||||
@ -80,13 +80,13 @@ starship-battery = { version = "0.8.2", optional = true }
|
||||
strsim = "0.10.0"
|
||||
systemstat = "=0.2.3"
|
||||
terminal_size = "0.2.6"
|
||||
toml = { version = "0.7.6", features = ["preserve_order"] }
|
||||
toml_edit = "0.19.14"
|
||||
toml = { version = "0.8.0", features = ["preserve_order"] }
|
||||
toml_edit = "0.20.0"
|
||||
unicode-segmentation = "1.10.1"
|
||||
unicode-width = "0.1.10"
|
||||
urlencoding = "2.1.3"
|
||||
versions = "5.0.1"
|
||||
which = "4.4.0"
|
||||
which = "4.4.2"
|
||||
yaml-rust = "0.4.5"
|
||||
|
||||
process_control = { version = "4.0.3", features = ["crossbeam-channel"] }
|
||||
@ -98,7 +98,7 @@ shell-words = "1.1.0"
|
||||
[dependencies.schemars]
|
||||
version = "0.8.13"
|
||||
optional = true
|
||||
features = ["preserve_order", "indexmap"]
|
||||
features = ["preserve_order", "indexmap2"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
deelevate = "0.2.0"
|
||||
|
BIN
docs/.vuepress/public/presets/img/jetpack.png
Normal file
BIN
docs/.vuepress/public/presets/img/jetpack.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
284
docs/.vuepress/public/presets/toml/jetpack.toml
Normal file
284
docs/.vuepress/public/presets/toml/jetpack.toml
Normal file
@ -0,0 +1,284 @@
|
||||
format = """
|
||||
$cmd_duration\
|
||||
$hostname\
|
||||
$localip\
|
||||
$shlvl\
|
||||
$shell\
|
||||
$env_var\
|
||||
$username\
|
||||
$sudo\
|
||||
$character\
|
||||
"""
|
||||
|
||||
right_format = """
|
||||
$singularity\
|
||||
$kubernetes\
|
||||
$directory\
|
||||
$vcsh\
|
||||
$fossil_branch\
|
||||
$git_branch\
|
||||
$git_commit\
|
||||
$git_state\
|
||||
$git_metrics\
|
||||
$git_status\
|
||||
$hg_branch\
|
||||
$pijul_channel\
|
||||
$docker_context\
|
||||
$package\
|
||||
$c\
|
||||
$cmake\
|
||||
$cobol\
|
||||
$daml\
|
||||
$dart\
|
||||
$deno\
|
||||
$dotnet\
|
||||
$elixir\
|
||||
$elm\
|
||||
$erlang\
|
||||
$fennel\
|
||||
$golang\
|
||||
$guix_shell\
|
||||
$haskell\
|
||||
$haxe\
|
||||
$helm\
|
||||
$java\
|
||||
$julia\
|
||||
$kotlin\
|
||||
$gradle\
|
||||
$lua\
|
||||
$nim\
|
||||
$nodejs\
|
||||
$ocaml\
|
||||
$opa\
|
||||
$perl\
|
||||
$php\
|
||||
$pulumi\
|
||||
$purescript\
|
||||
$python\
|
||||
$raku\
|
||||
$rlang\
|
||||
$red\
|
||||
$ruby\
|
||||
$rust\
|
||||
$scala\
|
||||
$solidity\
|
||||
$swift\
|
||||
$terraform\
|
||||
$vlang\
|
||||
$vagrant\
|
||||
$zig\
|
||||
$buf\
|
||||
$nix_shell\
|
||||
$conda\
|
||||
$meson\
|
||||
$spack\
|
||||
$memory_usage\
|
||||
$aws\
|
||||
$gcloud\
|
||||
$openstack\
|
||||
$azure\
|
||||
$crystal\
|
||||
$custom\
|
||||
$jobs\
|
||||
$status\
|
||||
$os\
|
||||
$container\
|
||||
$battery\
|
||||
$time\
|
||||
"""
|
||||
|
||||
add_newline = true
|
||||
|
||||
[character]
|
||||
format = "$symbol "
|
||||
success_symbol = "[◉](bold italic bright-yellow)"
|
||||
error_symbol = "[⊘](italic purple)"
|
||||
|
||||
[env_var.VIMSHELL] # vim subshell
|
||||
format = "[$env_value]($style)"
|
||||
style = 'green italic'
|
||||
|
||||
[sudo]
|
||||
format = "[$symbol]($style)"
|
||||
style = "italic bright-purple"
|
||||
symbol = "◇┈"
|
||||
disabled = false
|
||||
|
||||
[username]
|
||||
style_user = "yellow bold"
|
||||
style_root = "purple bold italic"
|
||||
format = "[$user]($style) ▻ "
|
||||
disabled = false
|
||||
|
||||
[directory]
|
||||
home_symbol = "⌂"
|
||||
truncation_length = 2
|
||||
truncation_symbol = "▦ "
|
||||
read_only = " ■"
|
||||
style = "italic blue"
|
||||
format = ' [$path]($style)[$read_only]($read_only_style)'
|
||||
|
||||
[cmd_duration]
|
||||
min_time = 500
|
||||
format = "[$duration ](italic bright-yellow)"
|
||||
|
||||
[jobs]
|
||||
format = "[ $symbol$number]($style)"
|
||||
style = "white"
|
||||
symbol = "[▶ ](blue italic)"
|
||||
|
||||
[localip]
|
||||
ssh_only = true
|
||||
format = " ◯[$localipv4](bold magenta)"
|
||||
disabled = false
|
||||
|
||||
[time]
|
||||
disabled = false
|
||||
format = "[ $time]($style)"
|
||||
time_format = "%R"
|
||||
utc_time_offset = "local"
|
||||
style = "dimmed white"
|
||||
|
||||
[battery]
|
||||
format = "[ $percentage $symbol]($style)"
|
||||
full_symbol = "[█](italic green)"
|
||||
charging_symbol = "[↑](italic green)"
|
||||
discharging_symbol = "[↓](italic)"
|
||||
unknown_symbol = "[░](italic)"
|
||||
empty_symbol = "[▃](italic red)"
|
||||
|
||||
[[battery.display]]
|
||||
threshold = 40
|
||||
style = "dimmed yellow"
|
||||
|
||||
[[battery.display]]
|
||||
threshold = 70
|
||||
style = "dimmed white"
|
||||
|
||||
[git_branch]
|
||||
format = "[ $symbol $branch(:$remote_branch)]($style)"
|
||||
symbol = "[◬](bold bright-blue)"
|
||||
style = "bold italic bright-blue"
|
||||
|
||||
[git_status]
|
||||
style = "italic bright-blue"
|
||||
format = """([⎪$ahead_behind$staged$modified$untracked$renamed$deleted$conflicted$stashed⎥]($style))"""
|
||||
conflicted = "[◪◦](italic bright-magenta)"
|
||||
ahead = "[▲│[${count}](bold white)│](italic green)"
|
||||
behind = "[▽│[${count}](bold white)│](italic red)"
|
||||
diverged = "[◇ ▲┤[${ahead_count}](regular white)│▽┤[${behind_count}](regular white)│](italic bright-magenta)"
|
||||
untracked = "[◌◦](italic bright-yellow)"
|
||||
stashed = "[◦◫◦](italic white)"
|
||||
modified = "[●◦](italic yellow)"
|
||||
staged = "[■┤[$count](bold white)│](italic bright-cyan)"
|
||||
renamed = "[◎◦](italic bright-blue)"
|
||||
deleted = "[✕](italic red)"
|
||||
|
||||
[deno]
|
||||
format = " deno [∫ $version](blue italic)"
|
||||
version_format = "${major}.${minor}"
|
||||
|
||||
[lua]
|
||||
format = " lua [${symbol}${version}]($style)"
|
||||
symbol = "⨀ "
|
||||
style = "italic bright-yellow"
|
||||
|
||||
[nodejs]
|
||||
format = " node [◫ ($version)](italic bright-green)"
|
||||
detect_files = ["package-lock.json", "yarn.lock"]
|
||||
version_format = "${major}.${minor}"
|
||||
|
||||
[python]
|
||||
format = " py [${symbol}${version}]($style)"
|
||||
symbol = "[⌉](italic bright-blue)⌊ "
|
||||
version_format = "${major}.${minor}"
|
||||
style = "italic bright-yellow"
|
||||
|
||||
[ruby]
|
||||
format = " rb [${symbol}${version}]($style)"
|
||||
symbol = "◆ "
|
||||
version_format = "${major}.${minor}"
|
||||
style = "italic red"
|
||||
|
||||
[rust]
|
||||
format = " rs [$symbol$version]($style)"
|
||||
symbol = "⊃ "
|
||||
version_format = "${major}.${minor}"
|
||||
style = "italic red"
|
||||
|
||||
[package]
|
||||
format = " pkg [$symbol$version]($style)"
|
||||
version_format = "${major}.${minor}"
|
||||
symbol = "◫ "
|
||||
style = "bright-yellow italic"
|
||||
|
||||
[swift]
|
||||
format = " sw [${symbol}${version}]($style)"
|
||||
symbol = "◁ "
|
||||
style = "italic bright-red"
|
||||
version_format = "${major}.${minor}"
|
||||
|
||||
[aws]
|
||||
format = " aws [$symbol $profile $region]($style)"
|
||||
style = "italic blue"
|
||||
symbol = "▲ "
|
||||
|
||||
[buf]
|
||||
symbol = "■ "
|
||||
format = " buf [$symbol $version $buf_version]($style)"
|
||||
|
||||
[c]
|
||||
symbol = "∁ "
|
||||
format = " c [$symbol($version(-$name))]($style)"
|
||||
|
||||
[conda]
|
||||
symbol = "◯ "
|
||||
format = " conda [$symbol$environment]($style)"
|
||||
|
||||
[dart]
|
||||
symbol = "◁◅ "
|
||||
format = " dart [$symbol($version )]($style)"
|
||||
|
||||
[docker_context]
|
||||
symbol = "◧ "
|
||||
format = " docker [$symbol$context]($style)"
|
||||
|
||||
[elixir]
|
||||
symbol = "△ "
|
||||
format = " exs [$symbol $version OTP $otp_version ]($style)"
|
||||
|
||||
[elm]
|
||||
symbol = "◩ "
|
||||
format = " elm [$symbol($version )]($style)"
|
||||
|
||||
[golang]
|
||||
symbol = "∩ "
|
||||
format = " go [$symbol($version )]($style)"
|
||||
|
||||
[haskell]
|
||||
symbol = "❯λ "
|
||||
format = " hs [$symbol($version )]($style)"
|
||||
|
||||
[java]
|
||||
symbol = "∪ "
|
||||
format = " java [${symbol}(${version} )]($style)"
|
||||
|
||||
[julia]
|
||||
symbol = "◎ "
|
||||
format = " jl [$symbol($version )]($style)"
|
||||
|
||||
[memory_usage]
|
||||
symbol = "▪▫▪ "
|
||||
format = " mem [${ram}( ${swap})]($style)"
|
||||
|
||||
[nim]
|
||||
symbol = "▴▲▴ "
|
||||
format = " nim [$symbol($version )]($style)"
|
||||
|
||||
[nix_shell]
|
||||
symbol = "⊛ "
|
||||
format = " nix [$symbol$state $name]($style)"
|
||||
|
||||
[spack]
|
||||
symbol = "◇ "
|
||||
format = " spack [$symbol$environment]($style)"
|
@ -266,6 +266,7 @@ $kubernetes\
|
||||
$directory\
|
||||
$vcsh\
|
||||
$fossil_branch\
|
||||
$fossil_metrics\
|
||||
$git_branch\
|
||||
$git_commit\
|
||||
$git_state\
|
||||
@ -1604,6 +1605,41 @@ truncation_length = 4
|
||||
truncation_symbol = ''
|
||||
```
|
||||
|
||||
## Fossil Metrics
|
||||
|
||||
The `fossil_metrics` module will show the number of added and deleted lines in the check-out in your current directory. At least v2.14 (2021-01-20) of Fossil is required.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ------------------------------------------------------------ | ------------------------------------- |
|
||||
| `format` | `'([+$added]($added_style) )([-$deleted]($deleted_style) )'` | The format for the module. |
|
||||
| `added_style` | `'bold green'` | The style for the added count. |
|
||||
| `deleted_style` | `'bold red'` | The style for the deleted count. |
|
||||
| `only_nonzero_diffs` | `true` | Render status only for changed items. |
|
||||
| `disabled` | `true` | Disables the `fossil_metrics` module. |
|
||||
|
||||
### Variables
|
||||
|
||||
| Variable | Example | Description |
|
||||
| --------------- | ------- | ------------------------------------------- |
|
||||
| added | `1` | The current number of added lines |
|
||||
| deleted | `2` | The current number of deleted lines |
|
||||
| added_style\* | | Mirrors the value of option `added_style` |
|
||||
| deleted_style\* | | Mirrors the value of option `deleted_style` |
|
||||
|
||||
*: This variable can only be used as a part of a style string
|
||||
|
||||
### Example
|
||||
|
||||
```toml
|
||||
# ~/.config/starship.toml
|
||||
|
||||
[fossil_metrics]
|
||||
added_style = 'bold blue'
|
||||
format = '[+$added]($added_style)/[-$deleted]($deleted_style) '
|
||||
```
|
||||
|
||||
## Google Cloud (`gcloud`)
|
||||
|
||||
The `gcloud` module shows the current configuration for [`gcloud`](https://cloud.google.com/sdk/gcloud) CLI.
|
||||
@ -2202,14 +2238,15 @@ The `hostname` module shows the system hostname.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `ssh_only` | `true` | Only show hostname when connected to an SSH session. |
|
||||
| `ssh_symbol` | `'🌐 '` | A format string representing the symbol when connected to SSH session. |
|
||||
| `trim_at` | `'.'` | String that the hostname is cut off at, after the first match. `'.'` will stop after the first dot. `''` will disable any truncation |
|
||||
| `format` | `'[$ssh_symbol$hostname]($style) in '` | The format for the module. |
|
||||
| `style` | `'bold dimmed green'` | The style for the module. |
|
||||
| `disabled` | `false` | Disables the `hostname` module. |
|
||||
| Option | Default | Description |
|
||||
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ssh_only` | `true` | Only show hostname when connected to an SSH session. |
|
||||
| `ssh_symbol` | `'🌐 '` | A format string representing the symbol when connected to SSH session. |
|
||||
| `trim_at` | `'.'` | String that the hostname is cut off at, after the first match. `'.'` will stop after the first dot. `''` will disable any truncation. |
|
||||
| `detect_env_vars` | `[]` | Which environment variable(s) should trigger this module. |
|
||||
| `format` | `'[$ssh_symbol$hostname]($style) in '` | The format for the module. |
|
||||
| `style` | `'bold dimmed green'` | The style for the module. |
|
||||
| `disabled` | `false` | Disables the `hostname` module. |
|
||||
|
||||
### Variables
|
||||
|
||||
@ -2221,7 +2258,9 @@ The `hostname` module shows the system hostname.
|
||||
|
||||
*: This variable can only be used as a part of a style string
|
||||
|
||||
### Example
|
||||
### Examples
|
||||
|
||||
#### Always show the hostname
|
||||
|
||||
```toml
|
||||
# ~/.config/starship.toml
|
||||
@ -2233,6 +2272,17 @@ trim_at = '.companyname.com'
|
||||
disabled = false
|
||||
```
|
||||
|
||||
#### Hide the hostname in remote tmux sessions
|
||||
|
||||
```toml
|
||||
# ~/.config/starship.toml
|
||||
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
detect_env_vars = ['!TMUX', 'SSH_CONNECTION']
|
||||
disabled = false
|
||||
```
|
||||
|
||||
## Java
|
||||
|
||||
The `java` module shows the currently installed version of [Java](https://www.oracle.com/java/).
|
||||
@ -2434,7 +2484,8 @@ kotlin_binary = 'kotlinc'
|
||||
Displays the current [Kubernetes context](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context) name and, if set, the namespace, user and cluster from the kubeconfig file.
|
||||
The namespace needs to be set in the kubeconfig file, this can be done via
|
||||
`kubectl config set-context starship-context --namespace astronaut`.
|
||||
Similarly the user and cluster can be set with `kubectl config set-context starship-context --user starship-user` and `kubectl config set-context starship-context --cluster starship-cluster`.
|
||||
Similarly, the user and cluster can be set with `kubectl config set-context starship-context --user starship-user`
|
||||
and `kubectl config set-context starship-context --cluster starship-cluster`.
|
||||
If the `$KUBECONFIG` env var is set the module will use that if not it will use the `~/.kube/config`.
|
||||
|
||||
::: tip
|
||||
@ -2450,18 +2501,45 @@ case the module will only be active in directories that match those conditions.
|
||||
|
||||
### Options
|
||||
|
||||
::: warning
|
||||
|
||||
The `context_aliases` and `user_aliases` options are deprecated. Use `contexts` and the corresponding `context_alias`
|
||||
and `user_alias` options instead.
|
||||
|
||||
:::
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------------- | -------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `symbol` | `'☸ '` | A format string representing the symbol displayed before the Cluster. |
|
||||
| `format` | `'[$symbol$context( \($namespace\))]($style) in '` | The format for the module. |
|
||||
| `style` | `'cyan bold'` | The style for the module. |
|
||||
| `context_aliases` | `{}` | Table of context aliases to display. |
|
||||
| `user_aliases` | `{}` | Table of user aliases to display. |
|
||||
| `context_aliases`* | `{}` | Table of context aliases to display. |
|
||||
| `user_aliases`* | `{}` | Table of user aliases to display. |
|
||||
| `detect_extensions` | `[]` | Which extensions should trigger this module. |
|
||||
| `detect_files` | `[]` | Which filenames should trigger this module. |
|
||||
| `detect_folders` | `[]` | Which folders should trigger this modules. |
|
||||
| `contexts` | `[]` | Customized styles and symbols for specific contexts. |
|
||||
| `disabled` | `true` | Disables the `kubernetes` module. |
|
||||
|
||||
*: This option is deprecated, please add `contexts` with the corresponding `context_alias` and `user_alias` options instead.
|
||||
|
||||
To customize the style of the module for specific environments, use the following configuration as
|
||||
part of the `contexts` list:
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `context_pattern` | **Required** Regular expression to match current Kubernetes context name. |
|
||||
| `user_pattern` | Regular expression to match current Kubernetes user name. |
|
||||
| `context_alias` | Context alias to display instead of the full context name. |
|
||||
| `user_alias` | User alias to display instead of the full user name. |
|
||||
| `style` | The style for the module when using this context. If not set, will use module's style. |
|
||||
| `symbol` | The symbol for the module when using this context. If not set, will use module's symbol. |
|
||||
|
||||
Note that all regular expression are anchored with `^<pattern>$` and so must match the whole string. The `*_pattern`
|
||||
regular expressions may contain capture groups, which can be referenced in the corresponding alias via `$name` and `$N`
|
||||
(see example below and the
|
||||
[rust Regex::replace() documentation](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replace)).
|
||||
|
||||
### Variables
|
||||
|
||||
| Variable | Example | Description |
|
||||
@ -2483,13 +2561,9 @@ case the module will only be active in directories that match those conditions.
|
||||
[kubernetes]
|
||||
format = 'on [⛵ ($user on )($cluster in )$context \($namespace\)](dimmed green) '
|
||||
disabled = false
|
||||
[kubernetes.context_aliases]
|
||||
'dev.local.cluster.k8s' = 'dev'
|
||||
'.*/openshift-cluster/.*' = 'openshift'
|
||||
'gke_.*_(?P<var_cluster>[\w-]+)' = 'gke-$var_cluster'
|
||||
[kubernetes.user_aliases]
|
||||
'dev.local.cluster.k8s' = 'dev'
|
||||
'root/.*' = 'root'
|
||||
contexts = [
|
||||
{ context_pattern = "dev.local.cluster.k8s", style = "green", symbol = "💔 " },
|
||||
]
|
||||
```
|
||||
|
||||
Only show the module in directories that contain a `k8s` file.
|
||||
@ -2502,29 +2576,37 @@ disabled = false
|
||||
detect_files = ['k8s']
|
||||
```
|
||||
|
||||
#### Regex Matching
|
||||
#### Kubernetes Context specific config
|
||||
|
||||
Additional to simple aliasing, `context_aliases` and `user_aliases` also supports
|
||||
extended matching and renaming using regular expressions.
|
||||
|
||||
The regular expression must match on the entire kube context,
|
||||
capture groups can be referenced using `$name` and `$N` in the replacement.
|
||||
This is more explained in the [regex crate](https://docs.rs/regex/1.5.4/regex/struct.Regex.html#method.replace) documentation.
|
||||
|
||||
Long and automatically generated cluster names can be identified
|
||||
and shortened using regular expressions:
|
||||
The `contexts` configuration option is used to customise what the current Kubernetes context name looks
|
||||
like (style and symbol) if the name matches the defined regular expression.
|
||||
|
||||
```toml
|
||||
[kubernetes.context_aliases]
|
||||
# OpenShift contexts carry the namespace and user in the kube context: `namespace/name/user`:
|
||||
'.*/openshift-cluster/.*' = 'openshift'
|
||||
# Or better, to rename every OpenShift cluster at once:
|
||||
'.*/(?P<var_cluster>[\w-]+)/.*' = '$var_cluster'
|
||||
# ~/.config/starship.toml
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
# "bold red" style + default symbol when Kubernetes current context name equals "production" *and* the current user
|
||||
# equals "admin_user"
|
||||
context_pattern = "production"
|
||||
user_pattern = "admin_user"
|
||||
style = "bold red"
|
||||
context_alias = "prod"
|
||||
user_alias = "admin"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
# "green" style + a different symbol when Kubernetes current context name contains openshift
|
||||
context_pattern = ".*openshift.*"
|
||||
style = "green"
|
||||
symbol = "💔 "
|
||||
context_alias = "openshift"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
# Using capture groups
|
||||
# Contexts from GKE, AWS and other cloud providers usually carry additional information, like the region/zone.
|
||||
# The following entry matches on the GKE format (`gke_projectname_zone_cluster-name`)
|
||||
# and renames every matching kube context into a more readable format (`gke-cluster-name`):
|
||||
'gke_.*_(?P<var_cluster>[\w-]+)' = 'gke-$var_cluster'
|
||||
context_pattern = "gke_.*_(?P<cluster>[\\w-]+)"
|
||||
context_alias = "gke-$cluster"
|
||||
```
|
||||
|
||||
## Line Break
|
||||
|
24
docs/presets/jetpack.md
Normal file
24
docs/presets/jetpack.md
Normal file
@ -0,0 +1,24 @@
|
||||
[Return to Presets](./README.md#jetpack)
|
||||
|
||||
# Jetpack Preset
|
||||
|
||||
This is a pseudo minimalist preset inspired by the [geometry](https://github.com/geometry-zsh/geometry) and [spaceship](https://github.com/spaceship-prompt/spaceship-prompt) prompts.
|
||||
|
||||
> Jetpack uses the terminal's color theme.
|
||||
|
||||
![Screenshot of Jetpack preset](/presets/img/jetpack.png)
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Requires a shell with [`right-prompt`](https://starship.rs/advanced-config/#enable-right-prompt) support.
|
||||
- [Jetbrains Mono](https://www.jetbrains.com/lp/mono/) is recommended.
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset jetpack -o ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/jetpack.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/jetpack.toml
|
28
src/configs/fossil_metrics.rs
Normal file
28
src/configs/fossil_metrics.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(
|
||||
feature = "config-schema",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(deny_unknown_fields)
|
||||
)]
|
||||
#[serde(default)]
|
||||
pub struct FossilMetricsConfig<'a> {
|
||||
pub format: &'a str,
|
||||
pub added_style: &'a str,
|
||||
pub deleted_style: &'a str,
|
||||
pub only_nonzero_diffs: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> Default for FossilMetricsConfig<'a> {
|
||||
fn default() -> Self {
|
||||
FossilMetricsConfig {
|
||||
format: "([+$added]($added_style) )([-$deleted]($deleted_style) )",
|
||||
added_style: "bold green",
|
||||
deleted_style: "bold red",
|
||||
only_nonzero_diffs: true,
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ pub struct HostnameConfig<'a> {
|
||||
pub ssh_only: bool,
|
||||
pub ssh_symbol: &'a str,
|
||||
pub trim_at: &'a str,
|
||||
pub detect_env_vars: Vec<&'a str>,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
@ -22,6 +23,7 @@ impl<'a> Default for HostnameConfig<'a> {
|
||||
ssh_only: true,
|
||||
ssh_symbol: "🌐 ",
|
||||
trim_at: ".",
|
||||
detect_env_vars: vec![],
|
||||
format: "[$ssh_symbol$hostname]($style) in ",
|
||||
style: "green dimmed bold",
|
||||
disabled: false,
|
||||
|
@ -18,6 +18,7 @@ pub struct KubernetesConfig<'a> {
|
||||
pub detect_extensions: Vec<&'a str>,
|
||||
pub detect_files: Vec<&'a str>,
|
||||
pub detect_folders: Vec<&'a str>,
|
||||
pub contexts: Vec<KubernetesContextConfig<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Default for KubernetesConfig<'a> {
|
||||
@ -32,6 +33,23 @@ impl<'a> Default for KubernetesConfig<'a> {
|
||||
detect_extensions: vec![],
|
||||
detect_files: vec![],
|
||||
detect_folders: vec![],
|
||||
contexts: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default)]
|
||||
#[cfg_attr(
|
||||
feature = "config-schema",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(deny_unknown_fields)
|
||||
)]
|
||||
#[serde(default)]
|
||||
pub struct KubernetesContextConfig<'a> {
|
||||
pub context_pattern: &'a str,
|
||||
pub user_pattern: Option<&'a str>,
|
||||
pub symbol: Option<&'a str>,
|
||||
pub style: Option<&'a str>,
|
||||
pub context_alias: Option<&'a str>,
|
||||
pub user_alias: Option<&'a str>,
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ pub mod erlang;
|
||||
pub mod fennel;
|
||||
pub mod fill;
|
||||
pub mod fossil_branch;
|
||||
pub mod fossil_metrics;
|
||||
pub mod gcloud;
|
||||
pub mod git_branch;
|
||||
pub mod git_commit;
|
||||
@ -159,6 +160,8 @@ pub struct FullConfig<'a> {
|
||||
#[serde(borrow)]
|
||||
fossil_branch: fossil_branch::FossilBranchConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
fossil_metrics: fossil_metrics::FossilMetricsConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
gcloud: gcloud::GcloudConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
git_branch: git_branch::GitBranchConfig<'a>,
|
||||
|
@ -39,6 +39,7 @@ pub const PROMPT_ORDER: &[&str] = &[
|
||||
"directory",
|
||||
"vcsh",
|
||||
"fossil_branch",
|
||||
"fossil_metrics",
|
||||
"git_branch",
|
||||
"git_commit",
|
||||
"git_state",
|
||||
|
@ -235,8 +235,32 @@ impl<'a> Context<'a> {
|
||||
disabled == Some(true)
|
||||
}
|
||||
|
||||
/// Returns true when a negated environment variable is defined in `env_vars` and is present
|
||||
fn has_negated_env_var(&self, env_vars: &'a [&'a str]) -> bool {
|
||||
env_vars
|
||||
.iter()
|
||||
.filter_map(|env_var| env_var.strip_prefix('!'))
|
||||
.any(|env_var| self.get_env(env_var).is_some())
|
||||
}
|
||||
|
||||
/// Returns true if 'detect_env_vars' is empty,
|
||||
/// or if at least one environment variable is set and no negated environment variable is set
|
||||
pub fn detect_env_vars(&'a self, env_vars: &'a [&'a str]) -> bool {
|
||||
env_vars.is_empty() || (env_vars.iter().any(|e| self.get_env(e).is_some()))
|
||||
if env_vars.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.has_negated_env_var(env_vars) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if at least one environment variable is set
|
||||
let mut iter = env_vars
|
||||
.iter()
|
||||
.filter(|env_var| !env_var.starts_with('!'))
|
||||
.peekable();
|
||||
|
||||
iter.peek().is_none() || iter.any(|env_var| self.get_env(env_var).is_some())
|
||||
}
|
||||
|
||||
// returns a new ScanDir struct with reference to current dir_files of context
|
||||
|
@ -153,10 +153,7 @@ pub fn init_stub(shell_name: &str) -> io::Result<()> {
|
||||
"#,
|
||||
starship.sprint_posix()?
|
||||
),
|
||||
"zsh" => print!(
|
||||
r#"source <({} init zsh --print-full-init)"#,
|
||||
starship.sprint_posix()?
|
||||
),
|
||||
"zsh" => print_script(ZSH_INIT, &starship.sprint_posix()?),
|
||||
"fish" => print!(
|
||||
// Fish does process substitution with pipes and psub instead of bash syntax
|
||||
r#"source ({} init fish --print-full-init | psub)"#,
|
||||
|
@ -35,6 +35,7 @@ pub const ALL_MODULES: &[&str] = &[
|
||||
"fennel",
|
||||
"fill",
|
||||
"fossil_branch",
|
||||
"fossil_metrics",
|
||||
"gcloud",
|
||||
"git_branch",
|
||||
"git_commit",
|
||||
|
297
src/modules/fossil_metrics.rs
Normal file
297
src/modules/fossil_metrics.rs
Normal file
@ -0,0 +1,297 @@
|
||||
use regex::Regex;
|
||||
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
|
||||
use crate::configs::fossil_metrics::FossilMetricsConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with currently added/deleted lines in the Fossil check-out in the current
|
||||
/// directory.
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("fossil_metrics");
|
||||
let config = FossilMetricsConfig::try_load(module.config);
|
||||
|
||||
// As we default to disabled=true, we have to check here after loading our config module,
|
||||
// before it was only checking against whatever is in the config starship.toml
|
||||
if config.disabled {
|
||||
return None;
|
||||
};
|
||||
|
||||
let checkout_db = if cfg!(windows) {
|
||||
"_FOSSIL_"
|
||||
} else {
|
||||
".fslckout"
|
||||
};
|
||||
// See if we're in a check-out by scanning upwards for a directory containing the checkout_db file
|
||||
context
|
||||
.begin_ancestor_scan()
|
||||
.set_files(&[checkout_db])
|
||||
.scan()?;
|
||||
|
||||
// Read the total number of added and deleted lines from "fossil diff --numstat"
|
||||
let output = context.exec_cmd("fossil", &["diff", "--numstat"])?.stdout;
|
||||
let stats = FossilDiff::parse(&output, config.only_nonzero_diffs);
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"added_style" => Some(Ok(config.added_style)),
|
||||
"deleted_style" => Some(Ok(config.deleted_style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"added" => Some(Ok(stats.added)),
|
||||
"deleted" => Some(Ok(stats.deleted)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `fossil_metrics`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
/// Represents the parsed output from a Fossil diff with the --numstat option enabled.
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct FossilDiff<'a> {
|
||||
added: &'a str,
|
||||
deleted: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> FossilDiff<'a> {
|
||||
/// Parses the output of `fossil diff --numstat` as a `FossilDiff` struct.
|
||||
pub fn parse(diff_numstat: &'a str, only_nonzero_diffs: bool) -> Self {
|
||||
// Fossil formats the last line of the output as "%10d %10d TOTAL over %d changed files\n"
|
||||
// where the 1st and 2nd placeholders are the number of added and deleted lines respectively
|
||||
let re = Regex::new(r"^\s*(\d+)\s+(\d+) TOTAL over \d+ changed files$").unwrap();
|
||||
|
||||
let (added, deleted) = diff_numstat
|
||||
.lines()
|
||||
.last()
|
||||
.and_then(|s| re.captures(s))
|
||||
.and_then(|caps| {
|
||||
let added = match caps.get(1)?.as_str() {
|
||||
"0" if only_nonzero_diffs => "",
|
||||
s => s,
|
||||
};
|
||||
|
||||
let deleted = match caps.get(2)?.as_str() {
|
||||
"0" if only_nonzero_diffs => "",
|
||||
s => s,
|
||||
};
|
||||
|
||||
Some((added, deleted))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Self { added, deleted }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use nu_ansi_term::{Color, Style};
|
||||
|
||||
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
|
||||
|
||||
use super::FossilDiff;
|
||||
|
||||
enum Expect<'a> {
|
||||
Empty,
|
||||
Added(Option<&'a str>),
|
||||
AddedStyle(Style),
|
||||
Deleted(Option<&'a str>),
|
||||
DeletedStyle(Style),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_nothing_on_empty_dir() -> io::Result<()> {
|
||||
let checkout_dir = tempfile::tempdir()?;
|
||||
|
||||
let actual = ModuleRenderer::new("fossil_metrics")
|
||||
.path(checkout_dir.path())
|
||||
.collect();
|
||||
let expected = None;
|
||||
assert_eq!(expected, actual);
|
||||
|
||||
checkout_dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_metrics_disabled_per_default() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
let checkout_dir = tempdir.path();
|
||||
expect_fossil_metrics_with_config(
|
||||
checkout_dir,
|
||||
Some(toml::toml! {
|
||||
// no "disabled=false" in config!
|
||||
[fossil_metrics]
|
||||
only_nonzero_diffs = false
|
||||
}),
|
||||
&[Expect::Empty],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_metrics_autodisabled() -> io::Result<()> {
|
||||
let tempdir = tempfile::tempdir()?;
|
||||
expect_fossil_metrics_with_config(tempdir.path(), None, &[Expect::Empty]);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_metrics() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
let checkout_dir = tempdir.path();
|
||||
expect_fossil_metrics_with_config(
|
||||
checkout_dir,
|
||||
None,
|
||||
&[Expect::Added(Some("3")), Expect::Deleted(Some("2"))],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_metrics_subdir() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
let checkout_dir = tempdir.path();
|
||||
expect_fossil_metrics_with_config(
|
||||
&checkout_dir.join("subdir"),
|
||||
None,
|
||||
&[Expect::Added(Some("3")), Expect::Deleted(Some("2"))],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_metrics_configured() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
let checkout_dir = tempdir.path();
|
||||
expect_fossil_metrics_with_config(
|
||||
checkout_dir,
|
||||
Some(toml::toml! {
|
||||
[fossil_metrics]
|
||||
added_style = "underline blue"
|
||||
deleted_style = "underline purple"
|
||||
disabled = false
|
||||
}),
|
||||
&[
|
||||
Expect::Added(Some("3")),
|
||||
Expect::AddedStyle(Color::Blue.underline()),
|
||||
Expect::Deleted(Some("2")),
|
||||
Expect::DeletedStyle(Color::Purple.underline()),
|
||||
],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_no_changes_discard_zeros() {
|
||||
let actual = FossilDiff::parse(" 0 0 TOTAL over 0 changed files\n", true);
|
||||
let expected = FossilDiff {
|
||||
added: "",
|
||||
deleted: "",
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_no_changes_keep_zeros() {
|
||||
let actual = FossilDiff::parse(" 0 0 TOTAL over 0 changed files\n", false);
|
||||
let expected = FossilDiff {
|
||||
added: "0",
|
||||
deleted: "0",
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_with_changes() {
|
||||
let actual = FossilDiff::parse(
|
||||
" 3 2 README.md\n 3 2 TOTAL over 1 changed files\n",
|
||||
true,
|
||||
);
|
||||
let expected = FossilDiff {
|
||||
added: "3",
|
||||
deleted: "2",
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_ignore_empty() {
|
||||
let actual = FossilDiff::parse("", true);
|
||||
let expected = FossilDiff {
|
||||
added: "",
|
||||
deleted: "",
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
/// Tests output as produced by Fossil v2.3 to v2.14, i.e. without the summary line.
|
||||
#[test]
|
||||
fn parse_ignore_when_missing_total_line() {
|
||||
let actual = FossilDiff::parse(" 3 2 README.md\n", true);
|
||||
let expected = FossilDiff {
|
||||
added: "",
|
||||
deleted: "",
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
fn expect_fossil_metrics_with_config(
|
||||
checkout_dir: &Path,
|
||||
config: Option<toml::Table>,
|
||||
expectations: &[Expect],
|
||||
) {
|
||||
let actual = ModuleRenderer::new("fossil_metrics")
|
||||
.path(checkout_dir.to_str().unwrap())
|
||||
.config(config.unwrap_or_else(|| {
|
||||
toml::toml! {
|
||||
[fossil_metrics]
|
||||
disabled = false
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
|
||||
let mut expect_added = Some("3");
|
||||
let mut expect_added_style = Color::Green.bold();
|
||||
let mut expect_deleted = Some("2");
|
||||
let mut expect_deleted_style = Color::Red.bold();
|
||||
|
||||
for expect in expectations {
|
||||
match expect {
|
||||
Expect::Empty => {
|
||||
assert_eq!(None, actual);
|
||||
return;
|
||||
}
|
||||
Expect::Added(added) => expect_added = *added,
|
||||
Expect::AddedStyle(style) => expect_added_style = *style,
|
||||
Expect::Deleted(deleted) => expect_deleted = *deleted,
|
||||
Expect::DeletedStyle(style) => expect_deleted_style = *style,
|
||||
}
|
||||
}
|
||||
|
||||
let expected = Some(format!(
|
||||
"{}{}",
|
||||
expect_added
|
||||
.map(|added| format!("{} ", expect_added_style.paint(format!("+{added}"))))
|
||||
.unwrap_or(String::from("")),
|
||||
expect_deleted
|
||||
.map(|deleted| format!("{} ", expect_deleted_style.paint(format!("-{deleted}"))))
|
||||
.unwrap_or(String::from("")),
|
||||
));
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
@ -8,14 +8,18 @@ use crate::formatter::StringFormatter;
|
||||
/// Creates a module with the system hostname
|
||||
///
|
||||
/// Will display the hostname if all of the following criteria are met:
|
||||
/// - hostname.disabled is absent or false
|
||||
/// - `hostname.disabled` is absent or false
|
||||
/// - `hostname.ssh_only` is false OR the user is currently connected as an SSH session (`$SSH_CONNECTION`)
|
||||
/// - `hostname.ssh_only` is false AND `hostname.detect_env_vars` is either empty or contains a defined environment variable
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("hostname");
|
||||
let config: HostnameConfig = HostnameConfig::try_load(module.config);
|
||||
|
||||
let ssh_connection = context.get_env("SSH_CONNECTION");
|
||||
if config.ssh_only && ssh_connection.is_none() {
|
||||
|
||||
if (config.ssh_only && ssh_connection.is_none())
|
||||
|| !context.detect_env_vars(&config.detect_env_vars)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -96,17 +100,82 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_only_false_no_ssh() {
|
||||
fn ssh_only_false_with_empty_detect_env_vars() {
|
||||
let hostname = get_hostname!();
|
||||
let actual = ModuleRenderer::new("hostname")
|
||||
.config(toml::toml! {
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = ""
|
||||
detect_env_vars = []
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!("{} in ", style().paint(hostname)));
|
||||
println!("{}", expected.as_ref().unwrap());
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_only_false_with_matching_negated_env_var() {
|
||||
let actual = ModuleRenderer::new("hostname")
|
||||
.config(toml::toml! {
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = ""
|
||||
detect_env_vars = ["!NEGATED"]
|
||||
})
|
||||
.env("NEGATED", "true")
|
||||
.collect();
|
||||
let expected = None;
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_only_false_with_only_negated_env_vars() {
|
||||
let hostname = get_hostname!();
|
||||
let actual = ModuleRenderer::new("hostname")
|
||||
.config(toml::toml! {
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = ""
|
||||
detect_env_vars = ["!NEGATED_ONE", "!NEGATED_TWO", "!NEGATED_THREE"]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!("{} in ", style().paint(hostname)));
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_only_false_with_matching_env_var() {
|
||||
let hostname = get_hostname!();
|
||||
let actual = ModuleRenderer::new("hostname")
|
||||
.config(toml::toml! {
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = ""
|
||||
detect_env_vars = ["FORCE_HOSTNAME"]
|
||||
})
|
||||
.env("FORCE_HOSTNAME", "true")
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!("{} in ", style().paint(hostname)));
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_only_false_without_matching_env_vars() {
|
||||
let actual = ModuleRenderer::new("hostname")
|
||||
.config(toml::toml! {
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = ""
|
||||
detect_env_vars = ["FORCE_HOSTNAME", "!NEGATED"]
|
||||
})
|
||||
.collect();
|
||||
let expected = None;
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
@ -121,6 +190,7 @@ mod tests {
|
||||
})
|
||||
.collect();
|
||||
let expected = Some(format!("{} in ", style().paint(hostname)));
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path;
|
||||
|
||||
@ -11,99 +10,91 @@ use crate::configs::kubernetes::KubernetesConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Default)]
|
||||
struct KubeCtxComponents {
|
||||
user: Option<String>,
|
||||
namespace: Option<String>,
|
||||
cluster: Option<String>,
|
||||
}
|
||||
|
||||
fn get_kube_context(filename: path::PathBuf) -> Option<String> {
|
||||
fn get_current_kube_context_name(filename: path::PathBuf) -> Option<String> {
|
||||
let contents = utils::read_file(filename).ok()?;
|
||||
|
||||
let yaml_docs = YamlLoader::load_from_str(&contents).ok()?;
|
||||
if yaml_docs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let conf = &yaml_docs[0];
|
||||
|
||||
let current_ctx = conf["current-context"].as_str()?;
|
||||
|
||||
if current_ctx.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(current_ctx.to_string())
|
||||
let conf = yaml_docs.get(0)?;
|
||||
conf["current-context"]
|
||||
.as_str()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(String::from)
|
||||
}
|
||||
|
||||
fn get_kube_ctx_component(filename: path::PathBuf, current_ctx: &str) -> Option<KubeCtxComponents> {
|
||||
fn get_kube_ctx_components(
|
||||
filename: path::PathBuf,
|
||||
current_ctx_name: &str,
|
||||
) -> Option<KubeCtxComponents> {
|
||||
let contents = utils::read_file(filename).ok()?;
|
||||
|
||||
let yaml_docs = YamlLoader::load_from_str(&contents).ok()?;
|
||||
if yaml_docs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let conf = &yaml_docs[0];
|
||||
let conf = yaml_docs.get(0)?;
|
||||
let contexts = conf["contexts"].as_vec()?;
|
||||
|
||||
let ctx_yaml = conf["contexts"].as_vec().and_then(|contexts| {
|
||||
contexts
|
||||
.iter()
|
||||
.filter_map(|ctx| Some((ctx, ctx["name"].as_str()?)))
|
||||
.find(|(_, name)| *name == current_ctx)
|
||||
});
|
||||
// Find the context with the name we're looking for
|
||||
// or return None if we can't find it
|
||||
let (ctx_yaml, _) = contexts
|
||||
.iter()
|
||||
.filter_map(|ctx| Some((ctx, ctx["name"].as_str()?)))
|
||||
.find(|(_, name)| name == ¤t_ctx_name)?;
|
||||
|
||||
let ctx_components = KubeCtxComponents {
|
||||
user: ctx_yaml
|
||||
.and_then(|(ctx, _)| ctx["context"]["user"].as_str())
|
||||
.and_then(|s| {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(s.to_owned())
|
||||
}),
|
||||
namespace: ctx_yaml
|
||||
.and_then(|(ctx, _)| ctx["context"]["namespace"].as_str())
|
||||
.and_then(|s| {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(s.to_owned())
|
||||
}),
|
||||
cluster: ctx_yaml
|
||||
.and_then(|(ctx, _)| ctx["context"]["cluster"].as_str())
|
||||
.and_then(|s| {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(s.to_owned())
|
||||
}),
|
||||
user: ctx_yaml["context"]["user"]
|
||||
.as_str()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(String::from),
|
||||
namespace: ctx_yaml["context"]["namespace"]
|
||||
.as_str()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(String::from),
|
||||
cluster: ctx_yaml["context"]["cluster"]
|
||||
.as_str()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(String::from),
|
||||
};
|
||||
|
||||
Some(ctx_components)
|
||||
}
|
||||
|
||||
fn get_kube_user<'a>(config: &'a KubernetesConfig, kube_user: &'a str) -> Cow<'a, str> {
|
||||
return get_alias(&config.user_aliases, kube_user).unwrap_or(Cow::Borrowed(kube_user));
|
||||
}
|
||||
|
||||
fn get_kube_context_name<'a>(config: &'a KubernetesConfig, kube_ctx: &'a str) -> Cow<'a, str> {
|
||||
return get_alias(&config.context_aliases, kube_ctx).unwrap_or(Cow::Borrowed(kube_ctx));
|
||||
}
|
||||
|
||||
fn get_alias<'a>(
|
||||
aliases: &'a HashMap<String, &'a str>,
|
||||
alias_candidate: &'a str,
|
||||
) -> Option<Cow<'a, str>> {
|
||||
if let Some(val) = aliases.get(alias_candidate) {
|
||||
return Some(Cow::Borrowed(val));
|
||||
fn get_aliased_name<'a>(
|
||||
pattern: Option<&'a str>,
|
||||
current_value: Option<&str>,
|
||||
alias: Option<&'a str>,
|
||||
) -> Option<String> {
|
||||
let replacement = alias.or(current_value)?.to_string();
|
||||
let Some(pattern) = pattern else {
|
||||
// If user pattern not set, treat it as a match-all pattern
|
||||
return Some(replacement);
|
||||
};
|
||||
// If a pattern is set, but we have no value, there is no match
|
||||
let value = current_value?;
|
||||
if value == pattern {
|
||||
return Some(replacement);
|
||||
}
|
||||
|
||||
return aliases.iter().find_map(|(k, v)| {
|
||||
let re = regex::Regex::new(&format!("^{k}$")).ok()?;
|
||||
let replaced = re.replace(alias_candidate, *v);
|
||||
match replaced {
|
||||
Cow::Owned(replaced) => Some(Cow::Owned(replaced)),
|
||||
_ => None,
|
||||
let re = match regex::Regex::new(&format!("^{pattern}$")) {
|
||||
Ok(re) => re,
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"Could not compile regular expression `{}`:\n{}",
|
||||
&format!("^{pattern}$"),
|
||||
error
|
||||
);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
};
|
||||
let replaced = re.replace(value, replacement.as_str());
|
||||
match replaced {
|
||||
Cow::Owned(replaced) => Some(replaced),
|
||||
// It didn't match...
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
@ -118,18 +109,28 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
// If we have some config for doing the directory scan then we use it but if we don't then we
|
||||
// assume we should treat it like the module is enabled to preserve backward compatibility.
|
||||
let have_scan_config = !(config.detect_files.is_empty()
|
||||
&& config.detect_folders.is_empty()
|
||||
&& config.detect_extensions.is_empty());
|
||||
let have_scan_config = [
|
||||
&config.detect_files,
|
||||
&config.detect_folders,
|
||||
&config.detect_extensions,
|
||||
]
|
||||
.into_iter()
|
||||
.any(|v| !v.is_empty());
|
||||
|
||||
let is_kube_project = context
|
||||
.try_begin_scan()?
|
||||
.set_files(&config.detect_files)
|
||||
.set_folders(&config.detect_folders)
|
||||
.set_extensions(&config.detect_extensions)
|
||||
.is_match();
|
||||
let is_kube_project = have_scan_config.then(|| {
|
||||
context
|
||||
.try_begin_scan()
|
||||
.map(|scanner| {
|
||||
scanner
|
||||
.set_files(&config.detect_files)
|
||||
.set_folders(&config.detect_folders)
|
||||
.set_extensions(&config.detect_extensions)
|
||||
.is_match()
|
||||
})
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
if have_scan_config && !is_kube_project {
|
||||
if !is_kube_project.unwrap_or(true) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -139,39 +140,89 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
.get_env("KUBECONFIG")
|
||||
.unwrap_or(default_config_file.to_str()?.to_string());
|
||||
|
||||
let kube_ctx = env::split_paths(&kube_cfg).find_map(get_kube_context)?;
|
||||
let current_kube_ctx_name =
|
||||
env::split_paths(&kube_cfg).find_map(get_current_kube_context_name)?;
|
||||
|
||||
let ctx_components: Vec<KubeCtxComponents> = env::split_paths(&kube_cfg)
|
||||
.filter_map(|filename| get_kube_ctx_component(filename, &kube_ctx))
|
||||
.collect();
|
||||
// Even if we have multiple config files, the first key wins
|
||||
// https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/
|
||||
// > Never change the value or map key. ... Example: If two files specify a red-user,
|
||||
// > use only values from the first file's red-user. Even if the second file has
|
||||
// > non-conflicting entries under red-user, discard them.
|
||||
// for that reason, we can pick the first context with that name
|
||||
let ctx_components: KubeCtxComponents = env::split_paths(&kube_cfg)
|
||||
.find_map(|filename| get_kube_ctx_components(filename, ¤t_kube_ctx_name))
|
||||
.unwrap_or_else(|| {
|
||||
// TODO: figure out if returning is more sensible. But currently we have tests depending on this
|
||||
log::warn!(
|
||||
"Invalid KUBECONFIG: identified current-context `{}`, but couldn't find the context in any config file(s): `{}`.\n",
|
||||
¤t_kube_ctx_name,
|
||||
&kube_cfg
|
||||
);
|
||||
KubeCtxComponents::default()
|
||||
});
|
||||
|
||||
// Select the first style that matches the context_pattern and,
|
||||
// if it is defined, the user_pattern
|
||||
let (matched_context_config, display_context, display_user) = config
|
||||
.contexts
|
||||
.iter()
|
||||
.find_map(|context_config| {
|
||||
let context_alias = get_aliased_name(
|
||||
Some(context_config.context_pattern),
|
||||
Some(¤t_kube_ctx_name),
|
||||
context_config.context_alias,
|
||||
)?;
|
||||
|
||||
let user_alias = get_aliased_name(
|
||||
context_config.user_pattern,
|
||||
ctx_components.user.as_deref(),
|
||||
context_config.user_alias,
|
||||
);
|
||||
if matches!((context_config.user_pattern, &user_alias), (Some(_), None)) {
|
||||
// defined pattern, but it didn't match
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((Some(context_config), context_alias, user_alias))
|
||||
})
|
||||
.unwrap_or_else(|| (None, current_kube_ctx_name.clone(), ctx_components.user));
|
||||
|
||||
// TODO: remove deprecated aliases after starship 2.0
|
||||
let display_context =
|
||||
deprecated::get_alias(display_context, &config.context_aliases, "context").unwrap();
|
||||
let display_user =
|
||||
display_user.and_then(|user| deprecated::get_alias(user, &config.user_aliases, "user"));
|
||||
|
||||
let display_style = matched_context_config
|
||||
.and_then(|ctx_cfg| ctx_cfg.style)
|
||||
.unwrap_or(config.style);
|
||||
let display_symbol = matched_context_config
|
||||
.and_then(|ctx_cfg| ctx_cfg.symbol)
|
||||
.unwrap_or(config.symbol);
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
"symbol" => Some(display_symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
"style" => Some(Ok(display_style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"context" => Some(Ok(get_kube_context_name(&config, &kube_ctx))),
|
||||
|
||||
"context" => Some(Ok(Cow::Borrowed(display_context.as_str()))),
|
||||
"namespace" => ctx_components
|
||||
.iter()
|
||||
.find_map(|kube| kube.namespace.as_deref())
|
||||
.map(|namespace| Ok(Cow::Borrowed(namespace))),
|
||||
|
||||
"user" => ctx_components
|
||||
.iter()
|
||||
.find_map(|kube| kube.user.as_deref())
|
||||
.map(|user| Ok(get_kube_user(&config, user))),
|
||||
|
||||
.namespace
|
||||
.as_ref()
|
||||
.map(|kube_ns| Ok(Cow::Borrowed(kube_ns.as_str()))),
|
||||
"cluster" => ctx_components
|
||||
.iter()
|
||||
.find_map(|kube| kube.cluster.as_deref())
|
||||
.map(|cluster| Ok(Cow::Borrowed(cluster))),
|
||||
.cluster
|
||||
.as_ref()
|
||||
.map(|kube_cluster| Ok(Cow::Borrowed(kube_cluster.as_str()))),
|
||||
"user" => display_user
|
||||
.as_ref()
|
||||
.map(|kube_user| Ok(Cow::Borrowed(kube_user.as_str()))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None, Some(context))
|
||||
@ -188,6 +239,47 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
Some(module)
|
||||
}
|
||||
|
||||
mod deprecated {
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn get_alias<'a>(
|
||||
current_value: String,
|
||||
aliases: &'a HashMap<String, &'a str>,
|
||||
name: &'a str,
|
||||
) -> Option<String> {
|
||||
let alias = if let Some(val) = aliases.get(current_value.as_str()) {
|
||||
// simple match without regex
|
||||
Some((*val).to_string())
|
||||
} else {
|
||||
// regex match
|
||||
aliases.iter().find_map(|(k, v)| {
|
||||
let re = regex::Regex::new(&format!("^{k}$")).ok()?;
|
||||
let replaced = re.replace(current_value.as_str(), *v);
|
||||
match replaced {
|
||||
// We have a match if the replaced string is different from the original
|
||||
Cow::Owned(replaced) => Some(replaced),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
match alias {
|
||||
Some(alias) => {
|
||||
log::warn!(
|
||||
"Usage of '{}_aliases' is deprecated and will be removed in 2.0; Use 'contexts' with '{}_alias' instead. (`{}` -> `{}`)",
|
||||
&name,
|
||||
&name,
|
||||
¤t_value,
|
||||
&alias
|
||||
);
|
||||
Some(alias)
|
||||
}
|
||||
None => Some(current_value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test::ModuleRenderer;
|
||||
@ -429,6 +521,21 @@ users: []
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_ctx_alias_regex_replace() -> io::Result<()> {
|
||||
base_test_ctx_alias(
|
||||
"gke_infra-cluster-28cccff6_europe-west4_cluster-1",
|
||||
toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "gke_.*_(?P<cluster>[\\w-]+)"
|
||||
context_alias = "example: $cluster"
|
||||
},
|
||||
"☸ example: cluster-1",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ctx_alias_broken_regex() -> io::Result<()> {
|
||||
base_test_ctx_alias(
|
||||
@ -577,7 +684,9 @@ users: []
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_config_files_with_ns() -> io::Result<()> {
|
||||
fn test_multiple_config_files_with_context_defined_once() -> io::Result<()> {
|
||||
// test that we get the current context from the first config file in the KUBECONFIG,
|
||||
// no matter if it is only defined in the latter
|
||||
let dir = tempfile::tempdir()?;
|
||||
|
||||
let filename_cc = dir.path().join("config_cc");
|
||||
@ -630,7 +739,7 @@ users: []
|
||||
})
|
||||
.collect();
|
||||
|
||||
// And tes with context and namespace first
|
||||
// And test with context and namespace first
|
||||
let actual_ctx_first = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env(
|
||||
@ -655,6 +764,87 @@ users: []
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_config_files_with_context_defined_twice() -> io::Result<()> {
|
||||
// tests that, if two files contain the same context,
|
||||
// only the context config from the first is used.
|
||||
let dir = tempfile::tempdir()?;
|
||||
|
||||
let config1 = dir.path().join("config1");
|
||||
|
||||
let mut file1 = File::create(&config1)?;
|
||||
file1.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test_cluster1
|
||||
namespace: test_namespace1
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file1.sync_all()?;
|
||||
|
||||
let config2 = dir.path().join("config2");
|
||||
|
||||
let mut file2 = File::create(&config2)?;
|
||||
file2.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test_cluster2
|
||||
user: test_user2
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file2.sync_all()?;
|
||||
|
||||
let paths1 = [config1.clone(), config2.clone()];
|
||||
let kubeconfig_content1 = env::join_paths(paths1.iter()).unwrap();
|
||||
|
||||
let actual1 = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", kubeconfig_content1.to_string_lossy())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
format = "($user )($cluster )($namespace )"
|
||||
disabled = false
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected1 = Some("test_cluster1 test_namespace1 ".to_string());
|
||||
assert_eq!(expected1, actual1);
|
||||
|
||||
let paths2 = [config2, config1];
|
||||
let kubeconfig_content2 = env::join_paths(paths2.iter()).unwrap();
|
||||
|
||||
let actual2 = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", kubeconfig_content2.to_string_lossy())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
format = "($user )($cluster )($namespace )"
|
||||
disabled = false
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected2 = Some("test_user2 test_cluster2 ".to_string());
|
||||
assert_eq!(expected2, actual2);
|
||||
|
||||
dir.close()
|
||||
}
|
||||
|
||||
fn base_test_user_alias(
|
||||
user_name: &str,
|
||||
config: toml::Table,
|
||||
@ -744,6 +934,23 @@ users: []
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_user_alias_regex_replace() -> io::Result<()> {
|
||||
base_test_user_alias(
|
||||
"gke_infra-user-28cccff6_europe-west4_cluster-1",
|
||||
toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
format = "[$symbol$context( \\($user\\))]($style) in "
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = ".*"
|
||||
user_pattern = "gke_.*_(?P<cluster>[\\w-]+)"
|
||||
user_alias = "example: $cluster"
|
||||
},
|
||||
"☸ test_context (example: cluster-1)",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_alias_broken_regex() -> io::Result<()> {
|
||||
base_test_user_alias(
|
||||
@ -932,4 +1139,310 @@ users: []
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_overwrites_defaults() -> std::io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
namespace: test_namespace
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
style = "bold red"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "test.*"
|
||||
style = "bold green"
|
||||
symbol = "§ "
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!(
|
||||
"{} in ",
|
||||
Color::Green.bold().paint("§ test_context (test_namespace)")
|
||||
));
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_both_pattern_must_match() -> io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
format = "$symbol$context ($user )"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "test.*"
|
||||
user_pattern = "test.*"
|
||||
context_alias = "yy"
|
||||
user_alias = "xx"
|
||||
symbol = "§ "
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some("§ yy xx ".to_string());
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_only_one_pattern_matches() -> io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
format = "$symbol$context ($user )"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "test.*"
|
||||
user_pattern = "test_BAD.*"
|
||||
context_alias = "yy"
|
||||
user_alias = "xx"
|
||||
symbol = "§ "
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some("☸ test_context test_user ".to_string());
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_uses_aliases() -> std::io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
namespace: test_namespace
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
style = "bold red"
|
||||
format = "$symbol($user )($context )($cluster )($namespace)"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "test.*"
|
||||
context_alias = "xyz"
|
||||
user_alias = "abc"
|
||||
symbol = "§ "
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some("§ abc xyz test_namespace".to_string());
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_user_pattern_does_not_match() -> std::io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
namespace: test_namespace
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
style = "bold red"
|
||||
format = "$symbol($user )($context )($cluster )($namespace)"
|
||||
|
||||
[[kubernetes.contexts]]
|
||||
context_pattern = "test"
|
||||
user_pattern = "not_matching"
|
||||
context_alias = "xyz"
|
||||
user_alias = "abc"
|
||||
symbol = "§ "
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some("☸ test_user test_context test_namespace".to_string());
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_contexts_does_not_match() -> std::io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
namespace: test_namespace
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
style = "bold red"
|
||||
contexts = [
|
||||
{context_pattern = "tests_.*", style = "bold green", symbol = "§ "},
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!(
|
||||
"{} in ",
|
||||
Color::Red.bold().paint("☸ test_context (test_namespace)")
|
||||
));
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_context_bad_regex_should_not_panic() -> std::io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let filename = dir.path().join("config");
|
||||
let mut file = File::create(&filename)?;
|
||||
file.write_all(
|
||||
b"
|
||||
apiVersion: v1
|
||||
clusters: []
|
||||
contexts:
|
||||
- context:
|
||||
user: test_user
|
||||
namespace: test_namespace
|
||||
name: test_context
|
||||
current-context: test_context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
",
|
||||
)?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = ModuleRenderer::new("kubernetes")
|
||||
.path(dir.path())
|
||||
.env("KUBECONFIG", filename.to_string_lossy().as_ref())
|
||||
.config(toml::toml! {
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
style = "bold red"
|
||||
contexts = [
|
||||
{context_pattern = "tests_(.*", style = "bold green", symbol = "§ "},
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!(
|
||||
"{} in ",
|
||||
Color::Red.bold().paint("☸ test_context (test_namespace)")
|
||||
));
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ mod erlang;
|
||||
mod fennel;
|
||||
mod fill;
|
||||
mod fossil_branch;
|
||||
mod fossil_metrics;
|
||||
mod gcloud;
|
||||
mod git_branch;
|
||||
mod git_commit;
|
||||
@ -129,6 +130,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||
"fennel" => fennel::module(context),
|
||||
"fill" => fill::module(context),
|
||||
"fossil_branch" => fossil_branch::module(context),
|
||||
"fossil_metrics" => fossil_metrics::module(context),
|
||||
"gcloud" => gcloud::module(context),
|
||||
"git_branch" => git_branch::module(context),
|
||||
"git_commit" => git_commit::module(context),
|
||||
@ -244,6 +246,7 @@ pub fn description(module: &str) -> &'static str {
|
||||
"fennel" => "The currently installed version of Fennel",
|
||||
"fill" => "Fills the remaining space on the line with a pad string",
|
||||
"fossil_branch" => "The active branch of the check-out in your current directory",
|
||||
"fossil_metrics" => "The currently added/deleted lines in your check-out",
|
||||
"gcloud" => "The current GCP client configuration",
|
||||
"git_branch" => "The active branch of the repo in your current directory",
|
||||
"git_commit" => "The active commit (and tag if any) of the repo in your current directory",
|
||||
|
@ -257,6 +257,12 @@ Elixir 1.10 (compiled with Erlang/OTP 22)\n",
|
||||
stdout: String::default(),
|
||||
stderr: String::default(),
|
||||
}),
|
||||
"fossil diff --numstat" => Some(CommandOutput{
|
||||
stdout: String::from("\
|
||||
3 2 README.md
|
||||
3 2 TOTAL over 1 changed files"),
|
||||
stderr: String::default(),
|
||||
}),
|
||||
"fossil update topic-branch" => Some(CommandOutput{
|
||||
stdout: String::default(),
|
||||
stderr: String::default(),
|
||||
|
Loading…
Reference in New Issue
Block a user