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>
690 lines
17 KiB
Text
690 lines
17 KiB
Text
#!/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)'
|
|
}
|
|
}
|