Compare commits

...

21 Commits

Author SHA1 Message Date
2fd5ae8aa6 Fix Linux dnf install 2025-08-17 20:45:29 +01:00
70d13b30b1 Fix ripgrep assert URL 2025-07-22 19:28:54 +00:00
b80f8edc29 Add bootstrap-Linux-rootless.sh
This is intended to install a MVP development environment on machines
where no root access is granted.
2025-07-09 12:20:21 +01:00
20cf3617bc Don't print skipping in bootstrap-Windows.ps1 2024-08-09 17:41:23 +01:00
379c081144 Add self-removal to bootstrap-Windows.ps1 2024-08-09 17:35:49 +01:00
2dea6fce1f Update bootstrap-macOS.sh (untested) 2024-08-09 17:30:38 +01:00
dd1c8e09e8 Reinstate removal of script 2024-08-09 17:16:37 +01:00
c6593d5ff2 Remove bootstrap-Windows.sh 2024-08-09 17:15:50 +01:00
82542168f8 Remove $ from commands to make copying easier 2024-08-09 17:14:56 +01:00
398a9019ea Consolidate Linux scripts into one 2024-08-09 17:13:08 +01:00
17d438ce0b Remove bootstrap-Arch.sh 2024-08-09 16:28:38 +01:00
668fcca581 Remove Python package components 2024-08-09 16:27:43 +01:00
1b8e170fd9 Update links to point at new URL & branch 2024-08-09 16:24:18 +01:00
5b629fc5b4 Update bootstrap-Windows.ps1 script
* Add 1Password install step
* Add Scoop install step
* Remove SSH key generation step
* Make 1Password, Hyper-V, Containers, and SSH Server steps optional
* Refactor to run Scoop install as unelevated then relaunch as elevated
  for remaining tasks
2024-08-09 15:37:24 +01:00
3606f4bbb9 Update Windows bootstrap scripts
* Add `bootstrap-Windows.ps1` sets up the bare minimum required to start
  Debian in WSL2 sharing SSH keys with the Windows host and enable SSH
  server for remote access.
* Add `bootstrap-Windows.sh` copies Windows host SSH keys, upgrades
  Debian to unstable, then installs Ansible and dependencies.
* Remove `bootstrap-Windows.cmd`.
2022-06-11 15:45:58 +01:00
9c3a2d89b8 Add bootstrap-Fedora.sh
usage: ./bootstrap-Fedora.sh [-h] [-y]

Bootstrap a Debian based distribution with:

* upgrade dnf packages
* git - from dnf
* virtualenv - from dnf
* SSH key - from ssh-keygen
* GitHub public key - with SSH key
* GitLab public key - with SSH key
* BitBucket Cloud public key - with SSH key
* Gogs Cloud public key - with SSH key
* conduit - configuration manager

If any already exist they will not be reinstalled.

optional arguments:
        -h              show this help message and exit
        -y              assume yes when prompted
2020-12-31 15:16:52 +00:00
91e205f44e Add bootstrap-Arch.sh
usage: ./bootstrap-Arch.sh [-h] [-y]

Bootstrap an Arch Linux based distribution with:

* update pacman database
* upgrade pacman packages
* git - from pacman
* python-pip - from pacman
* python-virtualenv - from pacman
* SSH key - from ssh-keygen
* GitHub public key - with SSH key
* GitLab public key - with SSH key
* BitBucket Cloud public key - with SSH key
* Gogs public key - with SSH key
* conduit - configuration manager

If any already exist they will not be reinstalled.

optional arguments:
         -h             show this help message and exit
         -y             assume yes when prompted
2019-09-17 20:38:15 +01:00
8ac80fe9e8 Don't use deprecated GitLab session endpoint 2019-07-05 11:03:35 +01:00
72f6c868c8 Fix one-liners so they work with user input 2019-07-05 11:02:49 +01:00
9433857356 Add bootstrap-Windows.cmd
usage: bootstrap-Windows.cmd [/?]

Bootstrap a macOS instance with:

* Windows SDK - from MSDN
* Chocolatey - package manager
* Cmder, including Git - from Chocolatey
* Python - from Chocolatey
* virtualenv - from pip
* SSH key - from ssh-keygen
* GitHub public key - with SSH key
* GitLab public key - with SSH key
* BitBucket Cloud public key - with SSH key
* Gogs Cloud public key - with SSH key
* conduit - configuration manager

