-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: dnf
module
#377
Draft
fiftydinar
wants to merge
52
commits into
main
Choose a base branch
from
dnf-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+488
−1
Draft
feat: dnf
module
#377
Changes from 2 commits
Commits
Show all changes
52 commits
Select commit
Hold shift + click to select a range
fd4ad28
feat: `dnf` module
fiftydinar cd91b05
chore: Add support for native installation of repos
fiftydinar cf3d495
docs: Revise the note about %OS_VERSION% in repos
fiftydinar 2f392e7
fix: missing quote in echo
fiftydinar 6d1047f
fix: Get raw COPR repos array elements without JSON brackets
fiftydinar 9d13c87
chore: Auto-confirm prompts (`-y`) when adding repos
fiftydinar 1396a18
fix: Accidental syntax error for `done`
fiftydinar b898af2
chore: Remove useless echo
fiftydinar 6523979
chore: Install & remove using `dnf` in single operation cases
fiftydinar 97566b8
docs: Add note that URL is not preferred for copr repos
fiftydinar 0bb79d0
fix: `/opt` symlink issue & migrate removal + install to `dnf`
fiftydinar 70c5766
chore: Document code some more
fiftydinar e01ed40
docs: Fix note about using install & remove at the same time
fiftydinar 7d4aff9
chore: Add `dnf` to `modules.json`
fiftydinar a4e9ba4
chore: Add support for replacing packages
fiftydinar 7da24f5
chore: Add option to enable or disable installation of weak dependencies
fiftydinar d495b6a
docs: Style backtick fix
fiftydinar 1137c8b
fix(rpm-ostree): Symlinking `/opt/` multiple times caused an error
fiftydinar eb522e3
chore: Rename `weak-dependencies` to `install-weak-dependencies` & ad…
fiftydinar 57f8b22
chore: Change `copr: user/project` formt to `COPR user/project`
fiftydinar 7a5bcea
fix: Syntax error due to redundant `fi`
fiftydinar c7ae430
fix: Restore logic about not removing any newlines to COPR repos
fiftydinar b1a7c6f
feat: Add removal & installation of RPM groups
fiftydinar b7c0206
docs: Style fix
fiftydinar feb0d71
chore: Make sorting order in typespec better
fiftydinar fba3840
typespec: Minor leftover fix
fiftydinar e27ba39
chore: Fix typespec replace definition
fiftydinar a53dd5b
chore: Add mutter-patched COPR to module.yml
fiftydinar 1a81609
fix: Forgot to replace `rpm-ostree` to `dnf` when checking for local …
fiftydinar a72ae5c
fix: Adding local file repos
fiftydinar 2790123
fix: Replacing packages
fiftydinar ecffeae
chore: Make error message consistent
fiftydinar 84f89d0
chore: Begin implementation of specific flag options for every instal…
fiftydinar 5d7f34b
chore: Simplify printing log message for install packages
fiftydinar 7fc5a24
feat: Add dnf flags to all operations
fiftydinar 63f745b
feat: Add `remove-unused-dependencies` option for package removal
fiftydinar b474a5a
chore: Fix group-remove typespec
fiftydinar 770bb33
chore: Fix group-install typespec
fiftydinar d8a9006
chore: Fix remove typespec
fiftydinar 33065b4
chore: Fix install typespec
fiftydinar 3423190
chore: Remove leftover global `install-weak-dependencies` option
fiftydinar f176120
chore: Remove redundant dnf5 plugin check
fiftydinar 7458b4b
fix: dnf group remove command missed `group` prefix
fiftydinar ce4163c
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar c8043a4
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar aa92eff
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar 8265b85
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar c9f4fb0
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar 92df6a0
fix: Don't quote the flags, else dnf will see it as an empty argument…
fiftydinar e66b176
Merge branch 'main' into dnf-module
fiftydinar 4920cc4
chore: Add separate `copr` array
fiftydinar 7612cc2
fix: Assume yes for copr enable
gmpinder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# `dnf` | ||
|
||
The [`dnf`](https://docs.fedoraproject.org/en-US/quick-docs/dnf/) module offers pseudo-declarative package and repository management using `dnf`. | ||
|
||
The module first downloads the repository files from URLs declared under `repos:` into `/etc/yum.repos.d/`. The magic string `%OS_VERSION%` is substituted with the current VERSION_ID (major Fedora version), which can be used, for example, for pulling correct versions of repositories from [Fedora's Copr](https://copr.fedorainfracloud.org/). | ||
|
||
You can also add repository files directly into your git repository if URLs are not provided. For example: | ||
```yml | ||
repos: | ||
- my-repository.repo # copies in .repo file from files/dnf/my-repository.repo to /etc/yum.repos.d/ | ||
``` | ||
|
||
Specific COPR repositories can also be specified in `copr: user/project` format. | ||
|
||
If you use a repo that requires adding custom keys (eg. Brave Browser), you can import the keys by declaring the key URLs under `keys:`. The magic string acts the same as it does in `repos`. | ||
|
||
Then the module installs the packages declared under `install:` using `dnf install`, it removes the packages declared under `remove:` using `dnf remove`. If there are packages declared under both `install:` and `remove:` a hybrid command `dnf remove <packages> --install <packages>` is used, which should allow you to switch required packages for other ones. | ||
|
||
Installing RPM packages directly from a `http(s)` url that points to the RPM file is also supported, you can just put the URLs under `install:` and they'll be installed along with the other packages. The magic string `%OS_VERSION%` is substituted with the current VERSION_ID (major Fedora version) like with the `repos:` property. | ||
|
||
If an RPM is not available in a repository or as an URL, you can also install it directly from a file in your git repository. For example: | ||
```yml | ||
install: | ||
- weird-package.rpm # tries to install files/dnf/weird-package.rpm | ||
``` | ||
The module can also replace base RPM packages with packages from COPR repo. Under `replace:`, the module finds every pair of keys `- from-repo:` and `packages:`. (Multiple pairs are supported.) The module downloads the COPR repository file declared by `- from-repo:` into `/etc/yum.repos.d/`, and from that repository replaces packages declared under `packages:` using the command `dnf replace`. The COPR repository file is then deleted. The magic string `%OS_VERSION%` is substituted with the current VERSION_ID (major Fedora version) as already said above. At the moment, only COPR repo is supported. | ||
|
||
:::note | ||
[Removed packages are still present in the underlying ostree repository](https://coreos.github.io/rpm-ostree/administrator-handbook/#removing-a-base-package), what `remove` does is kind of like hiding them from the system, it doesn't free up storage space. | ||
::: | ||
|
||
Additionally, the `dnf` module supports a fix for packages that install into `/opt/`. Installation for packages that install into folder names declared under `optfix:` are fixed using some symlinks. Directory path in `/opt/` for those packages should be provided in recipe, like in Example Configuration. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Tell build process to exit if there are any errors. | ||
set -euo pipefail | ||
|
||
# Fail the build if dnf5 isn't installed | ||
if ! rpm -q dnf5 &>/dev/null; then | ||
echo "ERROR: Main dependency 'dnf5' is not installed. Install 'dnf5' before using this module to solve this error." | ||
exit 1 | ||
fi | ||
|
||
# Fail the build if dnf5 plugins aren't installed | ||
if ! rpm -q dnf5-plugins &>/dev/null; then | ||
echo "ERROR: Dependency 'dnf5-plugins' is not installed. It is needed for cleanly adding COPR repositories. | ||
echo " Install 'dnf5-plugins' before using this module to solve this error." | ||
exit 1 | ||
fi | ||
|
||
# Pull in repos | ||
get_json_array REPOS 'try .["repos"][]' "$1" | ||
if [[ ${#REPOS[@]} -gt 0 ]]; then | ||
echo "Adding repositories" | ||
# Substitute %OS_VERSION% & remove newlines/whitespaces from all repo entries | ||
for i in "${!REPOS[@]}"; do | ||
repo="${REPOS[$i]}" | ||
repo="${repo//%OS_VERSION%/${OS_VERSION}}" | ||
REPOS[$i]="${repo//[$'\t\r\n ']}" | ||
done | ||
# dnf config-manager & dnf copr don't support adding multiple repositories at once, hence why for/done loop is used | ||
for repo in "${REPOS[@]}"; do | ||
if [[ "${repo}" =~ ^https?:\/\/.* ]]; then | ||
echo "Adding repository URL: '${repo}'" | ||
dnf config-manager addrepo --from-repofile="${repo}" | ||
elif [[ "${repo}" == *".repo" ]] && [[ -f "${CONFIG_DIRECTORY}/dnf/${repo}" ]]; then | ||
echo "Adding repository file: '${repo}'" | ||
dnf config-manager addrepo --from-repofile="${repo}" | ||
elif [[ "${repo}" == "copr: "* ]]; then | ||
echo "Adding COPR repository: '${repo#copr: }'" | ||
dnf copr enable "${repo#copr: }" | ||
fi | ||
done | ||
fi | ||
|
||
get_json_array KEYS 'try .["keys"][]' "$1" | ||
if [[ ${#KEYS[@]} -gt 0 ]]; then | ||
echo "Adding keys" | ||
for KEY in "${KEYS[@]}"; do | ||
KEY="${KEY//%OS_VERSION%/${OS_VERSION}}" | ||
rpm --import "${KEY//[$'\t\r\n ']}" | ||
done | ||
fi | ||
|
||
# Create symlinks to fix packages that create directories in /opt | ||
get_json_array OPTFIX 'try .["optfix"][]' "$1" | ||
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 -s "/var/opt" "/opt" | ||
# Create symlinks for each directory specified in recipe.yml | ||
for OPTPKG in "${OPTFIX[@]}"; do | ||
OPTPKG="${OPTPKG%\"}" | ||
OPTPKG="${OPTPKG#\"}" | ||
mkdir -p "/usr/lib/opt/${OPTPKG}" | ||
ln -s "../../usr/lib/opt/${OPTPKG}" "/var/opt/${OPTPKG}" | ||
echo "Created symlinks for ${OPTPKG}" | ||
done | ||
fi | ||
|
||
get_json_array INSTALL_PKGS 'try .["install"][]' "$1" | ||
get_json_array REMOVE_PKGS 'try .["remove"][]' "$1" | ||
|
||
CLASSIC_INSTALL=false | ||
HTTPS_INSTALL=false | ||
LOCAL_INSTALL=false | ||
|
||
# Install and remove RPM packages | ||
# Sort classic, URL & local packages | ||
if [[ ${#INSTALL_PKGS[@]} -gt 0 ]]; then | ||
for i in "${!INSTALL_PKGS[@]}"; do | ||
PKG="${INSTALL_PKGS[$i]}" | ||
if [[ "${PKG}" =~ ^https?:\/\/.* ]]; then | ||
INSTALL_PKGS[$i]="${PKG//%OS_VERSION%/${OS_VERSION}}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
HTTPS_INSTALL=true | ||
HTTPS_PKGS+=("${INSTALL_PKGS[$i]}") | ||
elif [[ ! "${PKG}" =~ ^https?:\/\/.* ]] && [[ -f "${CONFIG_DIRECTORY}/rpm-ostree/${PKG}" ]]; then | ||
LOCAL_INSTALL=true | ||
LOCAL_PKGS+=("${CONFIG_DIRECTORY}/rpm-ostree/${PKG}") | ||
else | ||
CLASSIC_INSTALL=true | ||
CLASSIC_PKGS+=("${PKG}") | ||
fi | ||
done | ||
fi | ||
|
||
echo_rpm_install() { | ||
if ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
echo "Installing: ${CLASSIC_PKGS[*]}" | ||
elif ! ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
echo "Installing package(s) directly from URL: ${HTTPS_PKGS[*]}" | ||
elif ! ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
echo "Installing local package(s): ${LOCAL_PKGS[*]}" | ||
elif ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
echo "Installing: ${CLASSIC_PKGS[*]}" | ||
echo "Installing package(s) directly from URL: ${HTTPS_PKGS[*]}" | ||
elif ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
echo "Installing: ${CLASSIC_PKGS[*]}" | ||
echo "Installing local package(s): ${LOCAL_PKGS[*]}" | ||
elif ! ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
echo "Installing package(s) directly from URL: ${HTTPS_PKGS[*]}" | ||
echo "Installing local package(s): ${LOCAL_PKGS[*]}" | ||
elif ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
echo "Installing: ${CLASSIC_PKGS[*]}" | ||
echo "Installing package(s) directly from URL: ${HTTPS_PKGS[*]}" | ||
echo "Installing local package(s): ${LOCAL_PKGS[*]}" | ||
fi | ||
fiftydinar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if [[ ${#INSTALL_PKGS[@]} -gt 0 && ${#REMOVE_PKGS[@]} -gt 0 ]]; then | ||
echo "Installing & Removing RPMs" | ||
echo_rpm_install | ||
echo "Removing: ${REMOVE_PKGS[*]}" | ||
# Doing both actions in one command allows for replacing required packages with alternatives | ||
# When --install= flag is used, URLs & local packages are not supported | ||
if ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" $(printf -- "--install=%s " "${CLASSIC_PKGS[@]}") | ||
elif ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" $(printf -- "--install=%s " "${CLASSIC_PKGS[@]}") | ||
rpm-ostree install "${HTTPS_PKGS[@]}" | ||
elif ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" $(printf -- "--install=%s " "${CLASSIC_PKGS[@]}") | ||
rpm-ostree install "${LOCAL_PKGS[@]}" | ||
elif ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" $(printf -- "--install=%s " "${CLASSIC_PKGS[@]}") | ||
rpm-ostree install "${HTTPS_PKGS[@]}" "${LOCAL_PKGS[@]}" | ||
elif ! ${CLASSIC_INSTALL} && ! ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" | ||
rpm-ostree install "${LOCAL_PKGS[@]}" | ||
elif ! ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ! ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" | ||
rpm-ostree install "${HTTPS_PKGS[@]}" | ||
elif ! ${CLASSIC_INSTALL} && ${HTTPS_INSTALL} && ${LOCAL_INSTALL}; then | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" | ||
rpm-ostree install "${HTTPS_PKGS[@]}" "${LOCAL_PKGS[@]}" | ||
fi | ||
elif [[ ${#INSTALL_PKGS[@]} -gt 0 ]]; then | ||
echo "Installing RPMs" | ||
echo_rpm_install | ||
rpm-ostree install "${INSTALL_PKGS[@]}" | ||
elif [[ ${#REMOVE_PKGS[@]} -gt 0 ]]; then | ||
echo "Removing RPMs" | ||
echo "Removing: ${REMOVE_PKGS[*]}" | ||
rpm-ostree override remove "${REMOVE_PKGS[@]}" | ||
fi | ||
|
||
get_json_array REPLACE 'try .["replace"][]' "$1" | ||
|
||
# Override-replace RPM packages | ||
if [[ ${#REPLACE[@]} -gt 0 ]]; then | ||
for REPLACEMENT in "${REPLACE[@]}"; do | ||
|
||
# Get repository | ||
REPO=$(echo "${REPLACEMENT}" | jq -r 'try .["from-repo"]') | ||
REPO="${REPO//%OS_VERSION%/${OS_VERSION}}" | ||
|
||
# Ensure repository is provided | ||
if [[ "${REPO}" == "null" ]]; then | ||
echo "Error: Key 'from-repo' was declared, but repository URL was not provided." | ||
exit 1 | ||
fi | ||
|
||
# Get info from repository URL | ||
MAINTAINER=$(awk -F'/' '{print $5}' <<< "${REPO}") | ||
REPO_NAME=$(awk -F'/' '{print $6}' <<< "${REPO}") | ||
FILE_NAME=$(awk -F'/' '{print $9}' <<< "${REPO}" | sed 's/\?.*//') # Remove params after '?' | ||
|
||
# Get packages to replace | ||
get_json_array PACKAGES 'try .["packages"][]' "${REPLACEMENT}" | ||
REPLACE_STR="$(echo "${PACKAGES[*]}" | tr -d '\n')" | ||
|
||
# Ensure packages are provided | ||
if [[ ${#PACKAGES[@]} == 0 ]]; then | ||
echo "Error: No packages were provided for repository '${REPO_NAME}'." | ||
exit 1 | ||
fi | ||
|
||
echo "Replacing packages from COPR repository: '${REPO_NAME}' owned by '${MAINTAINER}'" | ||
echo "Replacing: ${REPLACE_STR}" | ||
|
||
REPO_URL="${REPO//[$'\t\r\n ']}" | ||
|
||
echo "Downloading repo file ${REPO_URL}" | ||
curl -fLs --create-dirs -O "${REPO_URL}" --output-dir "/etc/yum.repos.d/" | ||
echo "Downloaded repo file ${REPO_URL}" | ||
|
||
rpm-ostree override replace --experimental --from "repo=copr:copr.fedorainfracloud.org:${MAINTAINER}:${REPO_NAME}" ${REPLACE_STR} | ||
rm "/etc/yum.repos.d/${FILE_NAME}" | ||
|
||
done | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import "@typespec/json-schema"; | ||
using TypeSpec.JsonSchema; | ||
|
||
@jsonSchema("/modules/dnf.json") | ||
model RpmOstreeModule { | ||
/** The dnf module offers pseudo-declarative package and repository management using dnf. | ||
* https://blue-build.org/reference/modules/dnf/ | ||
*/ | ||
type: "dnf"; | ||
|
||
/** List of links to .repo files to download into /etc/yum.repos.d/. */ | ||
repos?: Array<string>; | ||
|
||
/** List of links to key files to import for installing from custom repositories. */ | ||
keys?: Array<string>; | ||
|
||
/** List of folder names under /opt/ to enable for installing into. */ | ||
optfix?: Array<string>; | ||
|
||
/** List of RPM packages to install. */ | ||
install?: Array<string>; | ||
|
||
/** List of RPM packages to remove. */ | ||
remove?: Array<string>; | ||
|
||
/** List of configurations for `rpm-ostree override replace`ing packages. */ | ||
replace?: Array<{ | ||
/** 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>, | ||
}>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: dnf | ||
shortdesc: The dnf module offers pseudo-declarative package and repository management using dnf. | ||
example: | | ||
type: dnf | ||
repos: | ||
- copr: atim/starship | ||
gmpinder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo | ||
keys: | ||
- https://brave-browser-rpm-release.s3.brave.com/brave-core.asc | ||
optfix: | ||
- Tabby # needed because tabby installs into /opt/Tabby | ||
- brave.com | ||
install: | ||
- starship | ||
- brave-browser | ||
- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.rpm | ||
remove: | ||
- firefox | ||
- firefox-langpacks | ||
replace: | ||
- from-repo: https://copr.fedorainfracloud.org/coprs/trixieua/mutter-patched/repo/fedora-%OS_VERSION%/trixieua-mutter-patched-fedora-%OS_VERSION%.repo | ||
packages: | ||
- mutter | ||
- mutter-common | ||
- gdm |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[shellcheck] reported by reviewdog 🐶
$/$ {} is unnecessary on arithmetic variables. SC2004