feat: add latency probing, platform installers (deb/rpm/pkg/dmg/exe)
Release / Build aarch64-unknown-linux-gnu (push) Has been cancelled
Release / Build aarch64-apple-darwin (push) Has been cancelled
Release / Build aarch64-pc-windows-msvc (push) Has been cancelled
Release / Build x86_64-unknown-linux-gnu (push) Has been cancelled
Release / Build x86_64-apple-darwin (push) Has been cancelled
Release / Build x86_64-pc-windows-msvc (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
Release / Build aarch64-unknown-linux-gnu (push) Has been cancelled
Release / Build aarch64-apple-darwin (push) Has been cancelled
Release / Build aarch64-pc-windows-msvc (push) Has been cancelled
Release / Build x86_64-unknown-linux-gnu (push) Has been cancelled
Release / Build x86_64-apple-darwin (push) Has been cancelled
Release / Build x86_64-pc-windows-msvc (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
This commit is contained in:
@@ -22,9 +22,11 @@ jobs:
|
|||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
artifact: sshell-x86_64-linux
|
artifact: sshell-x86_64-linux
|
||||||
|
nfpm_arch: amd64
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
artifact: sshell-aarch64-linux
|
artifact: sshell-aarch64-linux
|
||||||
|
nfpm_arch: arm64
|
||||||
# macOS
|
# macOS
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
@@ -35,10 +37,10 @@ jobs:
|
|||||||
# Windows
|
# Windows
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
artifact: sshell-x86_64-windows.exe
|
artifact: sshell-x86_64-windows
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: aarch64-pc-windows-msvc
|
target: aarch64-pc-windows-msvc
|
||||||
artifact: sshell-aarch64-windows.exe
|
artifact: sshell-aarch64-windows
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -60,19 +62,77 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release --target ${{ matrix.target }}
|
run: cargo build --release --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Package (Unix)
|
- name: Set version from tag
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
echo "SHELL_VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Package binary archive (Unix)
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
cp target/${{ matrix.target }}/release/sshell sshell
|
cp target/${{ matrix.target }}/release/sshell sshell
|
||||||
chmod +x sshell
|
chmod +x sshell
|
||||||
tar czf ${{ matrix.artifact }}.tar.gz sshell
|
tar czf ${{ matrix.artifact }}.tar.gz sshell
|
||||||
|
|
||||||
- name: Package (Windows)
|
- name: Package binary archive (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
copy target\${{ matrix.target }}\release\sshell.exe sshell.exe
|
copy target\${{ matrix.target }}\release\sshell.exe sshell.exe
|
||||||
Compress-Archive -Path sshell.exe -DestinationPath ${{ matrix.artifact }}.zip
|
Compress-Archive -Path sshell.exe -DestinationPath ${{ matrix.artifact }}.zip
|
||||||
|
|
||||||
|
# --- Linux installer packages (nfpm) ---
|
||||||
|
- name: Install nfpm
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
uses: jaxxstorm/action-install-gh-release@v1.14.0
|
||||||
|
with:
|
||||||
|
repo: goreleaser/nfpm/v2
|
||||||
|
cache: enable
|
||||||
|
|
||||||
|
- name: Package .deb
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
env:
|
||||||
|
NFPM_ARCH: ${{ matrix.nfpm_arch }}
|
||||||
|
NFPM_VERSION: ${{ env.SHELL_VERSION }}
|
||||||
|
run: nfpm pkg --packager deb --target ${{ matrix.artifact }}.deb
|
||||||
|
|
||||||
|
- name: Package .rpm
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
env:
|
||||||
|
NFPM_ARCH: ${{ matrix.nfpm_arch }}
|
||||||
|
NFPM_VERSION: ${{ env.SHELL_VERSION }}
|
||||||
|
run: nfpm pkg --packager rpm --target ${{ matrix.artifact }}.rpm
|
||||||
|
|
||||||
|
- name: Package .pkg.tar.zst
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
env:
|
||||||
|
NFPM_ARCH: ${{ matrix.nfpm_arch }}
|
||||||
|
NFPM_VERSION: ${{ env.SHELL_VERSION }}
|
||||||
|
run: nfpm pkg --packager archlinux --target ${{ matrix.artifact }}.pkg.tar.zst
|
||||||
|
|
||||||
|
# --- macOS DMG ---
|
||||||
|
- name: Package .dmg
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
STAGING_DIR=$(mktemp -d)
|
||||||
|
cp sshell "$STAGING_DIR/sshell"
|
||||||
|
chmod +x "$STAGING_DIR/sshell"
|
||||||
|
hdiutil create -volname "sshell" -srcfolder "$STAGING_DIR" -ov -format UDZO ${{ matrix.artifact }}.dmg
|
||||||
|
rm -rf "$STAGING_DIR"
|
||||||
|
|
||||||
|
# --- Windows installer (Inno Setup) ---
|
||||||
|
- name: Compile Inno Setup installer
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: Minionguyjpro/Inno-Setup-Action@v1.2.8
|
||||||
|
env:
|
||||||
|
SHELL_VERSION: ${{ env.SHELL_VERSION }}
|
||||||
|
with:
|
||||||
|
path: sshell.iss
|
||||||
|
options: >-
|
||||||
|
/DMyArch=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
||||||
|
/DMyArchAllowed=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
||||||
|
/DMyArchInstall64=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -80,6 +140,11 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
${{ matrix.artifact }}.tar.gz
|
${{ matrix.artifact }}.tar.gz
|
||||||
${{ matrix.artifact }}.zip
|
${{ matrix.artifact }}.zip
|
||||||
|
${{ matrix.artifact }}.deb
|
||||||
|
${{ matrix.artifact }}.rpm
|
||||||
|
${{ matrix.artifact }}.pkg.tar.zst
|
||||||
|
${{ matrix.artifact }}.dmg
|
||||||
|
sshell-*-windows-setup.exe
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create GitHub Release
|
name: Create GitHub Release
|
||||||
@@ -100,3 +165,8 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
artifacts/*.tar.gz
|
artifacts/*.tar.gz
|
||||||
artifacts/*.zip
|
artifacts/*.zip
|
||||||
|
artifacts/*.deb
|
||||||
|
artifacts/*.rpm
|
||||||
|
artifacts/*.pkg.tar.zst
|
||||||
|
artifacts/*.dmg
|
||||||
|
artifacts/*-windows-setup.exe
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
name: sshell
|
||||||
|
arch: ${NFPM_ARCH}
|
||||||
|
platform: linux
|
||||||
|
version: ${NFPM_VERSION}
|
||||||
|
section: utils
|
||||||
|
priority: optional
|
||||||
|
maintainer: rain-bus <rainandbus@gmail.com>
|
||||||
|
description: |
|
||||||
|
A personal SSH and shell connection manager with a terminal user interface (TUI).
|
||||||
|
Manage SSH and local shell profiles with tagging, usage tracking, and smart sorting.
|
||||||
|
vendor: rain-bus
|
||||||
|
homepage: https://github.com/Rain-Bus/sshell
|
||||||
|
license: MIT
|
||||||
|
|
||||||
|
contents:
|
||||||
|
- src: sshell
|
||||||
|
dst: /usr/bin/sshell
|
||||||
|
file_info:
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
deb:
|
||||||
|
compression: gz
|
||||||
|
|
||||||
|
rpm:
|
||||||
|
compression: gz
|
||||||
|
|
||||||
|
archlinux:
|
||||||
|
pkgbase: sshell
|
||||||
|
packager: rain-bus <rainandbus@gmail.com>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod home_ops;
|
mod home_ops;
|
||||||
|
pub mod latency;
|
||||||
mod profile_ext;
|
mod profile_ext;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
/// Result of a single latency probe.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum LatencyStatus {
|
||||||
|
/// Host reachable, round-trip in milliseconds.
|
||||||
|
Reachable { ms: u64 },
|
||||||
|
/// TCP connect failed or timed out.
|
||||||
|
Unreachable,
|
||||||
|
/// No latency check applicable (shell connections).
|
||||||
|
Local,
|
||||||
|
/// Not yet checked.
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shared latency cache keyed by "host:port" strings.
|
||||||
|
/// Background threads write, draw reads.
|
||||||
|
pub type LatencyCache = Arc<Mutex<HashMap<String, LatencyStatus>>>;
|
||||||
|
|
||||||
|
/// Perform a TCP connect to measure latency.
|
||||||
|
/// Timeout is 3 seconds. Called from spawned threads.
|
||||||
|
pub fn probe(host: &str, port: u16) -> LatencyStatus {
|
||||||
|
let target = format!("{host}:{port}");
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let addr = match std::net::ToSocketAddrs::to_socket_addrs(&target) {
|
||||||
|
Ok(mut addrs) => match addrs.next() {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return LatencyStatus::Unreachable,
|
||||||
|
},
|
||||||
|
Err(_) => return LatencyStatus::Unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
match TcpStream::connect_timeout(&addr, Duration::from_secs(3)) {
|
||||||
|
Ok(_) => {
|
||||||
|
let ms = start.elapsed().as_millis() as u64;
|
||||||
|
LatencyStatus::Reachable { ms }
|
||||||
|
}
|
||||||
|
Err(_) => LatencyStatus::Unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use crate::import::ImportCandidate;
|
|||||||
|
|
||||||
use super::cred::CredFormState;
|
use super::cred::CredFormState;
|
||||||
use super::form::FormState;
|
use super::form::FormState;
|
||||||
|
use super::latency::LatencyCache;
|
||||||
use super::settings::SettingsState;
|
use super::settings::SettingsState;
|
||||||
|
|
||||||
// ── Text editing trait ──────────────────────────────────────
|
// ── Text editing trait ──────────────────────────────────────
|
||||||
@@ -183,6 +184,7 @@ pub struct Session {
|
|||||||
pub toast: Option<Toast>,
|
pub toast: Option<Toast>,
|
||||||
pub should_quit: bool,
|
pub should_quit: bool,
|
||||||
pub settings: SettingsState,
|
pub settings: SettingsState,
|
||||||
|
pub latency: LatencyCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -219,6 +221,7 @@ impl Session {
|
|||||||
toast: None,
|
toast: None,
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
settings: SettingsState::default(),
|
settings: SettingsState::default(),
|
||||||
|
latency: LatencyCache::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::app::{App, Mode};
|
use crate::app::{App, Mode};
|
||||||
|
use crate::config::ConnectionType;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::{Hide, Show},
|
cursor::{Hide, Show},
|
||||||
@@ -7,6 +8,7 @@ use crossterm::{
|
|||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
use ratatui::{Terminal, backend::CrosstermBackend};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -24,6 +26,8 @@ pub fn run() -> Result<()> {
|
|||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
let mut app = App::load()?;
|
let mut app = App::load()?;
|
||||||
|
|
||||||
|
spawn_latency_probes(&app);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|frame| super::draw(frame, &mut app))?;
|
terminal.draw(|frame| super::draw(frame, &mut app))?;
|
||||||
if app.session.should_quit {
|
if app.session.should_quit {
|
||||||
@@ -34,6 +38,7 @@ pub fn run() -> Result<()> {
|
|||||||
&& key.kind == KeyEventKind::Press
|
&& key.kind == KeyEventKind::Press
|
||||||
{
|
{
|
||||||
handle_key(&mut app, key)?;
|
handle_key(&mut app, key)?;
|
||||||
|
spawn_latency_probes(&app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,3 +88,27 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Result<()> {
|
|||||||
Mode::Settings => SettingsView.handle_key(app, key),
|
Mode::Settings => SettingsView.handle_key(app, key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_latency_probes(app: &App) {
|
||||||
|
let cache = app.session.latency.lock().unwrap();
|
||||||
|
let existing: HashSet<String> = cache.keys().cloned().collect();
|
||||||
|
drop(cache);
|
||||||
|
|
||||||
|
for (_, profile) in app.entries() {
|
||||||
|
if let ConnectionType::Ssh { host, port, .. } = &profile.kind {
|
||||||
|
let key = format!("{host}:{port}");
|
||||||
|
if existing.contains(&key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cache_clone = app.session.latency.clone();
|
||||||
|
let host = host.clone();
|
||||||
|
let port = *port;
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let status = crate::app::latency::probe(&host, port);
|
||||||
|
if let Ok(mut cache) = cache_clone.lock() {
|
||||||
|
cache.insert(key, status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+27
-14
@@ -1,8 +1,9 @@
|
|||||||
use crate::app::{App, Mode};
|
use crate::app::{App, Mode};
|
||||||
use crate::app::display_name;
|
use crate::app::display_name;
|
||||||
|
use crate::app::latency::LatencyStatus;
|
||||||
use crate::config::{ConnectionSource, ConnectionType, CredentialEntry};
|
use crate::config::{ConnectionSource, ConnectionType, CredentialEntry};
|
||||||
use crate::ui::component::{badge_span, draw_input, panel, tag_badge};
|
use crate::ui::component::{badge_span, draw_input, panel, tag_badge};
|
||||||
use crate::ui::{ACCENT, BLUE, GREEN, MUTED, PANEL_ALT, PURPLE, RED, SELECTED_BG, TEXT};
|
use crate::ui::{ACCENT, BLUE, GREEN, MUTED, PANEL_ALT, PURPLE, RED, SELECTED_BG, TEXT, YELLOW};
|
||||||
|
|
||||||
use super::View;
|
use super::View;
|
||||||
|
|
||||||
@@ -193,7 +194,7 @@ pub fn draw_connection_list(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
.header(
|
.header(
|
||||||
Row::new([" Name", "Type", "Target", "Auth"])
|
Row::new([" Name", "Type", "Target", "Ping"])
|
||||||
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
||||||
)
|
)
|
||||||
.block(panel(title))
|
.block(panel(title))
|
||||||
@@ -264,15 +265,27 @@ fn connection_row(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let auth_state = profile
|
let (ping_text, ping_color) = match &profile.kind {
|
||||||
.auth_ref()
|
ConnectionType::Ssh { host, port, .. } => {
|
||||||
.and_then(|auth| app.config.credential(auth))
|
let key = format!("{host}:{port}");
|
||||||
.map(|cred| if cred.has_value() { "ready" } else { "empty" })
|
let cache = app.session.latency.lock().unwrap();
|
||||||
.unwrap_or("none");
|
match cache.get(&key) {
|
||||||
let auth_color = match auth_state {
|
Some(LatencyStatus::Reachable { ms }) => {
|
||||||
"ready" => GREEN,
|
let text = format!("{ms} ms");
|
||||||
"empty" => RED,
|
let color = if *ms < 100 {
|
||||||
_ => MUTED,
|
GREEN
|
||||||
|
} else if *ms < 300 {
|
||||||
|
YELLOW
|
||||||
|
} else {
|
||||||
|
RED
|
||||||
|
};
|
||||||
|
(text, color)
|
||||||
|
}
|
||||||
|
Some(LatencyStatus::Unreachable) => ("timeout".to_string(), RED),
|
||||||
|
_ => ("...".to_string(), MUTED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConnectionType::Shell { .. } => ("-".to_string(), MUTED),
|
||||||
};
|
};
|
||||||
|
|
||||||
let badge_style = if selected {
|
let badge_style = if selected {
|
||||||
@@ -287,10 +300,10 @@ fn connection_row(
|
|||||||
Span::styled(format!(" {} ", type_badge), badge_style),
|
Span::styled(format!(" {} ", type_badge), badge_style),
|
||||||
])).style(row_style),
|
])).style(row_style),
|
||||||
Cell::from(target).style(row_style),
|
Cell::from(target).style(row_style),
|
||||||
Cell::from(auth_state).style(if selected {
|
Cell::from(ping_text).style(if selected {
|
||||||
Style::default().fg(auth_color).bg(SELECTED_BG)
|
Style::default().fg(ping_color).bg(SELECTED_BG)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(auth_color)
|
Style::default().fg(ping_color)
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
.height(1)
|
.height(1)
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#define MyAppName "sshell"
|
||||||
|
#define MyAppVersion GetEnv("SHELL_VERSION")
|
||||||
|
#ifndef MyAppVersion
|
||||||
|
#define MyAppVersion "0.1.0"
|
||||||
|
#endif
|
||||||
|
#define MyAppPublisher "rain-bus"
|
||||||
|
#define MyAppURL "https://github.com/Rain-Bus/sshell"
|
||||||
|
#define MyAppExeName "sshell.exe"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
AppId={{sshell-2024-1}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
AppPublisherURL={#MyAppURL}
|
||||||
|
AppSupportURL={#MyAppURL}
|
||||||
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
|
DefaultGroupName={#MyAppName}
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
OutputBaseFilename=sshell-{#MyAppVersion}-{#MyArch}-windows-setup
|
||||||
|
Compression=lzma2
|
||||||
|
SolidCompression=yes
|
||||||
|
WizardStyle=modern
|
||||||
|
ArchitecturesAllowed={#MyArchAllowed}
|
||||||
|
ArchitecturesInstallIn64BitMode={#MyArchInstall64}
|
||||||
|
OutputDir=.
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "addtopath"; Description: "Add sshell to PATH"; GroupDescription: "Environment:"; Flags: checked
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "sshell.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
|
[Registry]
|
||||||
|
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
|
||||||
|
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \
|
||||||
|
Flags: preservestringtype uninsdeletevalue; \
|
||||||
|
Tasks: addtopath
|
||||||
|
|
||||||
|
[UninstallDelete]
|
||||||
|
Type: files; Name: "{app}\sshell.exe"
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
const
|
||||||
|
WM_SETTINGCHANGE = $001A;
|
||||||
|
|
||||||
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
|
var
|
||||||
|
ResultCode: Integer;
|
||||||
|
begin
|
||||||
|
if CurStep = ssPostInstall then
|
||||||
|
begin
|
||||||
|
// Notify the system that environment variables have changed
|
||||||
|
RegWriteStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
|
||||||
|
'Path', RegQueryStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path'));
|
||||||
|
Exec('cmd.exe', '/C echo %Path%', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
Reference in New Issue
Block a user