Generate Bash installation scripts from a declarative YAML config for multiple Linux distros
Project description
distroscript
Generate installation scripts for various Linux distributions based on a declarative YAML configuration file.
distroscript [-h] --os OS [--out OUT] config_path
Generate installation scripts from YAML config.
positional arguments:
config_path Path to the YAML configuration file.
options:
-h, --help show this help message and exit
--os OS Target operating system (e.g., 'ubuntu', 'fedora').
--out OUT Output shell script file path (optional, defaults to stdout).
[!NOTE] For now, only
fedorais fully supported, but I plan to add support forubuntu,poposandmintin the future.
Installation
pip install distroscript
Development
Install dev dependencies with:
pip install -e ".[dev]"
- MacOS
brew install python3 pyyaml jsonschema
Schema Validation
The configuration files are validated against a JSON Schema specification (src/schema.json)
that defines the structure and constraints for all supported package types and their properties.
The validation happens automatically after reading the YAML file and before processing begins. If validation fails, the script will exit with a detailed error message showing:
- The path to the invalid field
- A description of what's wrong
This helps catch configuration errors early, such as:
- Missing required fields (e.g.,
urlfor tar packages) - Invalid package type names
- Incorrect property types (e.g., string instead of array)
- Unsupported properties for a given package type
Basic Usage
The YAML configuration file should defines the packages to be installed as a key, and it's value is a list of methods to install the package in different packaging systems.
Example:
git:
- type: apt
packages:
- git
- type: dnf
packages:
- git
Then, when generating the installation script, the output will depend on the
--os argument provided.
For example, for --os fedora, the generated script will use dnf to install
git:
> distroscript.py --os fedora config.yaml
#!/bin/bash
sudo dnf install -y git
For --os ubuntu, it will use apt:
> distroscript.py --os ubuntu config.yaml
#!/bin/bash
sudo apt-get install -y git
Features by Example
Below are simple examples for each major feature.
1. Install a package with different methods per OS
- By declaring multiple methods under the same package:
git:
- type: apt
packages: [git]
- type: dnf
packages: [git]
[!TIP] When
packagesis not included, the package name is assumed to be the same as the key:git: - type: apt - type: dnf
[!TIP] When declaring a simple string, it is expanded as the
typeproperty:git: - apt - dnf
2. Add dependencies between packages
- By declaring another package:
pip:
- type: apt
packages:
- python3-pip
depends_on:
- zsh
zsh: [apt]
- By inlining the dependency as another Package:
pip:
- type: apt
packages: [python3-pip]
depends_on:
- type: apt
packages: [zsh]
[!WARNING] If dependency is not declared, distroscript will output a TODO comment, so the user can manually add it to the configuration file later if needed.
3. Run commands before or after install
- All packages could define
pre_installandpost_installsteps as a list of Commands to be executed before or after the installation.
zsh:
- type: apt
pre_install: [...]
post_install: [...]
- Type
shellinlines the content into the destination script:
zsh:
- type: apt
post_install:
- type: shell
content: |
chsh -s "$(which zsh)" "$USER"
[!TIP] It's the default in case we provide a simple string:
zsh: - type: apt post_install: | chsh -s "$(which zsh)" "$USER"
- Type
teewrites content to a file, useful to add desktop entries or config files:
kazam:
- type: pip
post_install:
- type: tee
destination: "$HOME/.local/share/applications/kazam.desktop"
content: |
[Desktop Entry]
Name=Kazam
Exec=kazam
...
[!TIP] By default, it overwrites the file, but if
append: trueis set, it appends to the file so existing content is preserved:zsh: - type: apt post_install: - type: tee destination: "$HOME/.zshrc" append: true content: | # pip export PATH="$HOME/.local/bin:$PATH"
[!TIP] You can also specify
sudo: trueto write the file to a destination that requires elevated permissions:dnf-automatic: - type: dnf post_install: - type: tee destination: /etc/dnf/automatic.conf sudo: true content: | [commands] apply_updates=True
[!TIP] You can specify
mkdir: trueto create the destination directory before writing the file:kazam: - type: pip post_install: - type: tee destination: "$HOME/.local/share/applications/kazam.desktop" mkdir: true content: | [Desktop Entry] Name=Kazam Exec=kazam ...
4. Install from Flatpak, Snap, Pip, Tar, Zip, GitHub, File, Shell, or AppImage
For these types, if specified --os does not include the required command, it
will be included as a dependency, as if we declared it in depends_on as
a single string.
This allows the user to provide an installation method for the package manager itself if needed.
Flatpak:
kdenlive:
- type: flatpak
packages: [org.kde.kdenlive]
# depends_on: [flatpak] # Added implicitly if running on an OS without flatpak pre-installed
Snap:
code:
- type: snapd
packages: [code]
classic: true # Optional: adds the --classic flag
# depends_on: [snapd] # Added implicitly if running on an OS without snapd pre-installed
Pip:
yt-dlp:
- type: pip
packages: ["yt-dlp[default]"]
# depends_on: [pip] # Added implicitly if running on an OS without pip pre-installed
Tar:
go:
- type: tar
url: "https://go.dev/dl/go1.20.5.linux-amd64.tar.gz"
destination: /usr/local
sudo: true # Optional: use sudo to extract to destination
Zip:
jetbrains-nf:
- type: zip
url: "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/JetBrainsMono.zip"
destination: /usr/share/fonts/JetBrainsMonoNerdFont
sudo: true # Optional: use sudo to extract to destination
GitHub:
cspec:
- type: github
repository: mumuki/cspec
install: |
make
sudo make install
File:
doctest:
- type: file
url: https://raw.githubusercontent.com/doctest/doctest/v2.4.12/doctest/doctest.h
destination: /usr/local/include/doctest/doctest.h
sudo: true # Optional: use sudo to write to destination
silent: true # Optional: suppress output after download
executable: false # Optional: make the file executable after download
Shell:
- By providing an url:
rust:
- type: shell
url: https://sh.rustup.rs
shell: bash # Optional: specify the shell to use (default: bash).
# Will be added as a dependency if not preinstalled in the target OS.
- By providing an inline script:
oh-my-zsh:
- type: shell
script: |
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
shell: zsh # Optional: specify the shell to use (default: bash).
# Will be added as a dependency if not preinstalled in the target OS.
AppImage:
obsidian:
- type: appimage
url: https://github.com/obsidianmd/obsidian-releases/releases/download/v1.8.10/Obsidian-1.8.10.AppImage
name: Obsidian
icon_name: obsidian
mime_types:
- x-scheme-handler/obsidian
- type: text/markdown
default: true
AppImages are downloaded to $HOME/.local/bin/, a desktop entry is created in
$HOME/.local/share/applications/, and icons are extracted automatically when icon_name is specified.
When mime_types is provided, the desktop entry advertises those MIME types. If default is not set, only scheme
handler strings register the AppImage as their default handler, other MIME types will not be set as default unless
default: true is explicitly specified.
[!NOTE] AppImage packages require the
urlfield. Thename,icon_name,categories, andmime_typesfields are optional. Thenamefield defaults to the package name,icon_nameenables desktop icon extraction when provided, andcategoriesdefaults to "Application".
5. Use custom flags for package managers
gh:
- type: dnf
flags:
- "--repo gh-cli"
pre_install: |
sudo dnf config-manager addrepo --from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo
gleam:
- type: dnf
copr: frostyx/gleam
pre_install: |
sudo dnf copr enable frostyx/gleam -y
[!TIP] For
dnf, you can also specifyrepo,repofileand/orcoprproperties:gh: - type: dnf repo: gh-cli repofile: https://cli.github.com/packages/rpm/gh-cli.repo gleam: - type: dnf copr: frostyx/gleam
6. Add desktop entries or config files
kazam:
- type: pip
post_install:
- type: tee
destination: "$HOME/.local/share/applications/kazam.desktop"
content: |
[Desktop Entry]
Name=Kazam
Exec=kazam
...
7. Merging multiple packages to a single install command
If multiple packages use the same installation method and are not codependant, they will be merged into a single command to optimize the installation process.
jq: [apt]
htop: [apt]
zsh: [apt]
pip:
- type: apt
packages: [python3-pip]
depends_on: [zsh]
Output:
#!/bin/bash
sudo apt-get install -y jq htop zsh
sudo apt-get install -y python3-pip
8. Combine everything for complex setups
docker:
- type: dnf
packages:
- docker
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
repofile: https://download.docker.com/linux/fedora/docker-ce.repo
depends_on:
- type: dnf
packages: [dnf5-plugins]
post_install: |
sudo systemctl enable --now docker
sudo groupadd -f docker
sudo usermod -aG docker "$USER"
Output:
#!/bin/bash
sudo dnf install -y dnf5-plugins docker docker-ce docker-ce-cli \
containerd.io docker-buildx-plugin docker-compose-plugin
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
sudo systemctl enable --now docker
sudo groupadd -f docker
sudo usermod -aG docker "$USER"
License
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.
Author
Agustin Ranieri - @RaniAgus
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file distroscript-0.2.1.tar.gz.
File metadata
- Download URL: distroscript-0.2.1.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd3041978f05159f85d87918c009ec063773dd3a42530a76e2b1992792dc8541
|
|
| MD5 |
d4e95a2a2cf218d78573c1585e17398c
|
|
| BLAKE2b-256 |
d52a20cd70934eab7a32ace4494987c129bda4d5bb3d154ac1c80697c30daf14
|
Provenance
The following attestation bundles were made for distroscript-0.2.1.tar.gz:
Publisher:
publish.yml on RaniAgus/distroscript
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
distroscript-0.2.1.tar.gz -
Subject digest:
bd3041978f05159f85d87918c009ec063773dd3a42530a76e2b1992792dc8541 - Sigstore transparency entry: 2054133317
- Sigstore integration time:
-
Permalink:
RaniAgus/distroscript@38b1f537996c36abc1f681e18667d0d1c7d95efb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/RaniAgus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@38b1f537996c36abc1f681e18667d0d1c7d95efb -
Trigger Event:
push
-
Statement type:
File details
Details for the file distroscript-0.2.1-py3-none-any.whl.
File metadata
- Download URL: distroscript-0.2.1-py3-none-any.whl
- Upload date:
- Size: 14.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6bfb3de80c117e5aef7e0060b994703b5e330f004fbf6ec879fbe3f03f797d27
|
|
| MD5 |
71cde08834541733beab8d5a6c8b4856
|
|
| BLAKE2b-256 |
77641739387877abb8fd6f05b3b92284c6e7f56ea751e8fd171222902666efae
|
Provenance
The following attestation bundles were made for distroscript-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on RaniAgus/distroscript
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
distroscript-0.2.1-py3-none-any.whl -
Subject digest:
6bfb3de80c117e5aef7e0060b994703b5e330f004fbf6ec879fbe3f03f797d27 - Sigstore transparency entry: 2054133612
- Sigstore integration time:
-
Permalink:
RaniAgus/distroscript@38b1f537996c36abc1f681e18667d0d1c7d95efb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/RaniAgus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@38b1f537996c36abc1f681e18667d0d1c7d95efb -
Trigger Event:
push
-
Statement type: