feat: dnf module (#377)
Co-authored-by: xyny <60004820+xynydev@users.noreply.github.com> Co-authored-by: Gerald Pinder <gmpinder@gmail.com> Co-authored-by: certifiedfoolio <156134535+cherry-os@users.noreply.github.com> Co-authored-by: xyny <git@xyny.anonaddy.me> Co-authored-by: somebody once told me <156134535+certifiedfoolio@users.noreply.github.com> Co-authored-by: franute <franute@gmail.com>
This commit is contained in:
parent
d12d657371
commit
fef0f17870
13 changed files with 1712 additions and 4 deletions
|
|
@ -7,7 +7,7 @@ let images = ls modules | each { |moduleDir|
|
|||
cd $moduleDir.name
|
||||
|
||||
# module is unversioned
|
||||
if ($"($moduleDir.name | path basename).sh" | path exists) {
|
||||
if (glob $"($moduleDir.name | path basename).{sh,nu}" | any { path exists }) {
|
||||
|
||||
print $"(ansi cyan)Found(ansi reset) (ansi cyan_bold)unversioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename)"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ mkdir ./modules-latest
|
|||
ls modules | each { |moduleDir|
|
||||
|
||||
# module is unversioned
|
||||
if ($"($moduleDir.name)/($moduleDir.name | path basename).sh" | path exists) {
|
||||
if (glob $"($moduleDir.name)/($moduleDir.name | path basename).{sh,nu}" | any { path exists }) {
|
||||
|
||||
print $"(ansi cyan)Found(ansi reset) (ansi cyan_bold)unversioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename)"
|
||||
|
||||
|
|
@ -56,4 +56,4 @@ let digest = (
|
|||
print $"(ansi cyan)Signing image:(ansi reset) ($env.REGISTRY)/modules@($digest)"
|
||||
cosign sign -y --key env://COSIGN_PRIVATE_KEY $"($env.REGISTRY)/modules@($digest)"
|
||||
|
||||
print $"(ansi green_bold)DONE!(ansi reset)"
|
||||
print $"(ansi green_bold)DONE!(ansi reset)"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/gschema-overrides/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/justfiles/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/rpm-ostree/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/dnf/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/kargs/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/initramfs/module.yml",
|
||||
"https://raw.githubusercontent.com/blue-build/modules/main/modules/script/module.yml",
|
||||
|
|
|
|||
265
modules/dnf/README.md
Normal file
265
modules/dnf/README.md
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
# **`dnf` Module**
|
||||
|
||||
The `dnf` module offers pseudo-declarative package and repository management using [`dnf5`](https://github.com/rpm-software-management/dnf).
|
||||
|
||||
## Features
|
||||
|
||||
This module is capable of:
|
||||
|
||||
- Repository Management
|
||||
- Enabling/disabling COPR repos
|
||||
- Adding repo files via url or local files
|
||||
- Removing repos by specifying the repo name
|
||||
- Automatically cleaning up any repos added in the module
|
||||
- Adding keys for repos via url or local files
|
||||
- Adding non-free repos like `rpmfusion` and `negativo17`
|
||||
- Package Management
|
||||
- Installing packages from RPM urls, local RPM files, or package repositories
|
||||
- Installing packages from a specific repository
|
||||
- Removing packages
|
||||
- Replacing installed packages with versions from another repository
|
||||
- Optfix
|
||||
- Setup symlinks to `/opt/` to allow certain packages to install
|
||||
|
||||
## Repository Management
|
||||
|
||||
### Add Repository Files
|
||||
|
||||
- Add repos from
|
||||
- any `https://` or `http://` URL
|
||||
- any `.repo` files located in `./files/dnf/` of your image repo
|
||||
- If the OS version is included in the file name or URL, you can substitute it with the `%OS_VERSION%` magic string
|
||||
- The version is gathered from the `VERSION_ID` field of `/usr/lib/os-release`
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
files:
|
||||
- https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo
|
||||
- custom-file.repo # file path for /files/dnf/custom-file.repo
|
||||
```
|
||||
|
||||
### Add COPR Repositories
|
||||
|
||||
- [COPR](https://copr.fedorainfracloud.org/) contains software repositories maintained by fellow Fedora users
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
copr:
|
||||
- atim/starship
|
||||
- trixieua/mutter-patched
|
||||
```
|
||||
|
||||
### Disable/Enable Repositories
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
files:
|
||||
add:
|
||||
- repo1
|
||||
- repo2
|
||||
remove:
|
||||
- repo3
|
||||
copr:
|
||||
enable:
|
||||
- ryanabx/cosmic-epoch
|
||||
disable:
|
||||
- kylegospo/oversteer
|
||||
```
|
||||
|
||||
### Add Repository Keys
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
keys:
|
||||
- https://example.com/repo-1.asc
|
||||
- key2.asc
|
||||
```
|
||||
|
||||
### Add Non-free Repositories
|
||||
|
||||
This allows you to add a commonly used non-free repository.
|
||||
You can choose between [negativo17](https://negativo17.org/) and [rpmfusion](https://rpmfusion.org/).
|
||||
Your choice will also disable the opposite repository if it was already enabled.
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
nonfree: negativo17
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
There is currently only one option that can be specified in the repository management section.
|
||||
|
||||
- `cleanup` automatically cleans up repositories added in this section
|
||||
- Disabled by default
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
repos:
|
||||
cleanup: true
|
||||
```
|
||||
|
||||
## Package Management
|
||||
|
||||
### Installing
|
||||
|
||||
#### Packages from Any Repository
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
install:
|
||||
packages:
|
||||
- package-1
|
||||
- package-2
|
||||
```
|
||||
|
||||
#### Packages from URL or File
|
||||
|
||||
- If the OS version is included in the file name or URL, you can substitute it with the `%OS_VERSION%` magic string
|
||||
- The version is gathered from the `VERSION_ID` field of `/usr/lib/os-release`
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
install:
|
||||
packages:
|
||||
- https://example.com/package-%OS_VERSION%.rpm
|
||||
- custom-file.rpm # install files/dnf/custom-file.rpm from the image repository
|
||||
```
|
||||
|
||||
#### Packages from Specific Repositories
|
||||
|
||||
- Set `repo` to the name of the RPM repository, not the name or URL of the repo file
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
install:
|
||||
packages:
|
||||
- repo: copr:copr.fedorainfracloud.org:custom-user:custom-repo
|
||||
packages:
|
||||
- package-1
|
||||
```
|
||||
|
||||
#### Package Groups
|
||||
|
||||
- See list of all package groups by running `dnf5 group list --hidden` on a live system
|
||||
- Set the option `with-optional` to `true` to enable installation of optional packages in package groups
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
group-install:
|
||||
with-optional: true
|
||||
packages:
|
||||
- de-package-1
|
||||
- wm-package-2
|
||||
```
|
||||
|
||||
#### Replace Packages
|
||||
- You can specify one or more packages that will be swapped from another repo
|
||||
- This process uses `distro-sync` to perform this operation
|
||||
- All packages not specifying `old:` and `new:` will be swapped in a single transaction
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
replace:
|
||||
- from-repo: copr:copr.fedorainfracloud.org:custom-user:custom-repo
|
||||
packages:
|
||||
- package-1
|
||||
```
|
||||
|
||||
- If a package has a different name in another repo, you can use the `old:` and `new:` properties
|
||||
- This process uses `swap` to perform this operation for each set
|
||||
- This process is ran before `distro-sync`
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
replace:
|
||||
- from-repo: repo-1
|
||||
packages:
|
||||
- old: old-package-2
|
||||
new: new-package-2
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
The following options can specified in the package installation, group installation, and package replacement sections.
|
||||
|
||||
- `install-weak-deps` enables installation of the weak dependencies of RPMs
|
||||
- Enabled by default
|
||||
- Corresponds to the [`--setopt=install_weak_deps=True` / `--setopt=install_weak_deps=False`](https://dnf5.readthedocs.io/en/latest/dnf5.conf.5.html#install-weak-deps-options-label) flag
|
||||
- `skip-unavailable` enables skipping packages unavailable in repositories without erroring out
|
||||
- Disabled by default
|
||||
- Corresponds to the [`--skip-unavailable`](https://dnf5.readthedocs.io/en/latest/commands/install.8.html#options) flag
|
||||
- `skip-broken` enables skipping broken packages without erroring out
|
||||
- Disabled by default
|
||||
- Corresponds to the [`--skip-broken`](https://dnf5.readthedocs.io/en/latest/commands/install.8.html#options) flag
|
||||
- `allow-erasing` allows removing packages in case of dependency problems during package installation
|
||||
- Disabled by default
|
||||
- Corresponds to the [`--allowerasing`](https://dnf5.readthedocs.io/en/latest/commands/install.8.html#options) flag
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
install:
|
||||
skip-unavailable: true
|
||||
packages:
|
||||
...
|
||||
group-install:
|
||||
skip-broken: true
|
||||
packages:
|
||||
...
|
||||
replace:
|
||||
- from-repo: repo-1
|
||||
allow-erasing: true
|
||||
packages:
|
||||
...
|
||||
```
|
||||
|
||||
### Removing
|
||||
|
||||
#### Packages
|
||||
|
||||
- You can set the `auto-remove` option to `false` to only remove the specific package and leave unused dependencies
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
remove:
|
||||
auto-remove: false
|
||||
packages:
|
||||
- package-1
|
||||
- package-2
|
||||
```
|
||||
|
||||
#### Package Groups
|
||||
```yaml
|
||||
type: dnf
|
||||
group-remove:
|
||||
packages:
|
||||
- de-package-2
|
||||
```
|
||||
|
||||
## Optfix
|
||||
|
||||
- Optfix is a script used to work around problems with certain packages that install into `/opt/`
|
||||
- These issues are caused by Fedora Atomic storing `/opt/` at the location `/var/opt/` by default, while `/var/` is only writeable on a live system
|
||||
- The script works around these issues by moving the folder to `/usr/lib/opt/` and creating the proper symlinks at runtime
|
||||
- Specify a list of folders inside `/opt/`
|
||||
|
||||
```yaml
|
||||
type: dnf
|
||||
optfix:
|
||||
- brave.com
|
||||
- foldername
|
||||
```
|
||||
|
||||
## Known issues
|
||||
|
||||
Replacing the kernel with the `dnf` module is not done cleanly at the moment & some remaints of old kernel will be present.
|
||||
Please use the `rpm-ostree` module for this purpose until this `dnf` behavior is fixed.
|
||||
|
||||
## Note
|
||||
|
||||
This documentation page uses the installation of the Brave Browser as an example of a package that required a custom repository, with a custom key, and an optfix configuration to install properly. This is not an official endorsement of the Brave Browser by the BlueBuild project.
|
||||
11
modules/dnf/bluebuild-optfix.service
Normal file
11
modules/dnf/bluebuild-optfix.service
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Create symbolic links for directories in /usr/lib/opt/ to /var/opt/
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/bluebuild/optfix.sh
|
||||
RemainAfterExit=no
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
84
modules/dnf/dnf-repoinfo
Executable file
84
modules/dnf/dnf-repoinfo
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
#!/bin/bash
|
||||
# convert the output of dnf repoinfo into json
|
||||
|
||||
repo_id="$1"
|
||||
repo_info=$(dnf repoinfo -q "$repo_id")
|
||||
|
||||
echo "["
|
||||
echo " {"
|
||||
|
||||
repo_id_val=$(echo "$repo_info" | grep -oP "^Repo-id *: *\K.*")
|
||||
if [ -n "$repo_id_val" ]; then
|
||||
echo " \"id\":\"$repo_id_val\","
|
||||
fi
|
||||
|
||||
repo_name=$(echo "$repo_info" | grep -oP "^Repo-name *: *\K.*")
|
||||
if [ -n "$repo_name" ]; then
|
||||
echo " \"name\":\"$repo_name\","
|
||||
fi
|
||||
|
||||
repo_status=$(echo "$repo_info" | grep -oP "^Repo-status *: *\K.*")
|
||||
if [ -n "$repo_status" ]; then
|
||||
if [[ "$repo_status" == "enabled" ]]; then
|
||||
echo " \"is_enabled\":true,"
|
||||
else
|
||||
echo " \"is_enabled\":false,"
|
||||
fi
|
||||
fi
|
||||
|
||||
repo_revision=$(echo "$repo_info" | grep -oP "^Repo-revision *: *\K.*")
|
||||
if [ -n "$repo_revision" ]; then
|
||||
echo " \"revision\":\"$repo_revision\","
|
||||
fi
|
||||
|
||||
repo_updated=$(echo "$repo_info" | grep -oP "^Repo-updated *: *\K.*")
|
||||
if [ -n "$repo_updated" ]; then
|
||||
echo " \"updated\":\"$repo_updated\","
|
||||
fi
|
||||
|
||||
repo_available_pkgs=$(echo "$repo_info" | grep -oP "^Repo-available-pkgs *: *\K.*")
|
||||
if [ -n "$repo_available_pkgs" ]; then
|
||||
echo " \"available-pkgs\":$repo_available_pkgs,"
|
||||
fi
|
||||
|
||||
repo_pkgs=$(echo "$repo_info" | grep -oP "^Repo-pkgs *: *\K.*")
|
||||
if [ -n "$repo_pkgs" ]; then
|
||||
echo " \"pkgs\":$repo_pkgs,"
|
||||
fi
|
||||
|
||||
repo_size=$(echo "$repo_info" | grep -oP "^Repo-size *: *\K.*")
|
||||
if [ -n "$repo_size" ]; then
|
||||
echo " \"size\":\"$repo_size\","
|
||||
fi
|
||||
|
||||
repo_metalink=$(echo "$repo_info" | grep -oP "^Repo-metalink *: *\K.*")
|
||||
if [ -n "$repo_metalink" ]; then
|
||||
echo " \"metalink\":\"$repo_metalink\","
|
||||
fi
|
||||
|
||||
updated=$(echo "$repo_info" | grep -oP "^Updated *: *\K.*")
|
||||
if [ -n "$updated" ]; then
|
||||
echo " \"updated\":\"$updated\","
|
||||
fi
|
||||
|
||||
repo_baseurl=$(echo "$repo_info" | grep -oP "^Repo-baseurl *: *\K.*")
|
||||
if [ -n "$repo_baseurl" ]; then
|
||||
echo " \"baseurl\":\"$repo_baseurl\","
|
||||
fi
|
||||
|
||||
repo_expire=$(echo "$repo_info" | grep -oP "^Repo-expire *: *\K.*")
|
||||
if [ -n "$repo_expire" ]; then
|
||||
echo " \"expire\":\"$repo_expire\","
|
||||
fi
|
||||
|
||||
repo_filename=$(echo "$repo_info" | grep -oP "^Repo-filename *: *\K.*")
|
||||
if [ -n "$repo_filename" ]; then
|
||||
echo " \"repo_file_path\":\"$repo_filename\""
|
||||
fi
|
||||
|
||||
if [[ "$(tail -c 2 <<< "$(echo "$repo_info")" | head -c 1)" == "," ]]; then
|
||||
sed -i '$ s/,$//'
|
||||
fi
|
||||
|
||||
echo " }"
|
||||
echo "]"
|
||||
29
modules/dnf/dnf-repolist
Executable file
29
modules/dnf/dnf-repolist
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
# convert the output of dnf repolist into json
|
||||
|
||||
output=$(dnf repolist -q --all 2>/dev/null)
|
||||
lines=$(echo "$output" | tail -n +3)
|
||||
|
||||
echo "["
|
||||
|
||||
echo "$lines" | while read -r line; do
|
||||
repo_id=$(echo "$line" | awk '{print $1}')
|
||||
status=$(echo "$line" | awk '{print $NF}')
|
||||
repo_name=$(echo "$line" | awk '{$1=""; $NF=""; print $0}' | sed -e 's/^ *//g' -e 's/ *$//g')
|
||||
|
||||
if [ "$status" = "enabled" ]; then
|
||||
status=true
|
||||
else
|
||||
status=false
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"id":"$repo_id",
|
||||
"name":"$repo_name",
|
||||
"is_enabled":$status
|
||||
},
|
||||
EOF
|
||||
done | sed '$s/},/}/'
|
||||
|
||||
echo "]"
|
||||
690
modules/dnf/dnf.nu
Normal file
690
modules/dnf/dnf.nu
Normal file
|
|
@ -0,0 +1,690 @@
|
|||
#!/usr/bin/env nu
|
||||
|
||||
use dnf_interface.nu *
|
||||
|
||||
const NEGATIVO = 'negativo17'
|
||||
const NEGATIVO_URL = 'https://negativo17.org/repos/fedora-negativo17.repo'
|
||||
const RPMFUSION = 'rpmfusion'
|
||||
|
||||
# Handle adding/removing repo files and COPR repos.
|
||||
#
|
||||
# This command returns an object containing the repos
|
||||
# that were added to allow for cleaning up afterwards.
|
||||
def repos [$repos: record]: nothing -> record {
|
||||
let repos = $repos
|
||||
| default [] keys
|
||||
|
||||
let cleanup_repos = match $repos.files? {
|
||||
# Add repos if it's a list
|
||||
[..$files] => {
|
||||
add_repos ($files | default [])
|
||||
}
|
||||
# Add and remove repos
|
||||
{
|
||||
add: [..$add]
|
||||
remove: [..$remove]
|
||||
} => {
|
||||
let repos = add_repos ($add | default [])
|
||||
remove_repos ($remove | default [])
|
||||
$repos
|
||||
}
|
||||
# Add repos
|
||||
{ add: [..$add] } => {
|
||||
add_repos ($add | default [])
|
||||
}
|
||||
# Remove repos
|
||||
{ remove: [..$remove] } => {
|
||||
remove_repos ($remove | default [])
|
||||
[]
|
||||
}
|
||||
_ => []
|
||||
}
|
||||
|
||||
let cleanup_coprs = match $repos.copr? {
|
||||
# Enable repos if it's a list
|
||||
[..$coprs] => {
|
||||
add_coprs ($coprs | default [])
|
||||
}
|
||||
# Enable and disable repos
|
||||
{
|
||||
enable: [..$enable]
|
||||
disable: [..$disable]
|
||||
} => {
|
||||
let coprs = add_coprs ($enable | default [])
|
||||
disable_coprs ($disable | default [])
|
||||
$coprs
|
||||
}
|
||||
# Enable repos
|
||||
{ enable: [..$enable] } => {
|
||||
add_coprs ($enable | default [])
|
||||
}
|
||||
# Disable repos
|
||||
{ disable: [..$disable] } => {
|
||||
disable_coprs ($disable | default [])
|
||||
[]
|
||||
}
|
||||
_ => []
|
||||
}
|
||||
|
||||
nonfree_repos $repos.nonfree?
|
||||
add_keys $repos.keys
|
||||
|
||||
{
|
||||
copr: $cleanup_coprs
|
||||
files: $cleanup_repos
|
||||
}
|
||||
}
|
||||
|
||||
# Setup nonfree repos for rpmfusion or negativo17-multimedia.
|
||||
def nonfree_repos [repo_type?: string]: nothing -> list<string> {
|
||||
match $repo_type {
|
||||
$repo if $repo == $RPMFUSION => {
|
||||
disable_negativo
|
||||
enable_rpmfusion
|
||||
}
|
||||
$repo if $repo == $NEGATIVO => {
|
||||
disable_rpmfusion
|
||||
enable_negativo
|
||||
}
|
||||
null => [],
|
||||
_ => {
|
||||
error make {
|
||||
msg: $"The only valid values are '($NEGATIVO)' and '($RPMFUSION)'"
|
||||
label: {
|
||||
text: 'Passed in value'
|
||||
span: (metadata $repo_type).span
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Enable rpmfusion repos
|
||||
#
|
||||
# See https://rpmfusion.org/Configuration
|
||||
def enable_rpmfusion []: nothing -> nothing {
|
||||
const CISCO_REPO = 'fedora-cisco-openh264'
|
||||
|
||||
print $'(ansi green)Enabling rpmfusion repos(ansi reset)'
|
||||
|
||||
mut repos = []
|
||||
|
||||
if (^rpm -q rpmfusion-free-release | complete).exit_code != 0 {
|
||||
$repos = $repos | append $'https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-($env.OS_VERSION).noarch.rpm'
|
||||
}
|
||||
|
||||
if (^rpm -q rpmfusion-nonfree-release | complete).exit_code != 0 {
|
||||
$repos = $repos | append $'https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-($env.OS_VERSION).noarch.rpm'
|
||||
}
|
||||
|
||||
install_pkgs { packages: $repos }
|
||||
|
||||
print $"(ansi green)Enabling '(ansi cyan)($CISCO_REPO)(ansi green)' repo for RPMFusion compatibility(ansi reset)"
|
||||
dnf config-manager setopt [$'($CISCO_REPO).enabled=1']
|
||||
}
|
||||
|
||||
# Disable rpmfusion repos
|
||||
def disable_rpmfusion []: nothing -> nothing {
|
||||
print $'(ansi green)Removing rpmfusion repos(ansi reset)'
|
||||
|
||||
mut repos = []
|
||||
|
||||
if (^rpm -q rpmfusion-free-release | complete).exit_code == 0 {
|
||||
$repos = $repos | append 'rpmfusion-free-release'
|
||||
}
|
||||
|
||||
if (^rpm -q rpmfusion-nonfree-release | complete).exit_code == 0 {
|
||||
$repos = $repos | append 'rpmfusion-nonfree-release'
|
||||
}
|
||||
|
||||
remove_pkgs { packages: $repos }
|
||||
}
|
||||
|
||||
def negativo_repo_list []: nothing -> list<path> {
|
||||
dnf repo list
|
||||
| find negativo17
|
||||
| get id
|
||||
| ansi strip
|
||||
| par-each {|repo|
|
||||
dnf repo info $repo --all
|
||||
}
|
||||
| flatten
|
||||
| get id
|
||||
| uniq
|
||||
}
|
||||
|
||||
# Enable negativo17-multimedia repos
|
||||
def enable_negativo []: nothing -> nothing {
|
||||
print $'(ansi green)Enabling negativo17 repos(ansi reset)'
|
||||
|
||||
let current_repo_list = negativo_repo_list
|
||||
|
||||
if ($current_repo_list | is-not-empty) {
|
||||
print $'(ansi green)Cleaning up existing negativo17 repos(ansi reset)'
|
||||
remove_repos $current_repo_list
|
||||
}
|
||||
add_repos [$NEGATIVO_URL]
|
||||
|
||||
dnf repo list
|
||||
| find negativo17
|
||||
| get id
|
||||
| ansi strip
|
||||
| each {|id|
|
||||
[$'($id).enabled=1' $'($id).priority=90']
|
||||
}
|
||||
| flatten
|
||||
| dnf config-manager setopt $in
|
||||
}
|
||||
|
||||
# Disable negativo17-multimedia repos
|
||||
def disable_negativo []: nothing -> nothing {
|
||||
print $'(ansi green)Disabling negativo17 repos(ansi reset)'
|
||||
|
||||
remove_repos (negativo_repo_list)
|
||||
}
|
||||
|
||||
# Adds a list of repo files for `dnf` to use
|
||||
# for installing packages.
|
||||
#
|
||||
# Returns a list of IDs of the repos added
|
||||
def add_repos [$repos: list]: nothing -> list<string> {
|
||||
if ($repos | is-not-empty) {
|
||||
print $'(ansi green)Adding repositories:(ansi reset)'
|
||||
|
||||
# Substitute %OS_VERSION% & remove newlines/whitespaces from all repo entries
|
||||
let repos = $repos
|
||||
| each {
|
||||
str replace --all '%OS_VERSION%' $env.OS_VERSION
|
||||
| str trim
|
||||
}
|
||||
$repos
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
for $repo in $repos {
|
||||
let repo_path = [$env.CONFIG_DIRECTORY dnf $repo] | path join
|
||||
let repo = if ($repo | str starts-with 'https://') or ($repo | str starts-with 'http://') {
|
||||
print $"Adding repository URL: (ansi cyan)'($repo)'(ansi reset)"
|
||||
$repo
|
||||
} else if ($repo | str ends-with '.repo') and ($repo_path | path exists) {
|
||||
print $"Adding repository file: (ansi cyan)'($repo_path)'(ansi reset)"
|
||||
$repo_path
|
||||
} else {
|
||||
return (error make {
|
||||
msg: $"(ansi red)Unrecognized repo (ansi cyan)'($repo)'(ansi reset)"
|
||||
label: {
|
||||
span: (metadata $repo).span
|
||||
text: 'Found in config'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
dnf config-manager addrepo --from-repofile $repo
|
||||
}
|
||||
}
|
||||
|
||||
# Get a list of paths of all new repo files added
|
||||
let repo_files = $repos
|
||||
| each {|repo|
|
||||
[/ etc yum.repos.d ($repo | path basename)] | path join
|
||||
}
|
||||
|
||||
# Get a list of info for every repo installed
|
||||
let repo_info = dnf repo list
|
||||
| get id
|
||||
| par-each {|repo|
|
||||
dnf repo info $repo
|
||||
}
|
||||
| flatten
|
||||
|
||||
# Return the IDs of all repos that were added
|
||||
let repo_ids = $repo_info
|
||||
| filter {|repo|
|
||||
$repo.repo_file_path in $repo_files
|
||||
}
|
||||
| get id
|
||||
|
||||
$repo_ids
|
||||
| each {
|
||||
print $'Enabling repo (ansi cyan)($in)(ansi reset)'
|
||||
$'($in).enabled=1'
|
||||
}
|
||||
| dnf config-manager setopt $in
|
||||
|
||||
$repo_ids
|
||||
}
|
||||
|
||||
# Remove a list of repos. The list must be the IDs of the repos.
|
||||
def remove_repos [$repos: list]: nothing -> nothing {
|
||||
if ($repos | is-not-empty) {
|
||||
print $'(ansi green)Removing repositories:(ansi reset)'
|
||||
let repos = $repos | str trim
|
||||
$repos
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
$repos
|
||||
| par-each {|repo|
|
||||
dnf repo info $repo --all
|
||||
}
|
||||
| flatten
|
||||
| get repo_file_path
|
||||
| uniq
|
||||
| each {|file|
|
||||
print $"Removing repo file '(ansi cyan)($file)(ansi reset)'"
|
||||
rm -f $file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Enable a list of COPR repos. The COPR repo ID has a '/' in the name.
|
||||
#
|
||||
# This will error if a COPR repo ID is invalid.
|
||||
def add_coprs [$copr_repos: list]: nothing -> list<string> {
|
||||
if ($copr_repos | is-not-empty) {
|
||||
print $'(ansi green)Adding COPR repositories:(ansi reset)'
|
||||
$copr_repos
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
for $copr in $copr_repos {
|
||||
print $"Adding COPR repository: (ansi cyan)'($copr)'(ansi reset)"
|
||||
dnf copr enable $copr
|
||||
}
|
||||
}
|
||||
$copr_repos
|
||||
}
|
||||
|
||||
# Disable a list of COPR repos. The COPR repo ID has a '/' in the name.
|
||||
#
|
||||
# This will error if a COPR repo ID is invalid.
|
||||
def disable_coprs [$copr_repos: list]: nothing -> nothing {
|
||||
if ($copr_repos | is-not-empty) {
|
||||
print $'(ansi green)Disabling COPR repositories:(ansi reset)'
|
||||
$copr_repos
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
for $copr in $copr_repos {
|
||||
print $"Disabling COPR repository: (ansi cyan)'($copr)'(ansi reset)"
|
||||
dnf copr disable $copr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add a list of keys for integrity checking repos.
|
||||
def add_keys [$keys: list]: nothing -> nothing {
|
||||
if ($keys | is-not-empty) {
|
||||
print $'(ansi green)Adding keys:(ansi reset)'
|
||||
let keys = $keys
|
||||
| str replace --all '%OS_VERSION%' $env.OS_VERSION
|
||||
| str trim
|
||||
| each {|key|
|
||||
let key = if ($key | str starts-with 'https://') or ($key | str starts-with 'http://') {
|
||||
$key
|
||||
} else {
|
||||
[$env.CONFIG_DIRECTORY dnf $key] | path join
|
||||
}
|
||||
print $'- (ansi cyan)($key)(ansi reset)'
|
||||
$key
|
||||
}
|
||||
|
||||
for $key in $keys {
|
||||
let key = $key
|
||||
| str replace --all '%OS_VERSION%' $env.OS_VERSION
|
||||
| str trim
|
||||
|
||||
try {
|
||||
^rpm --import $key
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Setup /opt directory symlinks to allow certain packages to install.
|
||||
#
|
||||
# Each entry must be the directory name that the application expects
|
||||
# to install into /opt. A systemd unit will be installed to setup
|
||||
# symlinks on boot of the OS.
|
||||
def run_optfix [$optfix_pkgs: list]: nothing -> nothing {
|
||||
const LIB_EXEC_DIR = '/usr/libexec/bluebuild'
|
||||
const SYSTEMD_DIR = '/etc/systemd/system'
|
||||
const MODULE_DIR = '/tmp/modules/dnf'
|
||||
const LIB_OPT_DIR = '/usr/lib/opt'
|
||||
const VAR_OPT_DIR = '/var/opt'
|
||||
const OPTFIX_SCRIPT = 'optfix.sh'
|
||||
const SERV_UNIT = 'bluebuild-optfix.service'
|
||||
|
||||
if ($optfix_pkgs | is-not-empty) {
|
||||
if not ($LIB_EXEC_DIR | path join $OPTFIX_SCRIPT | path exists) {
|
||||
mkdir $LIB_EXEC_DIR
|
||||
cp ($MODULE_DIR | path join $OPTFIX_SCRIPT) $'($LIB_EXEC_DIR)/'
|
||||
|
||||
try {
|
||||
^chmod +x $'($LIB_EXEC_DIR | path join $OPTFIX_SCRIPT)'
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if not ($SYSTEMD_DIR | path join $SERV_UNIT | path exists) {
|
||||
cp ($MODULE_DIR | path join $SERV_UNIT) $'($SYSTEMD_DIR)/'
|
||||
|
||||
try {
|
||||
^systemctl enable $SERV_UNIT
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
print $"(ansi green)Creating symlinks to fix packages that install to /opt:(ansi reset)"
|
||||
$optfix_pkgs
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
mkdir $VAR_OPT_DIR
|
||||
try {
|
||||
^ln -snf $VAR_OPT_DIR /opt
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
|
||||
for $opt in $optfix_pkgs {
|
||||
let lib_dir = [$LIB_OPT_DIR $opt] | path join
|
||||
let var_opt_dir = [$VAR_OPT_DIR $opt] | path join
|
||||
|
||||
mkdir $lib_dir
|
||||
|
||||
try {
|
||||
^ln -sf $lib_dir $var_opt_dir
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
|
||||
print $"Created symlinks for '(ansi cyan)($opt)(ansi reset)'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Remove group packages.
|
||||
def group_remove [remove: record]: nothing -> nothing {
|
||||
let remove_list = $remove
|
||||
| default [] packages
|
||||
| get packages
|
||||
|
||||
if ($remove_list | is-not-empty) {
|
||||
print $'(ansi green)Removing group packages:(ansi reset)'
|
||||
$remove_list
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
try {
|
||||
dnf group remove $remove_list
|
||||
} catch {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Install group packages.
|
||||
def group_install [install: record]: nothing -> nothing {
|
||||
let install = $install
|
||||
| default false with-optional
|
||||
| default [] packages
|
||||
let install_list = $install
|
||||
| get packages
|
||||
| each { str trim }
|
||||
|
||||
if ($install_list | is-not-empty) {
|
||||
print $'(ansi green)Installing group packages:(ansi reset)'
|
||||
$install_list
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
(dnf
|
||||
group
|
||||
install
|
||||
--opts $install
|
||||
$install_list)
|
||||
}
|
||||
}
|
||||
|
||||
# Remove packages.
|
||||
def remove_pkgs [remove: record]: nothing -> nothing {
|
||||
let remove = $remove
|
||||
| default [] packages
|
||||
| default true auto-remove
|
||||
|
||||
if ($remove.packages | is-not-empty) {
|
||||
print $'(ansi green)Removing packages:(ansi reset)'
|
||||
$remove.packages
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
dnf remove --opts $remove $remove.packages
|
||||
}
|
||||
}
|
||||
|
||||
# Install packages.
|
||||
#
|
||||
# You can specify a list of packages to install, and you can
|
||||
# specify a list of packages for a specific repo to install.
|
||||
def install_pkgs [install: record]: nothing -> nothing {
|
||||
let install = $install
|
||||
| default [] packages
|
||||
|
||||
# Gather lists of the various ways a package is installed
|
||||
# to report back to the user.
|
||||
let install_list = $install.packages
|
||||
| filter {|pkg|
|
||||
($pkg | describe) == 'string'
|
||||
}
|
||||
| str replace --all '%OS_VERSION%' $env.OS_VERSION
|
||||
| str trim
|
||||
let http_list = $install_list
|
||||
| filter {|pkg|
|
||||
($pkg | str starts-with 'https://') or ($pkg | str starts-with 'http://')
|
||||
}
|
||||
let local_list = $install_list
|
||||
| each {|pkg|
|
||||
[$env.CONFIG_DIRECTORY dnf $pkg] | path join
|
||||
}
|
||||
| filter {|pkg|
|
||||
($pkg | path exists)
|
||||
}
|
||||
let normal_list = $install_list
|
||||
| filter {|pkg|
|
||||
not (
|
||||
($pkg | str starts-with 'https://') or ($pkg | str starts-with 'http://')
|
||||
) and not (
|
||||
[$env.CONFIG_DIRECTORY dnf $pkg]
|
||||
| path join
|
||||
| path exists
|
||||
)
|
||||
}
|
||||
|
||||
if ($install_list | is-not-empty) {
|
||||
if ($http_list | is-not-empty) {
|
||||
print $'(ansi green)Installing packages directly from URL:(ansi reset)'
|
||||
$http_list
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
}
|
||||
|
||||
if ($local_list | is-not-empty) {
|
||||
print $'(ansi green)Installing local packages:(ansi reset)'
|
||||
$local_list
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
}
|
||||
|
||||
if ($normal_list | is-not-empty) {
|
||||
print $'(ansi green)Installing packages:(ansi reset)'
|
||||
$normal_list
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
}
|
||||
|
||||
(dnf
|
||||
install
|
||||
--opts $install
|
||||
([
|
||||
$http_list
|
||||
$local_list
|
||||
$normal_list
|
||||
] | flatten))
|
||||
}
|
||||
|
||||
# Get all the entries that have a repo specified.
|
||||
let repo_install_list = $install.packages
|
||||
| filter {|pkg|
|
||||
'repo' in $pkg and 'packages' in $pkg
|
||||
}
|
||||
|
||||
for $repo_install in $repo_install_list {
|
||||
let repo = $repo_install.repo
|
||||
let packages = $repo_install.packages
|
||||
|
||||
print $'(ansi green)Installing packages from repo (ansi cyan)($repo)(ansi green):(ansi reset)'
|
||||
$packages
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
(dnf
|
||||
install
|
||||
--repoid
|
||||
$repo
|
||||
--opts $repo_install
|
||||
--global-opts $install
|
||||
$packages)
|
||||
}
|
||||
}
|
||||
|
||||
# Perform a replace operation for a list of packages that
|
||||
# you want to replace from a specific repo.
|
||||
def replace_pkgs [replace_list: list]: nothing -> nothing {
|
||||
let check = {|item|
|
||||
'old' in $item and 'new' in $item
|
||||
}
|
||||
|
||||
if ($replace_list | is-not-empty) {
|
||||
for $replacement in $replace_list {
|
||||
let replacement = $replacement
|
||||
| default [] packages
|
||||
|
||||
if ($replacement.packages | is-not-empty) {
|
||||
let has_from_repo = 'from-repo' in $replacement
|
||||
|
||||
if not $has_from_repo {
|
||||
return (error make {
|
||||
msg: $"(ansi red)A value is expected in key 'from-repo'(ansi reset)"
|
||||
label: {
|
||||
span: (metadata $replacement).span
|
||||
text: "Checks for 'from-repo' property"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let from_repo = $replacement
|
||||
| get from-repo
|
||||
|
||||
let swap_packages = $replacement.packages
|
||||
| filter $check
|
||||
let sync_packages = $replacement.packages
|
||||
| filter {
|
||||
not (do $check $in)
|
||||
}
|
||||
|
||||
if ($swap_packages | is-not-empty) {
|
||||
print $"(ansi green)Swapping packages from '(ansi cyan)($from_repo)(ansi green)':(ansi reset)"
|
||||
$swap_packages
|
||||
| each {
|
||||
print $'- (ansi cyan)($in.old)(ansi green) -> (ansi cyan)($in.new)(ansi reset)'
|
||||
}
|
||||
|
||||
for $pkg_pair in $swap_packages {
|
||||
(dnf
|
||||
swap
|
||||
--opts $pkg_pair
|
||||
--global-opts $replacement
|
||||
$pkg_pair.old
|
||||
$pkg_pair.new)
|
||||
}
|
||||
}
|
||||
|
||||
if ($sync_packages | is-not-empty) {
|
||||
print $"(ansi green)Replacing packages from '(ansi cyan)($from_repo)(ansi green)':(ansi reset)"
|
||||
$sync_packages
|
||||
| each {
|
||||
print $'- (ansi cyan)($in)(ansi reset)'
|
||||
}
|
||||
|
||||
(dnf
|
||||
distro-sync
|
||||
--opts $replacement
|
||||
--repo $from_repo
|
||||
$sync_packages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def main [config: string]: nothing -> nothing {
|
||||
let config = $config
|
||||
| from json
|
||||
| default {} repos
|
||||
| default {} group-remove
|
||||
| default {} group-install
|
||||
| default {} remove
|
||||
| default {} install
|
||||
| default [] optfix
|
||||
| default [] replace
|
||||
let should_cleanup = $config.repos
|
||||
| default false cleanup
|
||||
| get cleanup
|
||||
|
||||
dnf version
|
||||
|
||||
let cleanup_repos = repos $config.repos
|
||||
|
||||
dnf makecache
|
||||
|
||||
run_optfix $config.optfix
|
||||
group_remove $config.group-remove
|
||||
group_install $config.group-install
|
||||
remove_pkgs $config.remove
|
||||
install_pkgs $config.install
|
||||
replace_pkgs $config.replace
|
||||
|
||||
if $should_cleanup {
|
||||
print $'(ansi green)Cleaning up added repos(ansi reset)'
|
||||
remove_repos $cleanup_repos.files
|
||||
disable_coprs $cleanup_repos.copr
|
||||
|
||||
match $config.repos.nonfree? {
|
||||
$repo if $repo == $RPMFUSION => {
|
||||
disable_rpmfusion
|
||||
}
|
||||
$repo if $repo == $NEGATIVO => {
|
||||
disable_negativo
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
print $'(ansi green)Finished cleaning up repos(ansi reset)'
|
||||
}
|
||||
}
|
||||
159
modules/dnf/dnf.tsp
Normal file
159
modules/dnf/dnf.tsp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import "@typespec/json-schema";
|
||||
using TypeSpec.JsonSchema;
|
||||
|
||||
@jsonSchema("/modules/dnf-latest.json")
|
||||
model DnfModuleLatest {
|
||||
...DnfModuleV1;
|
||||
}
|
||||
|
||||
@jsonSchema("/modules/dnf-v1.json")
|
||||
model DnfModuleV1 {
|
||||
/**
|
||||
* The dnf module offers pseudo-declarative package and repository management using dnf.
|
||||
* https://blue-build.org/reference/modules/dnf/
|
||||
*/
|
||||
type: "dnf" | "dnf@v1" | "dnf@latest";
|
||||
|
||||
/** List of links to .repo files to download into /etc/yum.repos.d/. */
|
||||
repos?: Repo;
|
||||
|
||||
/** List of folder names under /opt/ to enable for installing into. */
|
||||
optfix?: Array<string>;
|
||||
|
||||
/** Configuration of RPM groups removal. */
|
||||
`group-remove`?: GroupRemove;
|
||||
|
||||
/** Configuration of RPM groups install. */
|
||||
`group-install`?: GroupInstall;
|
||||
|
||||
/** Configuration of RPM packages removal. */
|
||||
remove?: Remove;
|
||||
|
||||
/** Configuration of RPM packages install. */
|
||||
install?: Install;
|
||||
|
||||
/** List of configurations for replacing packages from another repo. */
|
||||
replace?: Array<Replace>;
|
||||
}
|
||||
|
||||
model Repo {
|
||||
/** Cleans up the repos added in the same step after packages are installed. */
|
||||
cleanup?: boolean = false;
|
||||
|
||||
/** List of paths or URLs to .repo files to import */
|
||||
files?: Array<string> | RepoFiles;
|
||||
|
||||
/**
|
||||
* List of COPR project repos to add.
|
||||
* You can also specify 2 lists
|
||||
* instead to 'enable' or 'disable' COPR repos.
|
||||
*/
|
||||
copr?: Array<string> | RepoCopr;
|
||||
|
||||
/** List of links to key files to import for installing from custom repositories. */
|
||||
keys?: Array<string>;
|
||||
|
||||
/**
|
||||
* Enable one of the nonfree repos.
|
||||
*
|
||||
* This allows you to enable one of the nonfree repos.
|
||||
* However, only one can be enabled at a time so if one
|
||||
* is enabled, the other will be disabled if it is already enabled.
|
||||
*/
|
||||
nonfree?: "negativo17" | "rpmfusion";
|
||||
}
|
||||
|
||||
model RepoFiles {
|
||||
/** List of repo files/URLs to add. */
|
||||
add?: Array<string>;
|
||||
|
||||
/**
|
||||
* List of repos to disable.
|
||||
* This must be the ID of the repo
|
||||
* as seen in `dnf5 repolist`.
|
||||
*/
|
||||
remove?: Array<string>;
|
||||
}
|
||||
|
||||
model RepoCopr {
|
||||
/** List of COPR repos to enable */
|
||||
enable?: Array<string>;
|
||||
|
||||
/** List of COPR repos to disable */
|
||||
disable?: Array<string>;
|
||||
}
|
||||
|
||||
model Install {
|
||||
/** List of RPM packages to install. */
|
||||
packages: Array<string | InstallRepo>;
|
||||
|
||||
...InstallCommon;
|
||||
}
|
||||
|
||||
model InstallRepo {
|
||||
/** The repo to use when installing packages */
|
||||
repo: string;
|
||||
|
||||
/** List of RPM packages to install. */
|
||||
packages: Array<string>;
|
||||
|
||||
...InstallCommon;
|
||||
}
|
||||
|
||||
model Remove {
|
||||
/** List of RPM packages to remove. */
|
||||
packages: Array<string>;
|
||||
|
||||
/** Whether to remove unused dependencies during removal operation. */
|
||||
`auto-remove`?: boolean = true;
|
||||
}
|
||||
|
||||
model Replace {
|
||||
/** URL to the source COPR repo for the new packages. */
|
||||
`from-repo`: string;
|
||||
|
||||
/** List of packages to replace using packages from the defined repo. */
|
||||
packages: Array<string | Swap>;
|
||||
|
||||
...InstallCommon;
|
||||
}
|
||||
|
||||
model Swap {
|
||||
/** The package to be replaced. */
|
||||
old: string;
|
||||
|
||||
/** The package to replace with. */
|
||||
new: string;
|
||||
|
||||
/** Whether to allow erasing (removal) of packages in case of dependency problems. */
|
||||
`allow-erasing`?: boolean = false;
|
||||
}
|
||||
|
||||
model GroupInstall {
|
||||
/** List of RPM groups to install. */
|
||||
packages: Array<string>;
|
||||
|
||||
/** Include optional packages from group. */
|
||||
`with-optional`?: boolean = false;
|
||||
|
||||
...InstallCommon;
|
||||
}
|
||||
|
||||
model GroupRemove {
|
||||
/** List of RPM groups to remove. */
|
||||
packages: Array<string>;
|
||||
}
|
||||
|
||||
model InstallCommon {
|
||||
/** Whether to install weak dependencies. */
|
||||
`install-weak-deps`?: boolean = true;
|
||||
|
||||
/** Whether to continue with the install if there are no packages available in the repository. */
|
||||
`skip-unavailable`?: boolean = false;
|
||||
|
||||
/** Whether to continue with the install if there are broken packages. */
|
||||
`skip-broken`?: boolean = false;
|
||||
|
||||
/** Whether to allow erasing (removal) of packages in case of dependency problems. */
|
||||
`allow-erasing`?: boolean = false;
|
||||
}
|
||||
394
modules/dnf/dnf_interface.nu
Normal file
394
modules/dnf/dnf_interface.nu
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
export def "dnf install" [
|
||||
--opts: record
|
||||
--global-opts: record
|
||||
--repoid: string
|
||||
packages: list
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
(^$dnf.path
|
||||
-y
|
||||
($opts | weak_arg --global-config $global_opts)
|
||||
install
|
||||
...(if $repoid != null {
|
||||
[--repoid $repoid]
|
||||
} else {
|
||||
[]
|
||||
})
|
||||
...($opts | install_args --global-config $global_opts)
|
||||
...$packages)
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf remove" [
|
||||
--opts: record
|
||||
packages: list
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
mut args = []
|
||||
|
||||
if not $opts.auto-remove {
|
||||
$args = $args | append '--no-autoremove'
|
||||
}
|
||||
|
||||
try {
|
||||
(^$dnf.path
|
||||
-y
|
||||
remove
|
||||
...($args)
|
||||
...($packages))
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf config-manager addrepo" [
|
||||
--from-repofile: string
|
||||
]: nothing -> nothing {
|
||||
check_dnf_plugins
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
match $dnf.command {
|
||||
"dnf4" => {
|
||||
^dnf4 -v -y config-manager --add-repo $from_repofile
|
||||
}
|
||||
"dnf5" => {
|
||||
(^dnf5
|
||||
-y
|
||||
config-manager
|
||||
addrepo
|
||||
--create-missing-dir
|
||||
--overwrite
|
||||
--from-repofile $from_repofile)
|
||||
}
|
||||
}
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf config-manager setopt" [
|
||||
opts: list
|
||||
]: nothing -> nothing {
|
||||
check_dnf_plugins
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
match $dnf.command {
|
||||
"dnf4" => {
|
||||
(^dnf4
|
||||
-y
|
||||
config-manager
|
||||
--save
|
||||
...($opts
|
||||
| each {|opt|
|
||||
[--setopt $opt]
|
||||
}
|
||||
| flatten))
|
||||
}
|
||||
"dnf5" => {
|
||||
^dnf5 -y config-manager setopt ...($opts)
|
||||
}
|
||||
}
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf copr enable" [copr: string]: nothing -> nothing {
|
||||
check_dnf_plugins
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
^$dnf.path -y copr enable ($copr | check_copr)
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf copr disable" [copr: string]: nothing -> nothing {
|
||||
check_dnf_plugins
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
^$dnf.path -y copr disable ($copr | check_copr)
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf swap" [
|
||||
--opts: record
|
||||
--global-opts: record
|
||||
old: string
|
||||
new: string
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
(^$dnf.path
|
||||
-y
|
||||
swap
|
||||
...($opts | install_args --global-config $global_opts 'allow-erasing')
|
||||
$old
|
||||
$new)
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf distro-sync" [
|
||||
--opts: record
|
||||
--repo: string
|
||||
packages: list
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
(^$dnf.path
|
||||
-y
|
||||
($opts | weak_arg)
|
||||
distro-sync
|
||||
...($opts | install_args)
|
||||
--repo $repo
|
||||
...($packages))
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf group install" [
|
||||
--opts: record
|
||||
packages: list
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
mut args = $opts | install_args
|
||||
|
||||
if $opts.with-optional {
|
||||
$args = $args | append '--with-optional'
|
||||
}
|
||||
|
||||
try {
|
||||
(^$dnf.path
|
||||
-y
|
||||
($opts | weak_arg)
|
||||
group
|
||||
install
|
||||
...($args)
|
||||
...($packages))
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf group remove" [
|
||||
packages: list
|
||||
]: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
(^$dnf.path -y group remove ...($packages))
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf repo list" []: nothing -> list {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
match $dnf.command {
|
||||
"dnf4" => {
|
||||
^/tmp/modules/dnf/dnf-repolist | from json
|
||||
}
|
||||
"dnf5" => {
|
||||
^dnf5 repo list --all --json | from json
|
||||
}
|
||||
}
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf repo info" [
|
||||
repo: string
|
||||
--all
|
||||
]: nothing -> record {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
match $dnf.command {
|
||||
"dnf4" => {
|
||||
^/tmp/modules/dnf/dnf-repoinfo $repo | from json
|
||||
}
|
||||
"dnf5" => {
|
||||
(^dnf5
|
||||
-y
|
||||
repo
|
||||
info
|
||||
$repo
|
||||
...(if $all {
|
||||
[--all]
|
||||
} else {
|
||||
[]
|
||||
})
|
||||
--json)
|
||||
| from json
|
||||
}
|
||||
}
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf makecache" []: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
try {
|
||||
^$dnf.path makecache --refresh
|
||||
} catch {|e|
|
||||
print $'($e.msg)'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
export def "dnf version" []: nothing -> record {
|
||||
let dnf = which dnf4 dnf5
|
||||
|
||||
if ("dnf5" in ($dnf | get command)) {
|
||||
$dnf | filter { $in.command == "dnf5" } | first
|
||||
} else if ("dnf4" in ($dnf | get command)) {
|
||||
$dnf | filter { $in.command == "dnf4" } | first
|
||||
} else {
|
||||
return (error make {
|
||||
msg: $"(ansi red)ERROR: Main dependency '(ansi cyan)dnf5/dnf4(ansi red)' is not installed. Install '(ansi cyan)dnf5/dnf4(ansi red)' before using this module to solve this error.(ansi reset)"
|
||||
label: {
|
||||
span: (metadata $dnf).span
|
||||
text: 'Checks for dnf5/dnf4'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
# Build up args to use on `dnf`
|
||||
def install_args [
|
||||
--global-config: record
|
||||
...filter: string
|
||||
]: record -> list<string> {
|
||||
let opts = $in | default {}
|
||||
let global_config = $global_config | default {}
|
||||
let install = $opts
|
||||
| default (
|
||||
$global_config.skip-unavailable?
|
||||
| default false
|
||||
) skip-unavailable
|
||||
| default (
|
||||
$global_config.skip-broken?
|
||||
| default false
|
||||
) skip-broken
|
||||
| default (
|
||||
$global_config.allow-erasing?
|
||||
| default false
|
||||
) allow-erasing
|
||||
mut args = []
|
||||
let check_filter = {|arg|
|
||||
let arg_exists = ($arg in $install)
|
||||
if ($filter | is-empty) {
|
||||
$arg_exists and ($install | get $arg)
|
||||
} else {
|
||||
$arg_exists and ($arg in $filter) and ($install | get $arg)
|
||||
}
|
||||
}
|
||||
|
||||
if (do $check_filter 'skip-unavailable') {
|
||||
$args = $args | append '--skip-unavailable'
|
||||
}
|
||||
|
||||
if (do $check_filter 'skip-broken') {
|
||||
$args = $args | append '--skip-broken'
|
||||
}
|
||||
|
||||
if (do $check_filter 'allow-erasing') {
|
||||
$args = $args | append '--allowerasing'
|
||||
}
|
||||
|
||||
$args
|
||||
}
|
||||
|
||||
# Generate a weak deps argument
|
||||
def weak_arg [
|
||||
--global-config: record
|
||||
]: record -> string {
|
||||
let opts = $in | default {}
|
||||
let global_config = $global_config | default {}
|
||||
let install = $opts
|
||||
| default (
|
||||
$global_config.install-weak-deps?
|
||||
| default true
|
||||
) install-weak-deps
|
||||
|
||||
if $install.install-weak-deps {
|
||||
'--setopt=install_weak_deps=True'
|
||||
} else {
|
||||
'--setopt=install_weak_deps=False'
|
||||
}
|
||||
}
|
||||
|
||||
# Handles installing necessary plugins for repo management.
|
||||
def check_dnf_plugins []: nothing -> nothing {
|
||||
let dnf = dnf version
|
||||
|
||||
match $dnf.command {
|
||||
"dnf4" => {
|
||||
if (^rpm -q dnf-plugins-core | complete).exit_code != 0 {
|
||||
print $'(ansi yellow1)Required dnf4 plugins are not installed. Installing plugins(ansi reset)'
|
||||
|
||||
dnf install [dnf-plugins-core]
|
||||
}
|
||||
}
|
||||
"dnf5" => {
|
||||
if (^rpm -q dnf5-plugins | complete).exit_code != 0 {
|
||||
print $'(ansi yellow1)Required dnf5 plugins are not installed. Installing plugins(ansi reset)'
|
||||
|
||||
dnf install [dnf5-plugins]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Checks to see if the string passed in is
|
||||
# a COPR repo string. Will error if it isn't
|
||||
def check_copr []: string -> string {
|
||||
let is_copr = ($in | split row / | length) == 2
|
||||
|
||||
if not $is_copr {
|
||||
return (error make {
|
||||
msg: $"(ansi red)The string '(ansi cyan)($in)(ansi red)' is not recognized as a COPR repo(ansi reset)"
|
||||
label: {
|
||||
span: (metadata $is_copr).span
|
||||
text: 'Checks if string is a COPR repo'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$in
|
||||
}
|
||||
|
||||
47
modules/dnf/module.yml
Normal file
47
modules/dnf/module.yml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
name: dnf
|
||||
shortdesc: The dnf module offers pseudo-declarative package and repository management using dnf.
|
||||
example: |
|
||||
type: dnf
|
||||
repos:
|
||||
cleanup: true # clean up added repos after module is done
|
||||
files:
|
||||
- https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo
|
||||
- fury.repo
|
||||
copr:
|
||||
- atim/starship
|
||||
- trixieua/mutter-patched
|
||||
keys:
|
||||
- https://brave-browser-rpm-release.s3.brave.com/brave-core.asc
|
||||
nonfree: rpmfusion
|
||||
optfix: # performs symlinking for `/opt/` to allow certain packages to install
|
||||
- Tabby # needed because tabby installs into `/opt/Tabby/`
|
||||
- brave.com
|
||||
install:
|
||||
skip-unavailable: true # skip unavailable packages
|
||||
packages:
|
||||
- repo: brave-browser
|
||||
packages:
|
||||
- brave-browser
|
||||
- starship
|
||||
- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.rpm
|
||||
- kubectl.rpm
|
||||
remove:
|
||||
packages:
|
||||
- firefox
|
||||
- firefox-langpacks
|
||||
replace:
|
||||
- from-repo: copr:copr.fedorainfracloud.org:trixieua:mutter-patched
|
||||
skip-unavailable: true # skip unavailable packages
|
||||
packages:
|
||||
- mutter
|
||||
- mutter-common
|
||||
- gdm
|
||||
group-install:
|
||||
with-optional: true # install optional packages from group
|
||||
packages:
|
||||
- cosmic-desktop
|
||||
- cosmic-desktop-apps
|
||||
- window-managers
|
||||
group-remove:
|
||||
packages:
|
||||
- development-tools
|
||||
27
modules/dnf/optfix.sh
Normal file
27
modules/dnf/optfix.sh
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_DIR="/usr/lib/opt"
|
||||
TARGET_DIR="/var/opt"
|
||||
|
||||
# Ensure the target directory exists
|
||||
mkdir -p "$TARGET_DIR"
|
||||
|
||||
# Loop through directories in the source directory
|
||||
for dir in "$SOURCE_DIR/"*/; do
|
||||
if [ -d "$dir" ]; then
|
||||
# Get the base name of the directory
|
||||
dir_name=$(basename "$dir")
|
||||
|
||||
# Check if the symlink already exists in the target directory
|
||||
if [ -L "$TARGET_DIR/$dir_name" ]; then
|
||||
echo "Symlink already exists for $dir_name, skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Create the symlink
|
||||
ln -s "$dir" "$TARGET_DIR/$dir_name"
|
||||
echo "Created symlink for $dir_name"
|
||||
fi
|
||||
done
|
||||
|
|
@ -60,7 +60,8 @@ if [[ ${#OPTFIX[@]} -gt 0 ]]; then
|
|||
echo "Creating symlinks to fix packages that install to /opt"
|
||||
# Create symlink for /opt to /var/opt since it is not created in the image yet
|
||||
mkdir -p "/var/opt"
|
||||
ln -fs "/var/opt" "/opt"
|
||||
ln -fs "/var/opt" "/opt"
|
||||
|
||||
|
||||
# Create symlinks for each directory specified in recipe.yml
|
||||
for OPTPKG in "${OPTFIX[@]}"; do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue