mirror of
https://github.com/openai/codex.git
synced 2026-11-12 06:40:35 +00:00
Move shell snapshot scripts into codex-shell-command (#39281)
## What changed - Add a public `snapshot_script` helper to `codex-shell-command` for Bash, Zsh, POSIX shell, and PowerShell, returning `None` for Command Prompt. - Update `codex-core` and its shell snapshot tests to use the shared helper without changing snapshot behavior. GitOrigin-RevId: 779b9dc964635b717922ea9f5646b94c4dbb0da1
This commit is contained in:
1 parent
88c39c4578
commit
681c82f497
4 files changed
+217
-203
No files matched your search
@@ -17,6 +17,7 @@ use anyhow::bail;
|
|||||||
use codex_exec_server::Environment;
|
use codex_exec_server::Environment;
|
||||||
use codex_otel::SessionTelemetry;
|
use codex_otel::SessionTelemetry;
|
||||||
use codex_protocol::ThreadId;
|
use codex_protocol::ThreadId;
|
||||||
|
use codex_shell_command::shell_snapshot::snapshot_script;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use codex_utils_path_uri::PathUri;
|
use codex_utils_path_uri::PathUri;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
@@ -44,7 +45,6 @@ pub(crate) struct ShellSnapshotFile {
|
|||||||
const SNAPSHOT_TIMEOUT: Duration = Duration::from_secs(10);
|
const SNAPSHOT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
const SNAPSHOT_RETENTION: Duration = Duration::from_secs(60 * 60 * 24 * 3); // 3 days retention.
|
const SNAPSHOT_RETENTION: Duration = Duration::from_secs(60 * 60 * 24 * 3); // 3 days retention.
|
||||||
const SNAPSHOT_DIR: &str = "shell_snapshots";
|
const SNAPSHOT_DIR: &str = "shell_snapshots";
|
||||||
const EXCLUDED_EXPORT_VARS: &[&str] = &["PWD", "OLDPWD"];
|
|
||||||
|
|
||||||
impl ShellSnapshot {
|
impl ShellSnapshot {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@@ -229,13 +229,9 @@ async fn write_shell_snapshot(
|
|||||||
|
|
||||||
async fn capture_snapshot(shell: &Shell, cwd: &AbsolutePathBuf) -> Result<String> {
|
async fn capture_snapshot(shell: &Shell, cwd: &AbsolutePathBuf) -> Result<String> {
|
||||||
let shell_type = shell.shell_type;
|
let shell_type = shell.shell_type;
|
||||||
match shell_type {
|
let script = snapshot_script(shell_type)
|
||||||
ShellType::Zsh => run_shell_script(shell, &zsh_snapshot_script(), cwd).await,
|
.ok_or_else(|| anyhow!("Shell snapshotting is not yet supported for {shell_type:?}"))?;
|
||||||
ShellType::Bash => run_shell_script(shell, &bash_snapshot_script(), cwd).await,
|
run_shell_script(shell, &script, cwd).await
|
||||||
ShellType::Sh => run_shell_script(shell, &sh_snapshot_script(), cwd).await,
|
|
||||||
ShellType::PowerShell => run_shell_script(shell, powershell_snapshot_script(), cwd).await,
|
|
||||||
ShellType::Cmd => bail!("Shell snapshotting is not yet supported for {shell_type:?}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_snapshot_preamble(snapshot: &str) -> Result<String> {
|
fn strip_snapshot_preamble(snapshot: &str) -> Result<String> {
|
||||||
@@ -315,196 +311,6 @@ async fn run_script_with_timeout(
|
|||||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn excluded_exports_regex() -> String {
|
|
||||||
EXCLUDED_EXPORT_VARS.join("|")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zsh_snapshot_script() -> String {
|
|
||||||
let excluded = excluded_exports_regex();
|
|
||||||
let script = r##"if [[ -n "$ZDOTDIR" ]]; then
|
|
||||||
rc="$ZDOTDIR/.zshrc"
|
|
||||||
else
|
|
||||||
rc="$HOME/.zshrc"
|
|
||||||
fi
|
|
||||||
[[ -r "$rc" ]] && . "$rc"
|
|
||||||
print '# Snapshot file'
|
|
||||||
print '# Unset all aliases to avoid conflicts with functions'
|
|
||||||
print 'unalias -a 2>/dev/null || true'
|
|
||||||
print '# Functions'
|
|
||||||
functions
|
|
||||||
print ''
|
|
||||||
setopt_count=$(setopt | wc -l | tr -d ' ')
|
|
||||||
print "# setopts $setopt_count"
|
|
||||||
setopt | sed 's/^/setopt /'
|
|
||||||
print ''
|
|
||||||
alias_count=$(alias -L | wc -l | tr -d ' ')
|
|
||||||
print "# aliases $alias_count"
|
|
||||||
alias -L
|
|
||||||
print ''
|
|
||||||
export_lines=$(export -p | awk '
|
|
||||||
/^(export|declare -x|typeset -x) / {
|
|
||||||
line=$0
|
|
||||||
name=line
|
|
||||||
sub(/^(export|declare -x|typeset -x) /, "", name)
|
|
||||||
if (name ~ /^-[A-Za-z]*r[A-Za-z]* /) {
|
|
||||||
next
|
|
||||||
}
|
|
||||||
if (name ~ /^-[A-Za-z]*T[A-Za-z]* /) {
|
|
||||||
sub(/^-[A-Za-z]*T[A-Za-z]* /, "", name)
|
|
||||||
sub(/ [A-Za-z_][A-Za-z0-9_]*=.*/, "", name)
|
|
||||||
}
|
|
||||||
sub(/=.*/, "", name)
|
|
||||||
if (name ~ /^(EXCLUDED_EXPORTS)$/) {
|
|
||||||
next
|
|
||||||
}
|
|
||||||
if (name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
|
|
||||||
print line
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
||||||
print "# exports $export_count"
|
|
||||||
if [[ -n "$export_lines" ]]; then
|
|
||||||
print -r -- "$export_lines"
|
|
||||||
fi
|
|
||||||
"##;
|
|
||||||
script.replace("EXCLUDED_EXPORTS", &excluded)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bash_snapshot_script() -> String {
|
|
||||||
let excluded = excluded_exports_regex();
|
|
||||||
let script = r##"if [ -z "$BASH_ENV" ] && [ -r "$HOME/.bashrc" ]; then
|
|
||||||
. "$HOME/.bashrc"
|
|
||||||
fi
|
|
||||||
echo '# Snapshot file'
|
|
||||||
echo '# Unset all aliases to avoid conflicts with functions'
|
|
||||||
unalias -a 2>/dev/null || true
|
|
||||||
echo '# Functions'
|
|
||||||
declare -f
|
|
||||||
echo ''
|
|
||||||
bash_opts=$(set -o | awk '$2=="on"{print $1}')
|
|
||||||
bash_opt_count=$(printf '%s\n' "$bash_opts" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
||||||
echo "# setopts $bash_opt_count"
|
|
||||||
if [ -n "$bash_opts" ]; then
|
|
||||||
printf 'set -o %s\n' $bash_opts
|
|
||||||
fi
|
|
||||||
echo ''
|
|
||||||
alias_count=$(alias -p | wc -l | tr -d ' ')
|
|
||||||
echo "# aliases $alias_count"
|
|
||||||
alias -p
|
|
||||||
echo ''
|
|
||||||
export_lines=$(
|
|
||||||
while IFS= read -r name; do
|
|
||||||
if [[ "$name" =~ ^(EXCLUDED_EXPORTS)$ ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if [[ ! "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
declare -xp "$name" 2>/dev/null || true
|
|
||||||
done < <(compgen -e)
|
|
||||||
)
|
|
||||||
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
||||||
echo "# exports $export_count"
|
|
||||||
if [ -n "$export_lines" ]; then
|
|
||||||
printf '%s\n' "$export_lines"
|
|
||||||
fi
|
|
||||||
"##;
|
|
||||||
script.replace("EXCLUDED_EXPORTS", &excluded)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sh_snapshot_script() -> String {
|
|
||||||
let excluded = excluded_exports_regex();
|
|
||||||
let script = r##"if [ -n "$ENV" ] && [ -r "$ENV" ]; then
|
|
||||||
. "$ENV"
|
|
||||||
fi
|
|
||||||
echo '# Snapshot file'
|
|
||||||
echo '# Unset all aliases to avoid conflicts with functions'
|
|
||||||
unalias -a 2>/dev/null || true
|
|
||||||
echo '# Functions'
|
|
||||||
if command -v typeset >/dev/null 2>&1; then
|
|
||||||
typeset -f
|
|
||||||
elif command -v declare >/dev/null 2>&1; then
|
|
||||||
declare -f
|
|
||||||
fi
|
|
||||||
echo ''
|
|
||||||
if set -o >/dev/null 2>&1; then
|
|
||||||
sh_opts=$(set -o | awk '$2=="on"{print $1}')
|
|
||||||
sh_opt_count=$(printf '%s\n' "$sh_opts" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
||||||
echo "# setopts $sh_opt_count"
|
|
||||||
if [ -n "$sh_opts" ]; then
|
|
||||||
printf 'set -o %s\n' $sh_opts
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo '# setopts 0'
|
|
||||||
fi
|
|
||||||
echo ''
|
|
||||||
if alias >/dev/null 2>&1; then
|
|
||||||
alias_count=$(alias | wc -l | tr -d ' ')
|
|
||||||
echo "# aliases $alias_count"
|
|
||||||
alias
|
|
||||||
echo ''
|
|
||||||
else
|
|
||||||
echo '# aliases 0'
|
|
||||||
fi
|
|
||||||
if export -p >/dev/null 2>&1; then
|
|
||||||
export_lines=$(export -p | awk '
|
|
||||||
/^(export|declare -x|typeset -x) / {
|
|
||||||
line=$0
|
|
||||||
name=line
|
|
||||||
sub(/^(export|declare -x|typeset -x) /, "", name)
|
|
||||||
sub(/=.*/, "", name)
|
|
||||||
if (name ~ /^(EXCLUDED_EXPORTS)$/) {
|
|
||||||
next
|
|
||||||
}
|
|
||||||
if (name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
|
|
||||||
print line
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
||||||
echo "# exports $export_count"
|
|
||||||
if [ -n "$export_lines" ]; then
|
|
||||||
printf '%s\n' "$export_lines"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
export_count=$(env | sort | awk -F= '$1 ~ /^[A-Za-z_][A-Za-z0-9_]*$/ { count++ } END { print count }')
|
|
||||||
echo "# exports $export_count"
|
|
||||||
env | sort | while IFS='=' read -r key value; do
|
|
||||||
case "$key" in
|
|
||||||
""|[0-9]*|*[!A-Za-z0-9_]*|EXCLUDED_EXPORTS) continue ;;
|
|
||||||
esac
|
|
||||||
escaped=$(printf "%s" "$value" | sed "s/'/'\"'\"'/g")
|
|
||||||
printf "export %s='%s'\n" "$key" "$escaped"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
"##;
|
|
||||||
script.replace("EXCLUDED_EXPORTS", &excluded)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn powershell_snapshot_script() -> &'static str {
|
|
||||||
r##"$ErrorActionPreference = 'Stop'
|
|
||||||
Write-Output '# Snapshot file'
|
|
||||||
Write-Output '# Unset all aliases to avoid conflicts with functions'
|
|
||||||
Write-Output 'Remove-Item Alias:* -ErrorAction SilentlyContinue'
|
|
||||||
Write-Output '# Functions'
|
|
||||||
Get-ChildItem Function: | ForEach-Object {
|
|
||||||
"function {0} {{`n{1}`n}}" -f $_.Name, $_.Definition
|
|
||||||
}
|
|
||||||
Write-Output ''
|
|
||||||
$aliases = Get-Alias
|
|
||||||
Write-Output ("# aliases " + $aliases.Count)
|
|
||||||
$aliases | ForEach-Object {
|
|
||||||
"Set-Alias -Name {0} -Value {1}" -f $_.Name, $_.Definition
|
|
||||||
}
|
|
||||||
Write-Output ''
|
|
||||||
$envVars = Get-ChildItem Env:
|
|
||||||
Write-Output ("# exports " + $envVars.Count)
|
|
||||||
$envVars | ForEach-Object {
|
|
||||||
$escaped = $_.Value -replace "'", "''"
|
|
||||||
"`$env:{0}='{1}'" -f $_.Name, $escaped
|
|
||||||
}
|
|
||||||
"##
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes shell snapshots that either lack a matching session rollout file or
|
/// Removes shell snapshots that either lack a matching session rollout file or
|
||||||
/// whose rollouts have not been updated within the retention window.
|
/// whose rollouts have not been updated within the retention window.
|
||||||
/// The active session id is exempt from cleanup.
|
/// The active session id is exempt from cleanup.
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ fn snapshot_file_name_parser_supports_legacy_and_suffixed_names() {
|
|||||||
fn bash_snapshot_filters_invalid_exports() -> Result<()> {
|
fn bash_snapshot_filters_invalid_exports() -> Result<()> {
|
||||||
let output = Command::new("/bin/bash")
|
let output = Command::new("/bin/bash")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(bash_snapshot_script())
|
.arg(snapshot_script(ShellType::Bash).expect("bash supports snapshots"))
|
||||||
.env("BASH_ENV", "/dev/null")
|
.env("BASH_ENV", "/dev/null")
|
||||||
.env("VALID_NAME", "ok")
|
.env("VALID_NAME", "ok")
|
||||||
.env("PWD", "/tmp/stale")
|
.env("PWD", "/tmp/stale")
|
||||||
@@ -153,7 +153,7 @@ fn bash_snapshot_preserves_multiline_exports() -> Result<()> {
|
|||||||
let multiline_cert = "-----BEGIN CERTIFICATE-----\nabc\n-----END CERTIFICATE-----";
|
let multiline_cert = "-----BEGIN CERTIFICATE-----\nabc\n-----END CERTIFICATE-----";
|
||||||
let output = Command::new("/bin/bash")
|
let output = Command::new("/bin/bash")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(bash_snapshot_script())
|
.arg(snapshot_script(ShellType::Bash).expect("bash supports snapshots"))
|
||||||
.env("BASH_ENV", "/dev/null")
|
.env("BASH_ENV", "/dev/null")
|
||||||
.env("MULTILINE_CERT", multiline_cert)
|
.env("MULTILINE_CERT", multiline_cert)
|
||||||
.output()?;
|
.output()?;
|
||||||
@@ -209,7 +209,7 @@ fn zsh_snapshot_restores_tied_path() -> Result<()> {
|
|||||||
let snapshot = Command::new("/bin/zsh")
|
let snapshot = Command::new("/bin/zsh")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(zsh_snapshot_script())
|
.arg(snapshot_script(ShellType::Zsh).expect("zsh supports snapshots"))
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.env("PATH", "/usr/bin:/bin")
|
.env("PATH", "/usr/bin:/bin")
|
||||||
.env("ZDOTDIR", dir.path())
|
.env("ZDOTDIR", dir.path())
|
||||||
@@ -246,7 +246,7 @@ fn zsh_snapshot_restores_tied_path() -> Result<()> {
|
|||||||
let readonly_snapshot = Command::new("/bin/zsh")
|
let readonly_snapshot = Command::new("/bin/zsh")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(zsh_snapshot_script())
|
.arg(snapshot_script(ShellType::Zsh).expect("zsh supports snapshots"))
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.env("PATH", "/usr/bin:/bin")
|
.env("PATH", "/usr/bin:/bin")
|
||||||
.env("ZDOTDIR", dir.path())
|
.env("ZDOTDIR", dir.path())
|
||||||
@@ -376,7 +376,7 @@ async fn snapshot_shell_does_not_inherit_stdin() -> Result<()> {
|
|||||||
let home_display = home.display();
|
let home_display = home.display();
|
||||||
let script = format!(
|
let script = format!(
|
||||||
"HOME=\"{home_display}\"; export HOME; {}",
|
"HOME=\"{home_display}\"; export HOME; {}",
|
||||||
bash_snapshot_script()
|
snapshot_script(ShellType::Bash).expect("bash supports snapshots")
|
||||||
);
|
);
|
||||||
let output = run_script_with_timeout(
|
let output = run_script_with_timeout(
|
||||||
&shell,
|
&shell,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Command parsing and safety utilities shared across Codex crates.
|
//! Command parsing and safety utilities shared across Codex crates.
|
||||||
|
|
||||||
pub mod shell_detect;
|
pub mod shell_detect;
|
||||||
|
pub mod shell_snapshot;
|
||||||
|
|
||||||
pub mod bash;
|
pub mod bash;
|
||||||
pub(crate) mod command_safety;
|
pub(crate) mod command_safety;
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
use crate::shell_detect::ShellType;
|
||||||
|
|
||||||
|
const EXCLUDED_EXPORT_VARS: &[&str] = &["PWD", "OLDPWD"];
|
||||||
|
|
||||||
|
/// Returns the shell-native script used to capture restorable shell state.
|
||||||
|
///
|
||||||
|
/// Command Prompt does not expose the POSIX or PowerShell state required by
|
||||||
|
/// this representation and therefore does not support snapshots.
|
||||||
|
pub fn snapshot_script(shell_type: ShellType) -> Option<String> {
|
||||||
|
match shell_type {
|
||||||
|
ShellType::Zsh => Some(zsh_snapshot_script()),
|
||||||
|
ShellType::Bash => Some(bash_snapshot_script()),
|
||||||
|
ShellType::Sh => Some(sh_snapshot_script()),
|
||||||
|
ShellType::PowerShell => Some(powershell_snapshot_script().to_string()),
|
||||||
|
ShellType::Cmd => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn excluded_exports_regex() -> String {
|
||||||
|
EXCLUDED_EXPORT_VARS.join("|")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zsh_snapshot_script() -> String {
|
||||||
|
let excluded = excluded_exports_regex();
|
||||||
|
let script = r##"if [[ -n "$ZDOTDIR" ]]; then
|
||||||
|
rc="$ZDOTDIR/.zshrc"
|
||||||
|
else
|
||||||
|
rc="$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
[[ -r "$rc" ]] && . "$rc"
|
||||||
|
print '# Snapshot file'
|
||||||
|
print '# Unset all aliases to avoid conflicts with functions'
|
||||||
|
print 'unalias -a 2>/dev/null || true'
|
||||||
|
print '# Functions'
|
||||||
|
functions
|
||||||
|
print ''
|
||||||
|
setopt_count=$(setopt | wc -l | tr -d ' ')
|
||||||
|
print "# setopts $setopt_count"
|
||||||
|
setopt | sed 's/^/setopt /'
|
||||||
|
print ''
|
||||||
|
alias_count=$(alias -L | wc -l | tr -d ' ')
|
||||||
|
print "# aliases $alias_count"
|
||||||
|
alias -L
|
||||||
|
print ''
|
||||||
|
export_lines=$(export -p | awk '
|
||||||
|
/^(export|declare -x|typeset -x) / {
|
||||||
|
line=$0
|
||||||
|
name=line
|
||||||
|
sub(/^(export|declare -x|typeset -x) /, "", name)
|
||||||
|
if (name ~ /^-[A-Za-z]*r[A-Za-z]* /) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (name ~ /^-[A-Za-z]*T[A-Za-z]* /) {
|
||||||
|
sub(/^-[A-Za-z]*T[A-Za-z]* /, "", name)
|
||||||
|
sub(/ [A-Za-z_][A-Za-z0-9_]*=.*/, "", name)
|
||||||
|
}
|
||||||
|
sub(/=.*/, "", name)
|
||||||
|
if (name ~ /^(EXCLUDED_EXPORTS)$/) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
print "# exports $export_count"
|
||||||
|
if [[ -n "$export_lines" ]]; then
|
||||||
|
print -r -- "$export_lines"
|
||||||
|
fi
|
||||||
|
"##;
|
||||||
|
script.replace("EXCLUDED_EXPORTS", &excluded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bash_snapshot_script() -> String {
|
||||||
|
let excluded = excluded_exports_regex();
|
||||||
|
let script = r##"if [ -z "$BASH_ENV" ] && [ -r "$HOME/.bashrc" ]; then
|
||||||
|
. "$HOME/.bashrc"
|
||||||
|
fi
|
||||||
|
echo '# Snapshot file'
|
||||||
|
echo '# Unset all aliases to avoid conflicts with functions'
|
||||||
|
unalias -a 2>/dev/null || true
|
||||||
|
echo '# Functions'
|
||||||
|
declare -f
|
||||||
|
echo ''
|
||||||
|
bash_opts=$(set -o | awk '$2=="on"{print $1}')
|
||||||
|
bash_opt_count=$(printf '%s\n' "$bash_opts" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
echo "# setopts $bash_opt_count"
|
||||||
|
if [ -n "$bash_opts" ]; then
|
||||||
|
printf 'set -o %s\n' $bash_opts
|
||||||
|
fi
|
||||||
|
echo ''
|
||||||
|
alias_count=$(alias -p | wc -l | tr -d ' ')
|
||||||
|
echo "# aliases $alias_count"
|
||||||
|
alias -p
|
||||||
|
echo ''
|
||||||
|
export_lines=$(
|
||||||
|
while IFS= read -r name; do
|
||||||
|
if [[ "$name" =~ ^(EXCLUDED_EXPORTS)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ ! "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
declare -xp "$name" 2>/dev/null || true
|
||||||
|
done < <(compgen -e)
|
||||||
|
)
|
||||||
|
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
echo "# exports $export_count"
|
||||||
|
if [ -n "$export_lines" ]; then
|
||||||
|
printf '%s\n' "$export_lines"
|
||||||
|
fi
|
||||||
|
"##;
|
||||||
|
script.replace("EXCLUDED_EXPORTS", &excluded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sh_snapshot_script() -> String {
|
||||||
|
let excluded = excluded_exports_regex();
|
||||||
|
let script = r##"if [ -n "$ENV" ] && [ -r "$ENV" ]; then
|
||||||
|
. "$ENV"
|
||||||
|
fi
|
||||||
|
echo '# Snapshot file'
|
||||||
|
echo '# Unset all aliases to avoid conflicts with functions'
|
||||||
|
unalias -a 2>/dev/null || true
|
||||||
|
echo '# Functions'
|
||||||
|
if command -v typeset >/dev/null 2>&1; then
|
||||||
|
typeset -f
|
||||||
|
elif command -v declare >/dev/null 2>&1; then
|
||||||
|
declare -f
|
||||||
|
fi
|
||||||
|
echo ''
|
||||||
|
if set -o >/dev/null 2>&1; then
|
||||||
|
sh_opts=$(set -o | awk '$2=="on"{print $1}')
|
||||||
|
sh_opt_count=$(printf '%s\n' "$sh_opts" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
echo "# setopts $sh_opt_count"
|
||||||
|
if [ -n "$sh_opts" ]; then
|
||||||
|
printf 'set -o %s\n' $sh_opts
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo '# setopts 0'
|
||||||
|
fi
|
||||||
|
echo ''
|
||||||
|
if alias >/dev/null 2>&1; then
|
||||||
|
alias_count=$(alias | wc -l | tr -d ' ')
|
||||||
|
echo "# aliases $alias_count"
|
||||||
|
alias
|
||||||
|
echo ''
|
||||||
|
else
|
||||||
|
echo '# aliases 0'
|
||||||
|
fi
|
||||||
|
if export -p >/dev/null 2>&1; then
|
||||||
|
export_lines=$(export -p | awk '
|
||||||
|
/^(export|declare -x|typeset -x) / {
|
||||||
|
line=$0
|
||||||
|
name=line
|
||||||
|
sub(/^(export|declare -x|typeset -x) /, "", name)
|
||||||
|
sub(/=.*/, "", name)
|
||||||
|
if (name ~ /^(EXCLUDED_EXPORTS)$/) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
echo "# exports $export_count"
|
||||||
|
if [ -n "$export_lines" ]; then
|
||||||
|
printf '%s\n' "$export_lines"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export_count=$(env | sort | awk -F= '$1 ~ /^[A-Za-z_][A-Za-z0-9_]*$/ { count++ } END { print count }')
|
||||||
|
echo "# exports $export_count"
|
||||||
|
env | sort | while IFS='=' read -r key value; do
|
||||||
|
case "$key" in
|
||||||
|
""|[0-9]*|*[!A-Za-z0-9_]*|EXCLUDED_EXPORTS) continue ;;
|
||||||
|
esac
|
||||||
|
escaped=$(printf "%s" "$value" | sed "s/'/'\"'\"'/g")
|
||||||
|
printf "export %s='%s'\n" "$key" "$escaped"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
"##;
|
||||||
|
script.replace("EXCLUDED_EXPORTS", &excluded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn powershell_snapshot_script() -> &'static str {
|
||||||
|
r##"$ErrorActionPreference = 'Stop'
|
||||||
|
Write-Output '# Snapshot file'
|
||||||
|
Write-Output '# Unset all aliases to avoid conflicts with functions'
|
||||||
|
Write-Output 'Remove-Item Alias:* -ErrorAction SilentlyContinue'
|
||||||
|
Write-Output '# Functions'
|
||||||
|
Get-ChildItem Function: | ForEach-Object {
|
||||||
|
"function {0} {{`n{1}`n}}" -f $_.Name, $_.Definition
|
||||||
|
}
|
||||||
|
Write-Output ''
|
||||||
|
$aliases = Get-Alias
|
||||||
|
Write-Output ("# aliases " + $aliases.Count)
|
||||||
|
$aliases | ForEach-Object {
|
||||||
|
"Set-Alias -Name {0} -Value {1}" -f $_.Name, $_.Definition
|
||||||
|
}
|
||||||
|
Write-Output ''
|
||||||
|
$envVars = Get-ChildItem Env:
|
||||||
|
Write-Output ("# exports " + $envVars.Count)
|
||||||
|
$envVars | ForEach-Object {
|
||||||
|
$escaped = $_.Value -replace "'", "''"
|
||||||
|
"`$env:{0}='{1}'" -f $_.Name, $escaped
|
||||||
|
}
|
||||||
|
"##
|
||||||
|
}
|
||||||
Reference in new issue
Block a user