optional arguments:
        /?              show this help message and exit
2018-06-03 22:05:32 +01:00
20f1b08e04 Add bootstrap-Debian.sh
usage: ./bootstrap-Debian.sh [-h] [-y]

Bootstrap a Debian based distribution with:

* update apt cache
* upgrade apt packages
* git - from apt
* python - from apt
* python-pip - from apt
* virtualenv - from pip
* SSH key - from ssh-keygen
* GitHub public key - with SSH key
* GitLab public key - with SSH key
* BitBucket Cloud public key - with SSH key
* Gogs Cloud public key - with SSH key
* conduit - configuration manager

If any already exist they will not be reinstalled.

optional arguments:
        -h              show this help message and exit
        -y              assume yes when prompted
2018-06-03 22:05:27 +01:00
7 changed files with 268 additions and 256 deletions

View File

@@ -4,18 +4,24 @@ Bootstrap an OS instance with bare essentials.
## Usage ## Usage
To bootstrap a Linux instance choose one of:
```bash
wget https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Linux.sh && chmod +x bootstrap-Linux.sh && ./bootstrap-Linux.sh
```
```bash
curl -O https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Linux.sh && chmod +x bootstrap-Linux.sh && ./bootstrap-Linux.sh
```
To bootstrap a Windows instance:
```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-WebRequest -Uri "https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Windows.ps1" -OutFile "./bootstrap-Windows.ps1"; ./bootstrap-Windows.ps1
```
To bootstrap a macOS instance: To bootstrap a macOS instance:
``` ```bash
$ curl -s https://code.infektor.net/config/bootstrap/raw/master/bootstrap-macOS.sh | sh curl -O https://git.infektor.net/config/bootstrap/raw/main/bootstrap-macOS.sh && chmod +x bootstrap-macOS.sh && ./bootstrap-macOS.sh
```
## Package
Install as a pip package to set SSH keys on any of GitHub, GitLab, BitBucket
Cloud, or Gogs servers:
```
pip install git+https://code.infektor.net/config/bootstrap.git
python -c 'import bootstrap; bootstrap.set_ssh_keys()'
``` ```

91
bootstrap-Linux-rootless.sh Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env bash
error() {
echo "$1 not found" >&2
exit 1
}
download() {
local url=$1
local output=$2
if command -v curl &> /dev/null; then
curl -o $output --location $url
elif command -v wget &> /dev/null; then
wget -O $output $url
else
error "curl/wget not found"
fi
}
download https://git.infektor.net/config/local/raw/branch/main/roles/bash/templates/bashrc ~/.bashrc
download https://git.infektor.net/config/local/raw/branch/main/roles/readline/templates/inputrc ~/.inputrc
if ! command -v git &> /dev/null; then
error "git not found"
fi
git-clone-or-pull() {
local repo=$1
local dir=$2
if [ -d $dir ]; then
git -C $dir pull
else
git clone $repo $dir
fi
}
git-clone-or-pull https://git.infektor.net/config/nvim.git ~/.config/nvim
# TODO: install nvim
git-clone-or-pull https://git.infektor.net/config/zsh.git ~/.config/zsh
# TODO: build & install zsh
if command -v zsh &> /dev/null; then
zsh ~/.config/zsh/install.zsh
fi
git-clone-or-pull https://git.infektor.net/config/tmux.git ~/.config/tmux
~/.config/tmux/install.sh
mkdir -p ~/.local/bin
mkdir -p ~/.local/share/man/man1
mkdir -p ~/.local/share/zsh/site-functions
if ! command -v rg &> /dev/null; then
mkdir -p ~/.cache/tmp
pushd ~/.cache/tmp
download https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz rg.tar.gz
tar zxf rg.tar.gz --strip-components=1 --no-same-owner
mv rg ~/.local/bin
mv doc/rg.1 ~/.local/share/man/man1/rg.1
mv complete/_rg ~/.local/share/zsh/site-functions
popd
rm -r ~/.cache/tmp
fi
if ! command -v fzf &> /dev/null; then
mkdir -p ~/.cache/tmp
pushd ~/.cache/tmp
download https://github.com/junegunn/fzf/releases/download/v0.64.0/fzf-0.64.0-linux_amd64.tar.gz fzf.tar.gz
tar zxf fzf.tar.gz
mv fzf ~/.local/bin
popd
rm -r ~/.cache/tmp
fi
if ! command -v bat &> /dev/null; then
mkdir -p ~/.cache/tmp
pushd ~/.cache/tmp
download https://github.com/sharkdp/bat/releases/download/v0.25.0/bat-v0.25.0-x86_64-unknown-linux-musl.tar.gz bat.tar.gz
tar zxf bat.tar.gz --strip-components=1 --no-same-owner
mv bat ~/.local/bin
mv bat.1 ~/.local/share/man/man1
mv autocomplete/bat.zsh ~/.local/share/zsh/site-functions/_bat
popd
rm -r ~/.cache/tmp
fi

