Motivation
Installation knowledge for this project is currently scattered across each
language's own README (Homebrew/Chocolatey/apt/pyenv/rbenv/SDKMAN
instructions, each written ad hoc). There's no single, organized,
machine-readable source of truth for "what needs to be installed, on which
platform, to run lesson X" - and no automated way to resolve cross-package-
manager dependencies (e.g. Groovy needs a JVM; corretto@17 via Homebrew,
Chocolatey, or apt-based Corretto repo all independently satisfy that).
Proposed design
1. Package manifest, organized by platform then by lesson directory
A YAML manifest, top-level keyed by platform (macos, windows, cygwin,
msys, ubuntu22, ...), nested under a lessons: key to mirror the actual
lessons/<category>/<language> directory structure. Each leaf lists
packages as pkg_manager: pkg_name (or a list of names), with optional
needs/meets/require keys for dependency resolution across package
managers - e.g. Groovy declares needs: java, and a Corretto package
declares meets: java to satisfy that regardless of which manager provided
it. A script: package type covers anything a plain package-manager line
can't handle (SDKMAN bootstrap, pyenv/rbenv setup, Chocolatey/Homebrew
bootstrapping, custom apt-repo setup) - defined once in a shared scripts:
block and referenced by name.
Small illustrative snippet (full manifest at the end of this issue):
macos:
lessons:
gen_scripts:
groovy:
packages:
- brew: groovy
needs: java
perl:
packages:
- brew: perl
meets: perl
ubuntu22:
lessons:
gen_scripts:
python3:
packages:
- pyenv: 3.14.6
cmd: pyenv global 3.10.4
scripts:
macos_homebrew:
type: bash
cmd: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. Install resolution algorithm
lesson_install <dotted.path> walks the manifest tree at that path,
collecting every package declared at and below it.
- Parents are installed before children; for any package declaring
require: [pkg_type: pkg_name, ...], those prerequisites are resolved
and installed first (topological order).
- Uses flattened dot-path addressing to target either a single language
(lesson_install lesson.gen_scripts.perl) or a whole category
(lesson_install shell_scripts installs bash/zsh/dash/tcsh/ksh together).
- A
cmd: step defaults to PowerShell on Windows, bash on POSIX, unless a
step explicitly overrides the type.
lesson_install lesson.gen_scripts.perl # install perl
lesson_install shell_scripts # installs bash, zsh, dash, tcsh, ksh on mac
lesson_install cibox
3. Future extensibility: "yaps" (yet another package)
For cases too complex for a single package-manager line, an embedded
mini-package format modeled on Debian's .deb structure - preinst/
postinst/prerm hook scripts plus a data: tree of files to place.
yaps:
yap_name:
preinst: |
command
data:
etc:
apt:
sources.list.d:
microsoft-prod-list: deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/ubuntu/22.04/prod jammy main
4. system_info() - normalized platform/version identification
Two-step detection: uname -s (or $PSVersionTable on native Windows) as
the first-pass discriminator, then an OS-specific follow-up query for the
actual distro/version, normalized to a consistent platform.version string:
uname -s / detection |
Follow-up query |
Normalized form |
CYGWIN_NT-10.0-26200 |
- |
cygwin_nt.10_0_26200 |
MINGW64_NT-10.0-26200 |
- |
mingw64_nt.10_0_26200 |
Darwin |
sw_vers |
darwin.26_5 |
Linux |
/etc/os-release |
linux_ubuntu.26_04 |
| Windows native |
$PSVersionTable / [System.Environment]::OSVersion / Get-CimInstance Win32_OperatingSystem / Get-ComputerInfo |
windows_nt.10_0_26200 |
5. Environment/platform support metadata
A separate block maps each manifest platform-name to the normalized
system_info() strings it's known to support:
environments:
- platform-name: windows
supports: [windows_nt.10_0_26200]
- platform-name: darwin
supports: [darwin.26_5]
- platform-name: ubuntu22
supports: [linux_ubuntu.26_04]
- platform-name: cygwin
supports: [cygwin_nt.10_0_26200]
- platform-name: msys
supports: [mingw64_nt.10_0_26200]
If the detected environment's normalized string isn't listed under any
platform's supports:, the tool aborts safely with an informative message
rather than guessing at a possibly-incompatible install path.
Open questions
- What actually implements
lesson_install (Ruby, since testbox already
depends on it? A standalone per-platform script?).
- How does this relate to the existing scattered manifests already in the
repo (scriptbox's Brewfile/choco.config, cygwin_pkgs.txt,
msys2_pkgs.txt) - wholesale replacement, or does this generate/subsume
those?
Out of scope for now
Design/discussion only - no implementation until the shape of this is
settled.
Full draft manifest
Click to expand full YAML draft (macos, windows, cygwin, msys, ubuntu22, scripts)
macos:
global:
packages:
- script: macos_homebrew
lessions:
gen_scripts:
awk:
packages:
- brew: gawk
groovy:
packages:
- brew: groovy
needs: java
perl:
packages:
- brew: perl
meets: perl
php:
packages:
- brew: php
python3:
packages:
- brew: python3
ruby:
packages:
- brew: ruby
tcl:
packages:
- brew: tcl-tk
shell_scripts:
packages:
- brew: bc
- brew: getopt
bash:
packages:
- brew: bash
csh:
packages:
- brew: tcsh
ksh:
packages:
- brew: ksh
posix:
packages:
- brew: dash-shell
- needs: perl
zsh:
packages:
- brew: zsh
compiled_lang:
cs:
packages:
- cask: dotnet-sdk
go:
packages:
- brew: go
java:
packages:
- cask: corretto@17
require:
- tap: homebrew/cask-versions
meets: java
- tap: homebrew/cask-versions
rust:
packages:
- brew: rust
win_scripts:
powershell:
packages:
- brew: powershell
cibox:
packages:
- brew: act
- brew: colima
- brew: docker
testbox:
packages:
- brew: powershell
- brew: ruby
windows:
global:
packages:
- choco: gsudo
require:
- script: windows_chocolatey
- script: windows_chocolatey
- choco: make
require:
- script: windows_chocolatey
lessons:
gen_scripts:
awk:
packages:
- choco: gawk
groovy:
packages:
- choco: groovy
needs: java
perl:
packages:
- choco: strawberryperl
meets: perl
php:
packages:
- choco: php
python2:
packages:
- choco: python2
python3:
packages:
- choco: python3
ruby:
packages:
- choco: ruby
tcl:
packages:
- choco: magicsplat-tcl-tk
shell_scripts:
packages:
- choco: cygwin
- choco: cyg-get
- choco: msys2
- choco_cyg: wget
require:
- choco: cyg-get
compiled_lang:
cs:
packages:
- choco: dotnet-sdk
go:
packages:
- choco: go
java:
packages:
- choco: corretto17jdk
meets: java
rust:
packages:
- choco: rust
win_scripts:
powershell:
packages:
- choco: powershell-core
cibox:
packages:
- feature: Microsoft-Windows-Subsystem-Linux
reboot: yes
- feature: VirtualMachinePlatform
reboot: yes
- choco: act-cli
- choco: docker-desktop
require:
- feature: Microsoft-Windows-Subsystem-Linux
- feature: VirtualMachinePlatform
testbox:
packages:
- choco: powershell-core
- choco: ruby
- choco: psake
require:
- choco: powershell-core
cygwin:
global:
gen_scripts:
perl:
packages:
- cyg: perl
meets: perl
php:
packages:
- cyg: php
python3:
packages:
- cyg: python312
ruby:
packages:
- cyg: ruby
tcl:
packages:
- cyg: tcl
shell_scripts:
packages:
- cyg: bc
csh:
packages:
- cyg: tcsh
posix:
packages:
- cyg: perl
compiled_lang:
packages:
- cyg:
- make
- git
cpp:
packages:
- cyg:
- gcc-core
- gcc-g++
- automake
- autoconf
- libtool
- binutils
- pkg-config
- patch
rust:
packages:
- cyg: rust
win_scripts:
testbox:
packages:
- cyg: ruby
- cyg: rubygems
msys:
global:
lessons:
gen_scripts:
awk:
packages:
- pacman: gawk
perl:
packages:
- pacman: perl
meets: perl
- script: cpan_setup
require:
- pacman: perl
- cpan: App::cpanminus
require:
- script: cpan_setup
- cpanm: Switch
require:
- cpan: App::cpanminus
python3:
packages:
- pacman: mingw-w64-ucrt-x86_64-python
ruby:
packages:
- pacman: mingw-w64-ucrt-x86_64-ruby
tcl:
packages:
- pacman: mingw-w64-ucrt-x86_64-tcl
shell_scripts:
packages:
- pacman: bc
csh:
packages:
- pacman: tcsh
posix:
packages:
- pacman: dash
compiled_lang:
packages:
- pacman: base-devel
- pacman: mingw-w64-x86_64-toolchain
go:
packages:
- packman: mingw-w64-ucrt-x86_64-go
rust:
packages:
- packman: mingw-w64-ucrt-x86_64-rustup
win_scripts: {}
testbox:
packages:
- pacman: ruby
ubuntu22:
global:
packages:
- script: ubuntu22_sdkman
- apt: ['curl','git','make','wget']
lessons:
gen_scripts:
packages:
- apt:
- autoconf
- bison
- build-essential
- libbz2-dev
- libdb-dev
- libffi-dev
- libgdbm6
- libgdbm-dev
- liblzma-dev
- libncurses5-dev
- libncursesw5-dev
- libreadline6-dev
- libreadline-dev
- libsqlite3-dev
- libssl-dev
- libxml2-dev
- libxmlsec1-dev
- libyaml-dev
- llvm
- tk-dev
- xz-utils
- zlib1g-dev
- apt: rbenv
script: ubuntu22_post_rbenv
- script: ubuntu22_pyenv
awk:
packages:
- apt: gawk
groovy:
packages:
- sdkman: groovy
needs: java
perl:
packages:
- apt: perl
meets: perl
php:
packages:
- apt: php
python2:
packages:
- pyenv: 2.7.18
cmd: pyenv global 3.10.4 2.7.18
requires:
- pyenv: 3.14.6
python3:
packages:
- pyenv: 3.14.6
cmd: pyenv global 3.10.4
ruby:
packages:
- rbenv: 4.0.6
cmd: rbenv global 4.0.6
tcl:
packages:
- apt: tcl
shell_scripts:
packages:
- brew: bc
- brew: getopt
bash:
packages:
- brew: bash
csh:
packages:
- brew: tcsh
ksh:
packages:
- brew: ksh
posix:
packages:
- brew: dash-shell
- needs: perl
zsh:
packages:
- brew: zsh
compiled_lang:
packages:
apt:
- curl
- build-essential
- make
- gcc
java:
packages:
script: ubuntu22_coretto_17
go:
packages:
script: ubuntu22_go
rust:
packages:
script: ubuntu22_rust
win_scripts:
powershell:
packages:
- script: ubuntu22_powershell
cibox:
packages:
- brew: act
- brew: colima
- brew: docker
testbox:
packages:
- script: ubuntu22_powershell
- rbenv: 4.0.6
scripts:
ubuntu22_powershell:
type: bash
cmd: |
wget -q https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# Add Microsoft packages list
sudo apt-get update
# Install PowerShell 7.x (pwsh)
sudo apt-get install -y powershell
ubuntu22_pyenv:
type: bash
cmd: |
# Install Pyenv
curl https://pyenv.run | bash
# Profile Setup
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'command -v pyenv >/dev/null || export PATH="$PATH:$PYENV_ROOT/bin"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
source ~/.bashrc
# Update List
pyenv update
ubuntu22_post_rbenv:
type: bash
cmd: |
# Profile Setup
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
# Update List
pushd $HOME/.rbenv/plugins/ruby-build && git pull && popd
macos_homebrew:
type: bash
cmd: |
script_url="https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"
/bin/bash -c "$(curl -fsSL "$script_url")"
windows_chocolatey:
type: powershell
cmd: |
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072
$WebClient = New-Object System.Net.WebClient
$ScriptUrl = 'https://community.chocolatey.org/install.ps1'
Invoke-Expression ($WebClient.DownloadString($ScriptUrl))
ubuntu22_rust:
type: bash
cmd: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
echo 'source "$HOME/.cargo/env' >> ~/.bashrc
source ~/.bashrc
ubuntu22_go:
type: bash
cmd: |
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt update && sudo apt install golang-go
echo 'export GOPATH=$(go env GOPATH)' >> ~/.bashrc
echo 'export PATH=$GOPATH/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
cpan_local_setup:
type: bash
cmd: |
PERL_MM_OPT="INSTALL_BASE=$HOME/perl5" cpan local::lib
echo 'eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib=$HOME/perl5)"' >> ~/.bashrc
source ~/.bashrc
ubuntu22_coretto_17:
type: bash
cmd: |
KEYRING="/usr/share/keyrings/corretto-keyring.gpg"
wget -O - https://apt.corretto.aws/corretto.key \
| sudo gpg --dearmor -o $KEYRING
echo "deb [signed-by=$KEYRING] https://apt.corretto.aws stable main" \
| sudo tee /etc/apt/sources.list.d/corretto.list
sudo apt update && sudo apt install -y java-17-amazon-corretto-jdk
echo "export JAVA_HOME=\$(readlink -f /usr/bin/java | sed 's|/bin/java||')" >> ~/.bashrc
source ~/.bashrc
Click to expand full "yaps" concept draft
yaps:
yap_name:
control:
preinst: |
command
command
postinst:
command
command
prerm: |
command
command
data:
etc:
apt:
sources.list.d:
microsoft-prod-list: deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/ubuntu/22.04/prod jammy main
Motivation
Installation knowledge for this project is currently scattered across each
language's own README (Homebrew/Chocolatey/apt/pyenv/rbenv/SDKMAN
instructions, each written ad hoc). There's no single, organized,
machine-readable source of truth for "what needs to be installed, on which
platform, to run lesson X" - and no automated way to resolve cross-package-
manager dependencies (e.g. Groovy needs a JVM;
corretto@17via Homebrew,Chocolatey, or apt-based Corretto repo all independently satisfy that).
Proposed design
1. Package manifest, organized by platform then by lesson directory
A YAML manifest, top-level keyed by platform (
macos,windows,cygwin,msys,ubuntu22, ...), nested under alessons:key to mirror the actuallessons/<category>/<language>directory structure. Each leaf listspackages as
pkg_manager: pkg_name(or a list of names), with optionalneeds/meets/requirekeys for dependency resolution across packagemanagers - e.g. Groovy declares
needs: java, and a Corretto packagedeclares
meets: javato satisfy that regardless of which manager providedit. A
script:package type covers anything a plain package-manager linecan't handle (SDKMAN bootstrap, pyenv/rbenv setup, Chocolatey/Homebrew
bootstrapping, custom apt-repo setup) - defined once in a shared
scripts:block and referenced by name.
Small illustrative snippet (full manifest at the end of this issue):
2. Install resolution algorithm
lesson_install <dotted.path>walks the manifest tree at that path,collecting every package declared at and below it.
require: [pkg_type: pkg_name, ...], those prerequisites are resolvedand installed first (topological order).
(
lesson_install lesson.gen_scripts.perl) or a whole category(
lesson_install shell_scriptsinstalls bash/zsh/dash/tcsh/ksh together).cmd:step defaults to PowerShell on Windows, bash on POSIX, unless astep explicitly overrides the type.
3. Future extensibility: "yaps" (yet another package)
For cases too complex for a single package-manager line, an embedded
mini-package format modeled on Debian's
.debstructure -preinst/postinst/prermhook scripts plus adata:tree of files to place.4.
system_info()- normalized platform/version identificationTwo-step detection:
uname -s(or$PSVersionTableon native Windows) asthe first-pass discriminator, then an OS-specific follow-up query for the
actual distro/version, normalized to a consistent
platform.versionstring:uname -s/ detectionCYGWIN_NT-10.0-26200cygwin_nt.10_0_26200MINGW64_NT-10.0-26200mingw64_nt.10_0_26200Darwinsw_versdarwin.26_5Linux/etc/os-releaselinux_ubuntu.26_04$PSVersionTable/[System.Environment]::OSVersion/Get-CimInstance Win32_OperatingSystem/Get-ComputerInfowindows_nt.10_0_262005. Environment/platform support metadata
A separate block maps each manifest
platform-nameto the normalizedsystem_info()strings it's known to support:If the detected environment's normalized string isn't listed under any
platform's
supports:, the tool aborts safely with an informative messagerather than guessing at a possibly-incompatible install path.
Open questions
lesson_install(Ruby, sincetestboxalreadydepends on it? A standalone per-platform script?).
repo (
scriptbox'sBrewfile/choco.config,cygwin_pkgs.txt,msys2_pkgs.txt) - wholesale replacement, or does this generate/subsumethose?
Out of scope for now
Design/discussion only - no implementation until the shape of this is
settled.
Full draft manifest
Click to expand full YAML draft (macos, windows, cygwin, msys, ubuntu22, scripts)
Click to expand full "yaps" concept draft