diff --git a/packageData.json b/packageData.json index 00f57d3..e6d328b 100644 --- a/packageData.json +++ b/packageData.json @@ -140,14 +140,14 @@ "install": { "linux": "asdf plugin add nodejs && asdf install nodejs 16.20.2 && asdf global nodejs 16.20.2", "mac": "asdf plugin add nodejs && asdf install nodejs 18.18.0 && asdf global nodejs 18.18.0", - "windows": "choco install nvm -y; nvm install 18.18.0; nvm use 18.18.0" + "windows": "choco install nvm -y ; nvm install 18.18.0 ; nvm use 18.18.0" }, "type": "force" }, "OpenSSH": { "description": "", "install": { - "linux": "sudo apt install openssh-server -y", + "linux": "sudo apt install openssh-server -y && mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys", "mac": "", "windows": "" }, @@ -234,6 +234,24 @@ }, "type": "disable" }, + "TypeScript": { + "description": "", + "install": { + "linux": "npm install -g typescript ts-node", + "mac": "npm install -g typescript ts-node", + "windows": "npm install -g typescript ts-node" + }, + "type": "enable" + }, + "VSCode Extension Development": { + "description": "VSCode 擴充套件開發工具", + "install": { + "linux": "npm install -g yo generator-code vsce esbuild", + "mac": "npm install -g yo generator-code vsce esbuild", + "windows": "npm install -g yo generator-code vsce esbuild" + }, + "type": "disable" + }, "Virtual Box": { "description": "自由及開放原始碼的虛擬機器軟體", "install": { @@ -510,7 +528,7 @@ "description": "", "install": { "linux": "", - "mac": "brew install --cask microsoft-edge", + "mac": "brew install --cask microsoft-edge && echo 'export CHROME_EXECUTABLE=\"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\"' >>~/.zshrc", "windows": "" }, "type": "enable" diff --git a/scripts/linux/.gitkeep b/scripts/linux/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/linux/asdf.sh b/scripts/linux/asdf.sh new file mode 100644 index 0000000..075a78e --- /dev/null +++ b/scripts/linux/asdf.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0 +echo '. "$HOME/.asdf/asdf.sh"' >>~/.bashrc +echo '. "$HOME/.asdf/completions/asdf.bash"' >>~/.bashrc +source ~/.bashrc diff --git a/scripts/linux/basesetup.sh b/scripts/linux/basesetup.sh new file mode 100644 index 0000000..e4ada87 --- /dev/null +++ b/scripts/linux/basesetup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +sudo apt install -y curl software-properties-common apt-transport-https lsb-release +sudo apt update && sudo apt upgrade -y + +# Load git-prompt.sh to use __git_ps1 for Git branch name +echo "# Load git prompt script for version control information" >> ~/.bashrc +echo "if type __git_ps1 &>/dev/null; then" >> ~/.bashrc +echo " GIT_PS1_SHOWDIRTYSTATE=1" >> ~/.bashrc +echo " GIT_PS1_SHOWSTASHSTATE=1" >> ~/.bashrc +echo " GIT_PS1_SHOWUNTRACKEDFILES=1" >> ~/.bashrc +echo " GIT_PS1_SHOWUPSTREAM='auto'" >> ~/.bashrc +echo " PROMPT_COMMAND='__git_ps1 \"\\[\\e[32m\\]\\w\\[\\e[0m\\]\" \" \\\$ \" \""'"'%F{87}(%s)%f'"'"'\"'" >> ~/.bashrc +echo "else" >> ~/.bashrc +echo " PS1='\\[\\e[32m\\]\\w\\[\\e[0m\\] \$ '" >> ~/.bashrc +echo "fi" >> ~/.bashrc +echo "" >> ~/.bashrc diff --git a/scripts/linux/poetry.sh b/scripts/linux/poetry.sh new file mode 100644 index 0000000..4de8fe2 --- /dev/null +++ b/scripts/linux/poetry.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +curl -sSL https://install.python-poetry.org | python3 +echo 'export PATH="$HOME/.local/bin:$PATH"' >>~/.bashrc +source ~/.bashrc diff --git a/scripts/macos/.gitkeep b/scripts/macos/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/macos/asdf.sh b/scripts/macos/asdf.sh new file mode 100644 index 0000000..9ef5fdb --- /dev/null +++ b/scripts/macos/asdf.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +brew install asdf +echo ". /opt/homebrew/opt/asdf/libexec/asdf.sh" >>~/.zshrc +source ~/.zshrc diff --git a/scripts/macos/homebrew.sh b/scripts/macos/homebrew.sh new file mode 100644 index 0000000..66c470d --- /dev/null +++ b/scripts/macos/homebrew.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +( + echo + echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' +) >>~/.zprofile +eval "$(/opt/homebrew/bin/brew shellenv)" +source ~/.zprofile diff --git a/scripts/macos/macsetup.sh b/scripts/macos/macsetup.sh new file mode 100644 index 0000000..34c589f --- /dev/null +++ b/scripts/macos/macsetup.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Reset Launchpad +# ////////////////////////////////////////////////// +defaults write com.apple.dock ResetLaunchPad -bool true +killall Dock + +# Display full path in Finder title bar +# ////////////////////////////////////////////////// +defaults write com.apple.finder _FXShowPosixPathInTitle -bool true + +# Show path bar in Finder +# ////////////////////////////////////////////////// +defaults write com.apple.finder ShowPathbar -bool true diff --git a/scripts/macos/poetry.sh b/scripts/macos/poetry.sh new file mode 100644 index 0000000..e4ea19b --- /dev/null +++ b/scripts/macos/poetry.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +curl -sSL https://install.python-poetry.org | python3 +echo 'export PATH="$HOME/.local/bin:$PATH"' >>~/.zshrc +source ~/.zshrc diff --git a/scripts/macos/zshsetup.sh b/scripts/macos/zshsetup.sh new file mode 100644 index 0000000..8887a9f --- /dev/null +++ b/scripts/macos/zshsetup.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +echo "# Load version control information" >>~/.zshrc +echo "autoload -Uz vcs_info" >>~/.zshrc +echo "precmd() { vcs_info }" >>~/.zshrc +echo "" >>~/.zshrc +echo "# Format the vcs_info_msg_0_ variable" >>~/.zshrc +echo "zstyle ':vcs_info:git:*' formats ' %F{87}(%b)%f'" >>~/.zshrc +echo "" >>~/.zshrc +echo "# Set up the prompt (with git branch name)" >>~/.zshrc +echo "setopt PROMPT_SUBST" >>~/.zshrc +echo "PROMPT='[%F{82}%~%f]\${vcs_info_msg_0_} $ '" >>~/.zshrc +echo "" >>~/.zshrc +source ~/.zshrc diff --git a/scripts/npm.setup b/scripts/npm.setup index e76180b..71ad725 100644 --- a/scripts/npm.setup +++ b/scripts/npm.setup @@ -1,4 +1,4 @@ -npm install -g npm +npm install -g npm@latest npm install -g pnpm npm install -g npm-check-updates npm install -g @leoli0605/git-setup diff --git a/scripts/windows/.gitkeep b/scripts/windows/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/windows/PoetryInstall.ps1 b/scripts/windows/PoetryInstall.ps1 new file mode 100644 index 0000000..71ac649 --- /dev/null +++ b/scripts/windows/PoetryInstall.ps1 @@ -0,0 +1 @@ +(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - diff --git a/src/bash.mjs b/src/bash.mjs new file mode 100644 index 0000000..c2530e5 --- /dev/null +++ b/src/bash.mjs @@ -0,0 +1,25 @@ +import fs from 'fs'; +import path, { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { getAppDir } from './dirname.mjs'; +import ShellScript from './shell_script.mjs'; + +const __dirname = getAppDir(); + +class Bash extends ShellScript { + constructor() { + super(); + this.scripts.push('#!/bin/bash\n'); + } + + getScript() { + const paths = this.environment.map((e) => `"${e}"`).join(',\n '); + let envSetup = fs.readFileSync(path.join(__dirname, '/scripts/windows/EnvSetup.ps1'), 'utf8'); + envSetup = envSetup.replace('${PATHS}', paths); + this.scripts.push(envSetup + '\n'); + this.scripts.push(this.commands.join('\n')); + return this.scripts.toString().replace(/(^,)/gm, '') + '\n'; + } +} + +export default Bash; diff --git a/src/index.mjs b/src/index.mjs index b16c911..635fae3 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -1,7 +1,6 @@ import fs from 'fs'; import os from 'os'; -import path, { dirname } from 'path'; -import { fileURLToPath } from 'url'; +import path from 'path'; import { cmd } from './cmd_process.mjs'; import { getAppDir } from './dirname.mjs'; import { selectPackages } from './package_selector.mjs'; @@ -11,63 +10,122 @@ const __dirname = getAppDir(); selectPackages() .then((selectedPackages) => { + let shell; let command = ''; let args = []; if (os.platform() === 'win32') { - const ps = new PowerShell(); + console.log('Windows detected'); + shell = new PowerShell(); + for (const p of selectedPackages) { if (p) { - ps.addCommand(p.installCommand); + shell.addCommand(p.installCommand); } } - ps.addCommand('refreshenv\n'); // for powershell to refresh environment variables + shell.addCommand('refreshenv\n'); // for powershell to refresh environment variables if (selectedPackages.some((p) => p && p.packageName.startsWith('Python'))) { - ps.addEnvironment('$HOME\\AppData\\Roaming\\Python\\Scripts'); - ps.addCommand('python.exe -m pip install --upgrade pip'); // for pip - ps.addCommand('(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -'); // for poetry - ps.addCommand('poetry config virtualenvs.in-project true'); // for poetry + shell.addEnvironment('$HOME\\AppData\\Roaming\\Python\\Scripts'); + const poetryInstall = fs.readFileSync(path.join(__dirname, 'scripts/windows/PoetryInstall.ps1'), 'utf8').trim(); + shell.addCommand(poetryInstall); + shell.addCommand('poetry config virtualenvs.in-project true'); // for poetry const pipSetup = fs.readFileSync(path.join(__dirname, 'scripts/pip.setup'), 'utf8').trim(); - ps.addCommand(pipSetup); + shell.addCommand(pipSetup); } if (selectedPackages.some((p) => p && p.packageName.startsWith('Rust'))) { - ps.addEnvironment('$HOME\\leoli\\.cargo\\bin'); + shell.addEnvironment('$HOME\\leoli\\.cargo\\bin'); } - ps.addCommand('refreshenv\n'); // for powershell to refresh environment variables + shell.addCommand('refreshenv\n'); // for powershell to refresh environment variables if (selectedPackages.some((p) => p && p.packageName.startsWith('Node.js'))) { const npmSetup = fs.readFileSync(path.join(__dirname, 'scripts/npm.setup'), 'utf8').trim(); - ps.addCommand(npmSetup); - ps.addCommand('npx @leoli0605/git-setup'); // for git + shell.addCommand(npmSetup); } - ps.addCommand('refreshenv\n'); // for powershell to refresh environment variables + shell.addCommand('refreshenv\n'); // for powershell to refresh environment variables if (selectedPackages.some((p) => p && p.packageName.startsWith('Sublime Text'))) { let sublimeSetup = fs.readFileSync(path.join(__dirname, 'scripts/windows/SublimeSetup.ps1'), 'utf8').trim(); sublimeSetup = sublimeSetup.replace('${PATH}', path.join(__dirname, 'scripts/windows/Preferences.sublime-settings')); - ps.addCommand(sublimeSetup); + shell.addCommand(sublimeSetup); } - ps.addCommand('refreshenv\n'); // for powershell to refresh environment variables - - const psScript = ps.getScripts(); - // fs.writeFileSync(path.join(__dirname, 'scripts.ps1.log'), psScript); + shell.addCommand('refreshenv\n'); // for powershell to refresh environment variables + shell.addCommand('npx @leoli0605/git-setup'); // for git + const script = shell.getScript(); - const encodedPsScript = Buffer.from(psScript, 'utf16le').toString('base64'); + const encodedScript = Buffer.from(script, 'utf16le').toString('base64'); command = 'powershell.exe'; - args = ['-NoProfile', '-EncodedCommand', encodedPsScript]; + args = ['-NoProfile', '-EncodedCommand', encodedScript]; } else if (os.platform() === 'darwin') { console.log('MacOS detected'); + shell = new Bash(); + + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/macos/homebrew.sh'), 'utf8').trim()); + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/macos/asdf.sh'), 'utf8').trim()); + + for (const p of selectedPackages) { + if (p) { + shell.addCommand(p.installCommand); + } + } + + if (selectedPackages.some((p) => p && p.packageName.startsWith('Python'))) { + const poetryInstall = fs.readFileSync(path.join(__dirname, 'scripts/macos/poetry.sh'), 'utf8').trim(); + shell.addCommand(poetryInstall); + shell.addCommand('poetry config virtualenvs.in-project true'); // for poetry + const pipSetup = fs.readFileSync(path.join(__dirname, 'scripts/pip.setup'), 'utf8').trim(); + shell.addCommand(pipSetup); + } + if (selectedPackages.some((p) => p && p.packageName.startsWith('Node.js'))) { + const npmSetup = fs.readFileSync(path.join(__dirname, 'scripts/npm.setup'), 'utf8').trim(); + shell.addCommand(npmSetup); + } + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/macos/zshsetup.sh'), 'utf8').trim()); + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/macos/macsetup.sh'), 'utf8').trim()); + shell.addCommand('npx @leoli0605/git-setup'); // for git + const script = shell.getScript(); + + command = 'bash'; + args = ['-c', script]; } else if (os.platform() === 'linux') { const distro = os .release() .split('.') .map((v) => parseInt(v)); console.log('Linux distribution version:', distro); + shell = new Bash(); + + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/linux/basesetup.sh'), 'utf8').trim()); + shell.addCommand(fs.readFileSync(path.join(__dirname, 'scripts/linux/asdf.sh'), 'utf8').trim()); + + for (const p of selectedPackages) { + if (p) { + shell.addCommand(p.installCommand); + } + } if (distro[0] >= 20 /* && distro[1] === 4 */) { console.log('Ubuntu 20.04 detected'); + shell.addCommand('asdf install nodejs 18.18.0'); + shell.addCommand('asdf global nodejs 18.18.0'); } else { console.log('Ubuntu 20.04 not detected'); } + + if (selectedPackages.some((p) => p && p.packageName.startsWith('Python'))) { + const poetryInstall = fs.readFileSync(path.join(__dirname, 'scripts/linux/poetry.sh'), 'utf8').trim(); + shell.addCommand(poetryInstall); + shell.addCommand('poetry config virtualenvs.in-project true'); // for poetry + const pipSetup = fs.readFileSync(path.join(__dirname, 'scripts/pip.setup'), 'utf8').trim(); + shell.addCommand(pipSetup); + } + if (selectedPackages.some((p) => p && p.packageName.startsWith('Node.js'))) { + const npmSetup = fs.readFileSync(path.join(__dirname, 'scripts/npm.setup'), 'utf8').trim(); + shell.addCommand(npmSetup); + } + shell.addCommand('npx @leoli0605/git-setup'); // for git + const script = shell.getScript(); + + command = 'bash'; + args = ['-c', script]; } console.log(`command: ${command}`); diff --git a/src/powershell.mjs b/src/powershell.mjs index 72dc03d..69a892a 100644 --- a/src/powershell.mjs +++ b/src/powershell.mjs @@ -13,7 +13,7 @@ class PowerShell extends ShellScript { this.scripts.push(fs.readFileSync(path.join(__dirname, 'scripts/windows/Chocolatey.ps1'), 'utf8') + '\n'); } - getScripts() { + getScript() { const paths = this.environment.map((e) => `"${e}"`).join(',\n '); let envSetup = fs.readFileSync(path.join(__dirname, 'scripts/windows/EnvSetup.ps1'), 'utf8'); envSetup = envSetup.replace('${PATHS}', paths); diff --git a/src/shell_script.mjs b/src/shell_script.mjs index 2c6f343..1df02af 100644 --- a/src/shell_script.mjs +++ b/src/shell_script.mjs @@ -15,7 +15,7 @@ class ShellScript { console.log(`Environment added: ${value}`); } - getScripts() { + getScript() { throw new Error('You have to implement the method getScripts!'); } }