77
bootstrap-Linux.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/bin/bash
set -e
show_usage() {
echo "usage: $0 [-h] [-y]"
}
show_help() {
show_usage
echo
echo "Bootstrap Fedora or Debian-based Linux distribution with:"
echo
echo "* Update packages"
echo "* Install git, python3-pip"
echo "* Install ansible"
echo "* Clone configuration repository"
echo "* Install 1password"
echo
echo "If any already exist they will not be reinstalled."
echo
echo "optional arguments:"
echo " -h show this help message and exit"
echo " -y assume yes when prompted"
}
yes=0
while getopts 'hy' opt; do
case $opt in
h) show_help; exit 0 ;;
y) yes=1 ;;
*) show_usage; exit 1 ;;
esac
done
agree() {
local check=^[Nn]$
[ $yes -eq 1 ] && [[ ! "$2" =~ $check ]] && return 0
[[ "$2" =~ $check ]] && local default="[y/N]" || local default="[Y/n]"
read -p "$1 $default? " answer
case "$answer" in
y|Y|yes) return 0 ;;
n|N|no) return 1 ;;
'') [[ "$2" =~ $check ]] && return 1 || return 0 ;;
*) echo "invalid input: $answer" && return `agree "$1"` ;;
esac
}
if command -v apt &> /dev/null; then
agree "Update apt cache" && sudo apt-get update
agree "Upgrade apt packages" "N" && sudo apt-get upgrade
echo "Install git python3-pip"
sudo apt-get install --yes git python3-pip
fi
if command -v dnf &> /dev/null; then
agree "Upgrade dnf packages" "N" && sudo dnf upgrade
echo "Install git python3-pip"
sudo dnf install --assumeyes git python3-pip python3-libdnf5
fi
echo "Install ansible"
pip install --user --break-system-packages ansible jmespath
if [ ! -d ~/.config/local ]; then
echo "Clone configuration repository"
git clone https://git.infektor.net/config/local.git ~/.config/local
fi
if agree "Install 1password"; then
pushd ~/.config/local
~/.local/bin/ansible-playbook ~/.config/local/playbooks/1password.yaml
popd
fi
[ -f $0 ] && agree "Remove $0" "N" && rm $0 || exit 0

62
bootstrap-Windows.ps1 Normal file
View File

