
नेटवर्क-जागरूक SSH राउटर - सक्रिय VPN या नेटवर्क के आधार पर विभिन्न IPs/पोर्ट्स/कीज़/जंप होस्ट्स पर कनेक्शन रूट करता है
नेटवर्क-जागरूक SSH राउटर। आपके सक्रिय नेटवर्क या VPN का पता लगाता है और प्रत्येक SSH कनेक्शन के लिए स्वचालित रूप से सही होस्ट, पोर्ट, पहचान फ़ाइल और जंप होस्ट चुनता है — बिना ~/.ssh/config को छुए।
प्रत्येक तार्किक होस्ट को एक बार default प्रोफ़ाइल और वैकल्पिक प्रति-नेटवर्क ओवरराइड के साथ परिभाषित करें। हर कनेक्शन पर, sshroute पता लगाता है कि आप किस नेटवर्क पर हैं (VPN, ऑफिस LAN, WireGuard पीयर, आदि) और वास्तविक /usr/bin/ssh को सौंपने से पहले सही SSH पैरामीटर हल करता है।
ssh myserver
→ sshroute पता लगाता है: corp-vpn सक्रिय है
→ हल करता है: bastion.corp.internal के माध्यम से 10.100.0.50:2222
→ exec /usr/bin/ssh -p 2222 -i ~/.ssh/corp_key -J bastion.corp.internal 10.100.0.50
आपकी लैब में संभवतः कम से कम दो वास्तविकताएँ हैं: या तो आप घर पर LAN पर बैठे हैं, या आप दूर हैं और WireGuard या किसी अन्य VPN के माध्यम से आ रहे हैं। समस्या यह है कि ~/.ssh/config को पता नहीं है कि आप किसमें हैं — इसलिए आप अलग-अलग उपनाम (server-lan, server-vpn) के साथ समाप्त होते हैं, या एक जंप होस्ट जो केवल आधे समय काम करता है, या आप बस IP याद कर लेते हैं।
sshroute प्रत्येक कनेक्शन से पहले आपके वर्तमान नेटवर्क का पता लगाकर इसे हल करता है। जब WireGuard इंटरफ़ेस ऊपर होता है और पीयर रूट मौजूद होता है, तो यह सीधे टनल IP से कनेक्ट होता है। जब आप LAN पर होते हैं, तो यह स्थानीय पते का उपयोग करता है। जब कोई भी पहुंच योग्य नहीं होता है, तो यह सार्वजनिक होस्टनाम पर वापस आ जाता है। एक उपनाम, तीन वास्तविकताएँ, शून्य मैन्युअल स्विचिंग।
यह पारदर्शी रूप से SSH को भी इंटरसेप्ट करता है — git push, rsync, scp — एक बार जब आप शैडो मोड सेट करते हैं तो सब कुछ स्वचालित रूप से इसके माध्यम से जाता है। कोई रैपर नहीं, कोई शेल फ़ंक्शन नहीं, कोई सोचने की ज़रूरत नहीं।
एंटरप्राइज़ नेटवर्क और भी बदतर हैं। आपके पास सार्वजनिक इंटरनेट है, हो सकता है एक साइट-टू-साइट VPN, हो सकता है एक व्यक्तिगत VPN स्प्लिट-टनल, और उसके अंदर आपके पास अलग-अलग जंप होस्ट हैं जो इस बात पर निर्भर करते हैं कि आप किस वातावरण को लक्षित कर रहे हैं — dev, staging, prod, प्रत्येक का अपना बास्टियन और कुंजी है। इसे ~/.ssh/config में सीधा रखने का मतलब है या तो एक विशाल कॉन्फिग जो जब भी इंफ्रा बदलता है टूट जाता है, या आप एक स्क्रिप्ट लिखते हैं जिसे टीम का हर व्यक्ति अलग-अलग बनाए रखता है।
sshroute आपको रूटिंग लॉजिक को घोषणात्मक रूप से परिभाषित करने, इसे एक संस्करणित YAML फ़ाइल में रखने और इसे पूरी टीम में साझा करने देता है। एक ही कॉन्फिग सभी के लिए काम करता है — सही नेटवर्क स्वचालित रूप से इस आधार पर पता लगाया जाता है कि प्रत्येक मशीन पर कौन से इंटरफ़ेस या रूट सक्रिय हैं। कुंजियाँ, पोर्ट, उपयोगकर्ता और जंप होस्ट बिना उपयोगकर्ता को इसके बारे में सोचने के हल हो जाते हैं।
Teleport और Boundary एक अलग श्रेणी हैं — वे रूटिंग के शीर्ष पर एक्सेस कंट्रोल, ऑडिट लॉग और प्रमाणपत्र-आधारित प्रमाणीकरण जोड़ते हैं। यदि आपको वह चाहिए, तो उनका उपयोग करें। sshroute उसके लिए है जब आप केंद्रीय auth सर्वर चलाने के परिचालन बोझ के बिना रूटिंग इंटेलिजेंस चाहते हैं।
नवीनतम रिलीज़ GitHub Releases से डाउनलोड करें। Linux, macOS और Android के लिए AMD64 और ARM64 पर बाइनरी उपलब्ध हैं।
go install github.com/thereisnotime/sshroute@latest
GitHub Releases से android_arm64 टारबॉल डाउनलोड करें, निकालें और बाइनरी को ~/.local/bin में रखें:
mkdir -p ~/.local/bin
curl -Lo "$TMPDIR/sshroute.tar.gz" \
https://github.com/thereisnotime/sshroute/releases/latest/download/sshroute_android_arm64.tar.gz
tar -xzf "$TMPDIR/sshroute.tar.gz" -C ~/.local/bin sshroute
chmod +x ~/.local/bin/sshroute
यदि पहले से नहीं है तो ~/.local/bin को ~/.bashrc या ~/.profile में अपने PATH में जोड़ें:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
वैकल्पिक रूप से, Termux के Go के साथ स्रोत से संकलित करें। क्योंकि आधिकारिक Go टूलचेन android/arm64 बाइनरी प्रकाशित नहीं करती है, Termux जो भेजता है उसका उपयोग करने के लिए GOTOOLCHAIN=local सेट करें:
GOTOOLCHAIN=local go install github.com/thereisnotime/sshroute@latest
स्थापित करने के बाद, SSH बाइनरी पथ सेट करें क्योंकि Termux में /usr/bin/ssh नहीं है:
# ~/.config/sshroute/config.yaml
ssh_binary: /data/data/com.termux/files/usr/bin/ssh
या पर्यावरण चर के माध्यम से: export SSHROUTE_SSH=$(which ssh)
docker run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
SELinux-सक्षम सिस्टम (Fedora, RHEL, आदि) पर वॉल्यूम फ्लैग में :Z जोड़ें:
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute:Z \
ghcr.io/thereisnotime/sshroute network
sshroute को ssh के रूप में अपने $PATH में पहले स्थापित करें। आपके टर्मिनल, git, rsync, scp से सभी SSH कॉल स्वचालित रूप से इंटरसेप्ट हो जाती हैं। आपके कॉन्फिग में नहीं होने वाले होस्ट बिना बदलाव के /usr/bin/ssh के माध्यम से पास हो जाते हैं।
mkdir -p ~/.local/bin
ln -s $(which sshroute) ~/.local/bin/ssh
# ~/.bashrc या ~/.zshrc में जोड़ें यदि पहले से नहीं है:
export PATH="$HOME/.local/bin:$PATH"
# डिफ़ॉल्ट प्रोफ़ाइल के साथ एक होस्ट जोड़ें
sshroute add myserver --host myserver.example.com --user alice --key ~/.ssh/id_ed25519
# VPN-विशिष्ट ओवरराइड जोड़ें
sshroute add myserver --network vpn --host 10.8.0.50 --port 2222 --jump bastion.vpn
# कनेक्ट करें — नेटवर्क स्वचालित रूप से पता लगाया जाता है
sshroute connect myserver
# इसे चलाए बिना हल किया गया कमांड देखें
sshroute connect myserver --dry-run
# देखें कि वर्तमान में कौन सा नेटवर्क सक्रिय है
sshroute network
ये फ्लैग प्रत्येक कमांड पर लागू होते हैं:
initटिप्पणी किए गए उदाहरणों के साथ एक स्टार्टर कॉन्फिग फ़ाइल बनाएं। यदि फ़ाइल पहले से मौजूद है तो विफल हो जाता है।
| फ्लैग | डिफ़ॉल्ट | विवरण |
|---|---|---|
--force | false | मौजूदा कॉन्फिग फ़ाइल को अधिलेखित करें |
connect <alias>सक्रिय नेटवर्क का पता लगाएं, alias के लिए SSH पैरामीटर हल करें, और वास्तविक SSH बाइनरी को exec करें। उपनाम के बाद के किसी भी अतिरिक्त तर्क SSH को अपरिवर्तित पास कर दिए जाते हैं।
--reconnect के साथ, sshroute ड्रॉप किए गए कनेक्शन (लैपटॉप स्लीप, वाई-फाई हैंडऑफ़, नेटवर्कों के बीच रोमिंग) पर ssh को जीवित रखता है। क्योंकि यह हर पुनः कनेक्ट पर नेटवर्क का पुनः पता लगाता है, यह आपको एक अलग रूट पर फॉलो करता है: उदाहरण के लिए, LAN पर सोना और हॉटस्पॉट पर जागना सार्वजनिक रूट पर पुनः कनेक्ट होता है, न कि अब अप्राप्य LAN पते पर पुनः प्रयास करता है। एक स्वच्छ लॉगआउट (एग्जिट 0) या प्रमाणीकरण/रिमोट-कमांड विफलता लूप को रोकती है; केवल वास्तविक कनेक्शन ड्रॉप पुनः कनेक्ट होते हैं। पुनः कनेक्ट ssh को एक उपप्रक्रिया के रूप में चलाता है (--fallback की तरह), इसलिए sshroute सत्र के लिए निवासी रहता है; SIGINT/SIGTERM इसे तोड़ देता है। ब्लिप के दौरान सत्र स्थिति आपके मल्टीप्लेक्सर (tmux/zellij) का काम है; सीधे अपने सत्र में वापस आने के लिए --reconnect को -- tmux attach या -- zellij attach -c <name> के साथ संयोजित करें:
sshroute connect myserver --reconnect --fallback -- zellij attach -c work
listसभी कॉन्फ़िगर किए गए होस्ट और वर्तमान नेटवर्क पर उपयोग किए जाने वाले SSH पैरामीटर सूचीबद्ध करें। -o table|json|yaml का समर्थन करता है।
add <alias>एक होस्ट जोड़ें या मौजूदा को अपडेट करें। छोड़े गए फ्लैग अपना वर्तमान मान बनाए रखते हैं। प्रति-नेटवर्क ओवरराइड बनाने के लिए विभिन्न --network मानों के साथ कई बार चलाएं।
remove <alias>कॉन्फिग से alias के लिए सभी प्रोफ़ाइल हटाएं।
networkवर्तमान में पता लगाए गए नेटवर्क का नाम प्रिंट करें (या यदि कोई मेल नहीं खाता है तो default)।
network listसभी कॉन्फ़िगर किए गए नेटवर्क को उनकी प्राथमिकता, जांच नियम और वर्तमान सक्रिय स्थिति के साथ सूचीबद्ध करें। -o table|json|yaml का समर्थन करता है।
network test <name>नेटवर्क name के लिए प्रत्येक जांच चलाएं और प्रति नियम पास/फेल प्रिंट करें। डिटेक्शन लॉजिक को डीबग करने के लिए उपयोगी।
configकॉन्फिग फ़ाइल का हल किया गया पथ प्रिंट करें।
config editकॉन्फिग फ़ाइल को $EDITOR में खोलें (nano पर वापस आ जाता है)। यदि फ़ाइल और उसकी पैरेंट निर्देशिका मौजूद नहीं है तो उन्हें बनाता है।
resolve <alias>वर्तमान नेटवर्क पर alias के लिए उपयोग किए जाने वाले SSH पैरामीटर प्रिंट करें। डीबगिंग और स्क्रिप्टिंग के लिए उपयोगी। पता लगाए गए नेटवर्क को ओवरराइड करने के लिए --network <name> का उपयोग करें। -o table|json|yaml का समर्थन करता है।
| फ्लैग | डिफ़ॉल्ट | विवरण |
|---|---|---|
--network | ऑटो-डिटेक्ट | जिसके विरुद्ध हल करना है नेटवर्क प्रोफ़ाइल |
copy <alias> <src> <dst>connect के समान हल किए गए पैरामीटर (कुंजी, पोर्ट, जंप) के साथ scp का उपयोग करके किसी कॉन्फ़िगर किए गए होस्ट पर या उससे फ़ाइलें कॉपी करें। रिमोट पथों के लिए <alias>:<path> सिंटैक्स का उपयोग करें:
sshroute copy myserver ./local.txt myserver:/remote/path/
sshroute copy myserver myserver:/remote/file.txt ./local/
SSHROUTE_SCP पर्यावरण चर उपयोग किए गए scp बाइनरी को ओवरराइड करता है।
versionसंस्करण, git कमिट, बिल्ड तिथि और Go रनटाइम जानकारी प्रिंट करें।
updatesshroute को नवीनतम GitHub रिलीज़ में स्थान पर अपडेट करता है। यह आपके प्लेटफ़ॉर्म के लिए आर्काइव डाउनलोड करता है, checksums.txt के विरुद्ध इसकी sha256 सत्यापित करता है, और — यदि cosign स्थापित है — रिलीज़ के cosign हस्ताक्षर को सत्यापित करता है, फिर चल रही बाइनरी को परमाणु रूप से बदलता है।
sshroute update # नवीनतम रिलीज़ डाउनलोड करें, सत्यापित करें और स्थापित करें
sshroute update --check # केवल रिपोर्ट करें कि कोई नया संस्करण उपलब्ध है या नहीं
sshroute update --force # नवीनतम को फिर से स्थापित करें भले ही पहले से वर्तमान हो
यदि sha256 (या cosign, जब मौजूद हो) सत्यापन विफल होता है, तो अपडेट रद्द हो जाता है और बाइनरी को अछूता छोड़ दिया जाता है। यह रिलीज़ बाइनरी की स्थापनाओं को लक्षित करता है; यदि आपने go install या पैकेज मैनेजर के माध्यम से स्थापित किया है, तो उसके साथ अपडेट करें।
डिफ़ॉल्ट स्थान: ~/.config/sshroute/config.yaml
networks:
corp-vpn:
priority: 10 # कम = पहले जांचा गया
checks:
- type: interface
match: wg0
- type: route
match: 10.100.0.0
office:
priority: 20
checks:
- type: ping
host: 192.168.1.1
timeout: 500ms
hosts:
myserver:
default: # आवश्यक — जब कोई नेटवर्क मेल नहीं खाता तब उपयोग किया जाता है
host: myserver.example.com
port: 22
user: alice
key: ~/.ssh/id_ed25519
options: # वैकल्पिक — SSH को `-o Key=Value` फ्लैग के रूप में पास किया गया
ConnectTimeout: "10"
ServerAliveInterval: "30"
corp-vpn:
host: 10.100.0.50
port: 2222
key: ~/.ssh/corp_key
jump: bastion.corp.internal
options:
ConnectTimeout: "5" # इस नेटवर्क के लिए केवल डिफ़ॉल्ट को ओवरराइड करता है
office:
host: 192.168.1.50
प्रत्येक होस्ट में एक default प्रोफ़ाइल होनी चाहिए। नेटवर्क प्रोफ़ाइलों को केवल उन फ़ील्ड्स को निर्दिष्ट करने की आवश्यकता है जो डिफ़ॉल्ट से भिन्न हैं — अनसेट फ़ील्ड्स default से प्राप्त होते हैं।
options कुंजियाँ default से नेटवर्क प्रोफ़ाइलों में मर्ज की जाती हैं — नेटवर्क मान मिलान करने वाली कुंजियों को ओवरराइड करते हैं, गैर-अतिव्यापी कुंजियाँ प्राप्त होती हैं।
नेटवर्क का मूल्यांकन priority क्रम में किया जाता है (पहले सबसे कम मान)। वर्णमाला क्रम टाई तोड़ता है। पहला नेटवर्क जिसकी सभी जाँचें पास होती हैं, उपयोग किया जाता है; यदि कोई मेल नहीं खाता है, तो default लागू होता है।
एक नेटवर्क परिभाषा के भीतर कई जाँचें AND तर्क का उपयोग करती हैं — सभी को पास होना चाहिए।
उपयोग के लिए तैयार कॉन्फिग फ़ाइलें examples/ में हैं:
गहन गाइड docs/ में हैं:
सभी सूची कमांड एकाधिक आउटपुट प्रारूपों का समर्थन करते हैं:
sshroute list # table (डिफ़ॉल्ट)
sshroute list -o json # JSON — स्क्रिप्टिंग के लिए
sshroute list -o yaml # YAML
sshroute network list -o json
सॉफ्टवेयर प्राप्त करें — Releases से एक पूर्व-निर्मित बाइनरी डाउनलोड करें, go install github.com/thereisnotime/sshroute@latest के साथ स्थापित करें, या स्रोत से बनाएं।
प्रतिक्रिया और बग रिपोर्ट — GitHub Issues पर एक मुद्दा खोलें। अप्रत्याशित व्यवहार के लिए बग रिपोर्ट टेम्पलेट और विचारों के लिए फीचर अनुरोध टेम्पलेट का उपयोग करें।
योगदान — परियोजना को कैसे सेट अप करें, परीक्षण चलाएं और पुल अनुरोध खोलें, यह देखने के लिए CONTRIBUTING.md देखें। सुरक्षा कमजोरियों को GitHub Security Advisories के माध्यम से निजी तौर पर रिपोर्ट किया जाना चाहिए।
Go 1.22+ और just की आवश्यकता है।
git clone [email protected]:thereisnotime/sshroute.git
cd sshroute
just build # आउटपुट bin/sshroute
just build-all # क्रॉस-कम्पाइल linux/darwin × amd64/arm64
just test # रेस डिटेक्टर के साथ परीक्षण चलाएं
just install # संस्करण ldflags इंजेक्ट किए गए go install
|
|
| सुविधा | ~/.ssh/config | केवल WireGuard | Teleport / Boundary | sshroute |
|---|
| आपके वर्तमान नेटवर्क का पता लगाता है | ❌ | ❌ | ❌ | ✅ |
| स्वचालित रूप से सबसे अच्छा पथ चुनता है | ❌ | ❌ | ❌ | ✅ |
| कनेक्शन विफलता पर वापस आ जाता है | ❌ | ❌ | ✅ | ✅ |
| ड्रॉप पर ऑटो-रीकनेक्ट + री-रूट | ❌ | ⚠️ टनल रोम करता है | ⚠️ निश्चित प्रॉक्सी के माध्यम से | ✅ |
| प्रति होस्ट एक कमांड, किसी भी स्थान पर | ❌ | ⚠️ VPN ऊपर होना चाहिए | ✅ | ✅ |
| 10 होस्ट × 4 पथों के लिए कॉन्फिग आकार | 📄 ~600 पंक्तियाँ | 📄 ~600 पंक्तियाँ + VPN कॉन्फिग | 📄 सर्वर-साइड कॉन्फिग | 📄 ~60 पंक्तियाँ |
| रोमिंग मोबाइल उपकरण | ⚠️ मैन्युअल उपनाम | ⚠️ VPN आवश्यक | ✅ | ✅ |
| जंप होस्ट ऑटो-चेनिंग | ⚠️ मैन्युअल -J | ➖ लागू नहीं | ✅ | ✅ |
| scp / rsync / git / Ansible के साथ काम करता है | ✅ | ✅ | ⚠️ आंशिक | ✅ |
| लक्ष्यों पर कोई सर्वर-साइड इंस्टॉल नहीं | ✅ | ❌ | ❌ | ✅ |
| चलाने के लिए कोई auth सर्वर या डेमन नहीं | ✅ | ❌ | ❌ | ✅ |
| कोई क्लाइंट एजेंट नहीं | ✅ | ❌ | ❌ | ✅ |
| ओपन सोर्स, पूरी तरह से स्व-होस्टेड | ✅ | ✅ | ⚠️ ओपन-कोर | ✅ |
| फ्लैग | एनवी वेर | डिफ़ॉल्ट | विवरण |
|---|
--config | SSHROUTE_CONFIG | ~/.config/sshroute/config.yaml | कॉन्फिग फ़ाइल पथ |
-o, --output | table | आउटपुट प्रारूप: table, json, yaml | |
-v, --verbose | SSHROUTE_VERBOSE=1 | false | stderr पर डीबग लॉगिंग |
--dry-run | false | निष्पादित किए बिना हल किया गया SSH कमांड प्रिंट करें |
| फ्लैग | डिफ़ॉल्ट | विवरण |
|---|
--fallback | false | प्राथमिकता क्रम में प्रत्येक प्रोफ़ाइल का प्रयास करें, केवल कनेक्शन विफलता (एग्जिट 255) पर अगले पर पुनः प्रयास करें |
--reconnect | false | कनेक्शन की निगरानी करें और जब यह ड्रॉप हो जाए तो स्वचालित रूप से पुनः कनेक्ट करें, हर बार सक्रिय नेटवर्क का पुनः पता लगाएं और रूट को पुनः हल करें |
--reconnect-delay | 2s | जब --reconnect सेट हो तो पुनः कनेक्ट प्रयासों के बीच प्रतीक्षा करें |
| फ्लैग | डिफ़ॉल्ट | विवरण |
|---|
--host | होस्टनाम या IP पता | |
--port | 22 | SSH पोर्ट |
--user | SSH उपयोगकर्ता नाम | |
--key | पहचान फ़ाइल का पथ (~ का समर्थन करता है) | |
--jump | जंप होस्ट — SSH को -J के रूप में पास किया जाता है | |
--network | default | पैरामीटर लिखने के लिए नेटवर्क प्रोफ़ाइल |
| फ़ील्ड | प्रकार | विवरण |
|---|
host | string | होस्टनाम या IP पता |
port | int | SSH पोर्ट (डिफ़ॉल्ट: 22) |
user | string | SSH उपयोगकर्ता |
key | string | पहचान फ़ाइल का पथ (~ का विस्तार किया जाता है) |
jump | string | जंप होस्ट उपनाम या user@host |
options | map | मनमाना SSH -o Key=Value फ्लैग (जैसे ConnectTimeout, StrictHostKeyChecking) |
comment | string | sshroute list में दिखाया गया विवरण |
tags | list | sshroute list --tag के साथ फ़िल्टर करने के लिए टैग |
| जाँच प्रकार | पास कब होता है | आवश्यक फ़ील्ड |
|---|
route | सबनेट/IP कर्नेल रूटिंग टेबल में दिखाई देता है | match |
interface | नामित इंटरफ़ेस मौजूद है और संचालनात्मक रूप से ऊपर है | match |
ping | होस्ट टाइमआउट के भीतर ICMP echo का जवाब देता है | host, timeout (वैकल्पिक, डिफ़ॉल्ट 2s) |
exec | शेल कमांड कोड 0 के साथ बाहर निकलता है | command |
| फ़ाइल | उपयोग का मामला |
|---|
basic.yaml | एकल होस्ट, VPN बनाम सार्वजनिक फ़ॉलबैक |
multi-network.yaml | ऑफिस LAN, कॉर्प VPN, रिमोट VPN, सार्वजनिक |
wireguard-backconnect.yaml | WireGuard पीयर जो आपसे बैककनेक्ट करता है |
jump-hosts.yaml | प्रति नेटवर्क अलग-अलग बास्टियन |
multi-zone-roaming.yaml | WireGuard गेटवे और रोमिंग मोबाइल उपकरणों के साथ मल्टी-ज़ोन होमलैब |
| गाइड | विवरण |
|---|
| Homelab सेटअप | WireGuard, जंप होस्ट, NAS, k3s नोड्स के साथ मल्टी-ज़ोन होमलैब |
| मल्टी-ज़ोन रोमिंग | एकाधिक LAN, WireGuard गेटवे, मोबाइल उपकरण जो नेटवर्कों के बीच रोम करते हैं |
| कॉर्पोरेट / मल्टी-एनवायरनमेंट | प्रति-एनवायरनमेंट बास्टियन और VPN डिटेक्शन के साथ Dev/staging/prod |
| शैडो मोड | पारदर्शी SSH प्रतिस्थापन — git, rsync, scp, Ansible |
| शेल कम्पलीशन | bash, zsh, fish के लिए डायनामिक उपनाम पूर्णता |
| स्क्रिप्टिंग और ऑटोमेशन | स्क्रिप्ट और CI पाइपलाइनों में resolve और copy का उपयोग करना |