@@ -0,0 +1,62 @@
$IsElevated = [bool]([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
$Choices = '&Yes', '&No'
if (-Not $IsElevated) {
Write-Host "Not running as Administrator. Performing unprivileged actions."
# Install Scoop
Write-Host "`nInstall Scoop"
$ScoopInstaller = "$env:USERPROFILE/Downloads/ScoopInstaller.ps1"
Invoke-WebRequest -Uri https://get.scoop.sh -OutFile $ScoopInstaller
&$ScoopInstaller -ScoopDir "$env:LocalAppData/Scoop" -ScoopGlobalDir "$env:ProgramData/Scoop"
$Bootstrap = $MyInvocation.MyCommand.Path
$Decision = $Host.UI.PromptForChoice('Relaunch as Administrator', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
Start-Process powershell.exe "-ExecutionPolicy ByPass -NoProfile -File $Bootstrap" -Verb RunAs
}
$Decision = $Host.UI.PromptForChoice("Remove $Bootstrap", "Proceed?", $Choices, 1)
if ($Decision -eq 0) {
Remove-Item $Bootstrap -Force
}
} else {
Write-Host "Running as Administrator. Performing privileged actions."
# Install 1Password
$Decision = $Host.UI.PromptForChoice('Install 1Password', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
$1passwordInstaller = "$env:USERPROFILE/Downloads/1PasswordSetup-latest.exe"
Invoke-WebRequest -Uri "https://downloads.1password.com/win/1PasswordSetup-latest.exe" -OutFile "$1passwordInstaller"
&$1passwordInstaller
Remove-Item $1passwordInstaller
}
# Enable Hyper-V
$Decision = $Host.UI.PromptForChoice('Enable Hyper-V', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
}
# Enable Containters
$Decision = $Host.UI.PromptForChoice('Enable Containers', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All
}
# Install Chocolatey
Write-Host "`nInstall Chocolatey"
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Install SSH Server
$Decision = $Host.UI.PromptForChoice('Install SSH Server', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
choco install --yes "--package-parameters=/SSHServerFeature" openssh
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
}
Write-Host 'Press any key to continue...'
[System.Console]::ReadKey($true)
}

View File

@@ -11,16 +11,13 @@ show_help() {
echo echo
echo "Bootstrap a macOS instance with:" echo "Bootstrap a macOS instance with:"
echo echo
echo "* Xcode command line developer tools" echo "* Install xcode command line developer tools"
echo "* Homebrew - package manager" echo "* Install homebrew"
echo "* python - from Homebrew" echo "* Update packages"
echo "* virtualenv - from pip" echo "* Install python, pip"
echo "* SSH key - from ssh-keygen" echo "* Install ansible"
echo "* GitHub public key - with SSH key" echo "* Clone configuration repository"
echo "* GitLab public key - with SSH key" echo "* Install 1password"
echo "* BitBucket Cloud public key - with SSH key"
echo "* Gogs Cloud public key - with SSH key"
echo "* conduit - configuration manager"
echo echo
echo "If any already exist they will not be reinstalled." echo "If any already exist they will not be reinstalled."
echo echo
@@ -56,14 +53,6 @@ agree() {
esac esac
} }
brew_install() {
brew install $1 > /dev/null
}
pip_install() {
pip install --user $1 > /dev/null
}
export PATH=~/.local/bin:$PATH export PATH=~/.local/bin:$PATH
export PYTHONUSERBASE=~/.local export PYTHONUSERBASE=~/.local
@@ -71,40 +60,23 @@ if ! xcode-select --print-path &> /dev/null; then
agree "Install Xcode command line developer tools" && xcode-select --install agree "Install Xcode command line developer tools" && xcode-select --install
fi fi
missing brew && agree "Intalll Homebrew" && /usr/bin/ruby -e \ missing brew && agree "Install homebrew" && /usr/bin/ruby -e \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
agree "Update Homebrew packages" && brew update > /dev/null agree "Update homebrew packages" && brew update
brew install python
if missing /usr/local/bin/python2 && agree "Install Homebrew python"; then # TODO: Update PATH for pip?
brew_install python
export PATH=/usr/local/opt/python/libexec/bin:$PATH echo "Install ansible"
echo "To use Homebrew python update your PATH:" pip install --user --break-system-packages ansible jmespath
echo 'export PATH=/usr/local/opt/python/libexec/bin:$PATH'
if [ ! -d ~/.config/local ]; then
echo "Clone configuration repository"
git clone https://git.infektor.net/config/local.git ~/.config/local
fi fi
missing virtualenv && agree "Install virtualenv" && pip_install virtualenv agree "Install 1password" && \
~/.local/bin/ansible-playbook ~/.config/local/playbooks/1password.yaml
if [ ! -f ~/.ssh/id_rsa ] && agree "Generate SSH key"; then
read -rp "SSH email: " email
[ ! -d ~/.ssh ] && mkdir -p ~/.ssh
ssh-keygen -t rsa -b 4096 -C "$email" -N "" -f ~/.ssh/id_rsa
fi
if ! missing virtualenv && agree "Set SSH keys on remote Git servers"; then
env=$(mktemp -d)
virtualenv $env &> /dev/null
source $env/bin/activate
pip install git+https://code.infektor.net/config/bootstrap.git > /dev/null
python -c 'import bootstrap; bootstrap.set_ssh_keys()'
deactivate
rm -r $env
fi
! missing pip && missing conduit && agree "Install conduit" && \
pip_install git+ssh://git@github.com/kbenzie/conduit.git
echo "To use installed pip packages update your PATH and PYTHONUSERBASE:"
echo 'export PATH=~/.local/bin:$PATH && export PYTHONUSERBASE=~/.local'
[ -f $0 ] && agree "Remove $0" "N" && rm $0 || exit 0 [ -f $0 ] && agree "Remove $0" "N" && rm $0 || exit 0

View File

@@ -1,185 +0,0 @@
"""Interactively set SSH keys on remote Git servers."""
from __future__ import print_function
from getpass import getpass
from os import environ
from os.path import join
from platform import node, system
from requests import ConnectionError, get, post
from requests.auth import HTTPBasicAuth
from requests.compat import urlparse
try:
input = raw_input
except NameError:
pass
class BootstrapError(Exception):
"""Bootstrap Exception."""
pass
def bootstrap_error(response):
"""Create a BootstrapError from a Response."""
return BootstrapError('%s %s' % (response.status_code, response.reason))
def agree(question, default='Y'):
"""Prompt user to answer a yes/no question."""
valid = {
'y': True,
'Y': True,
'yes': True,
'n': False,
'N': False,
'no': False,
'': {'Y': True,
'N': False}[default]
}
answer = input('%s [%s]? ' % (question, {'Y': 'Y/n', 'N': 'y/N'}[default]))
try:
return valid[answer]
except KeyError:
print('invalid input: %s' % answer)
return agree(question)
def get_url(service, default):
"""Get URL."""
url = input('%s URL%s: ' % (service, ' (%s)' % default if default else ''))
if url == '':
if default:
return default
else:
print('invalid input: %s' % url)
return get_url(service, default)
return url
def get_username_password(service):
"""Get username/password."""
username = input('%s username: ' % service)
password = getpass('%s password: ' % service)
return (username, password)
def get_local_key():
"""Get local SSH key."""
if system() == 'Windows':
home = environ['userprofile']
else:
home = environ['HOME']
with open(join(home, '.ssh', 'id_rsa.pub'), 'r') as key_file:
return key_file.read().rstrip()
def key_exists(keys, local_key):
"""Check if local SSH key is already set."""
for key in keys:
if local_key.startswith(key['key']):
return True
return False
def set_github_ssh_key():
"""Set GitHub SSH key."""
url = urlparse(get_url('GitHub', 'https://github.com'))
keys_url = '%s://api.%s/user/keys' % (url.scheme, url.netloc)
username, password = get_username_password('GitHub')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_gitlab_ssh_key():
"""Set GitLab SSH key."""
api_url = '%s/api/v4' % get_url('GitLab', 'https://gitlab.com')
session_url = '%s/session' % api_url
keys_url = '%s/user/keys' % api_url
username, password = get_username_password('GitLab')
response = post(session_url, {'login': username, 'password': password})
if response.status_code != 201:
raise bootstrap_error(response)
auth = {'Private-Token': response.json()['private_token']}
response = get(keys_url, headers=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, headers=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_bitbucket_cloud_ssh_key():
"""Set BitBucket Cloud SSH key."""
username, password = get_username_password('BitBucket Cloud')
keys_url = 'https://api.bitbucket.org/1.0/users/%s/ssh-keys' % username
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, data={'label': node(),
'key': local_key})
if response.status_code != 200:
raise bootstrap_error(response)
def set_gogs_ssh_key():
"""Set Gogs SSH key."""
keys_url = '%s/api/v1/user/keys' % get_url('Gogs', None)
username, password = get_username_password('Gogs')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_ssh_keys():
"""Interactively set SSH keys on remote Git servers."""
try:
for service in ['GitHub', 'GitLab', 'BitBucket Cloud', 'Gogs']:
question = 'Set %s SSH key' % service
default = 'Y'
while agree(question, default):
try:
{
'GitHub': set_github_ssh_key,
'GitLab': set_gitlab_ssh_key,
'BitBucket Cloud': set_bitbucket_cloud_ssh_key,
'Gogs': set_gogs_ssh_key,
}[service]()
if service == 'BitBucket Cloud':
break
question = 'Set another %s SSH key' % service
default = 'N'
except (BootstrapError, ConnectionError) as error:
print('error: %s' % error.message)
except KeyboardInterrupt:
exit(130)

View File

@@ -1,11 +0,0 @@
"""Setup bootstrap package."""
from setuptools import find_packages, setup
setup(
name='bootstrap',
version='0.1.0',
packages=find_packages(),
install_requires=[
'requests',
])