Update .local/share/chezmoi/home/dot_local/bin/executable_install-program, .local/share/chezmoi/software.yml

This commit is contained in:
Brian Zalewski 2022-12-03 00:13:21 +00:00
parent 7ede3b2b7a
commit fd68a6a7ac
2 changed files with 79 additions and 81 deletions

View file

@ -48,40 +48,27 @@ function isUnicodeSupported() {
const figures = isUnicodeSupported() ? figuresDefault : figuresFallback const figures = isUnicodeSupported() ? figuresDefault : figuresFallback
function log(type, label, msg) { function log(type, label, msg) {
let icon, labelStyle, message let icon, message
if (type === 'info') { if (type === 'info') {
icon = chalk.cyanBright(this.figures.pointer) icon = chalk.cyanBright(figures.pointer)
labelStyle = chalk.bold.underline message = chalk.gray.bold(msg)
message = chalk.bold(msg)
} else if (type === 'star') { } else if (type === 'star') {
icon = chalk.yellowBright(this.figures.star) icon = chalk.yellowBright(figures.star)
labelStyle = chalk.bold.underline
message = chalk.bold(msg) message = chalk.bold(msg)
} else if (type === 'success') { } else if (type === 'success') {
icon = chalk.greenBright(this.figures.play) icon = chalk.greenBright(figures.play)
labelStyle = chalk.bold.underline message = chalk.bold(msg)
chalk.bold(msg)
} else if (type === 'warn') { } else if (type === 'warn') {
icon = `${chalk.yellowBright(this.figures.lozenge)} ${chalk.bold.black.bgYellowBright(' WARNING ')}` icon = `${chalk.yellowBright(figures.lozenge)} ${chalk.bold.black.bgYellowBright(' WARNING ')}`
labelStyle = chalk.bold.underline message = chalk.yellowBright(msg)
chalk.yellowBright(msg)
} else if (type === 'error') { } else if (type === 'error') {
icon = `${chalk.redBright(this.figures.cross)} ${chalk.black.bold.bgRedBright(' ERROR ')} + ' '}` icon = `${chalk.redBright(figures.cross)} ${chalk.black.bold.bgRedBright(' ERROR ')} + ' '}`
labelStyle = chalk.bold.underline
message = chalk.redBright(msg) message = chalk.redBright(msg)
} }
const outputMessage = `${icon} ${labelStyle(label)} ${message}` const outputMessage = `${icon} ${chalk.bold(label)} ${message}`
console.log(outputMessage) console.log(outputMessage)
} }
$.log = (entry) => {
if (entry.kind === 'cmd' && entry.cmd.substring(0, 4) === 'logg') {
//execSync(entry.cmd, {stdio: 'inherit', shell: true})
} else {
log(entry)
}
}
let installData let installData
const installOrders = {}; const installOrders = {};
const installOrdersPre = []; const installOrdersPre = [];
@ -101,21 +88,36 @@ async function downloadInstallData() {
const text = await response.text() const text = await response.text()
return YAML.parse(text) return YAML.parse(text)
} else { } else {
log('error', 'Install Data', `Failed to download the installation map`) log('error', 'Catalog Download', `Failed to download the installation map`)
} }
} }
// Creates the installOrders object which maps package managers to arrays of packages to install // Creates the installOrders object which maps package managers to arrays of packages to install
async function generateInstallOrders() { async function generateInstallOrders() {
const logStage = 'Install Orders'
const packagesToInstall = process.argv.slice(3); const packagesToInstall = process.argv.slice(3);
const installerPreference = await OSTypeInstallerKey() const installerPreference = await OSTypeInstallerKey()
log('info', 'Install Orders', `Installer preference category detected as ${installerPreference}`) log('info', logStage, `Installer preference category detected as ${installerPreference}`)
const preferenceOrder = installData.installerPreference[installerPreference]; const preferenceOrder = installData.installerPreference[installerPreference];
log('info', 'Install Orders', `Preference order acquired:`) log('info', logStage, `Preference order acquired:`)
console.log(preferenceOrder) console.log(preferenceOrder)
const softwarePackages = installData.softwarePackages; const softwarePackages = installData.softwarePackages;
pkgFor:
for (let pkg of packagesToInstall) { for (let pkg of packagesToInstall) {
let packageKey; let packageKey;
const bins = [
softwarePackages[pkg + ":" + osID] && softwarePackages[pkg + ":" + osID]['_bin'],
softwarePackages[pkg + ":" + osType] && softwarePackages[pkg + ":" + osType]['_bin'],
softwarePackages[pkg] && softwarePackages[pkg]['_bin']
]
for (const bin of bins) {
if (bin) {
const alreadyInstalled = which.sync(bin, { nothrow: true })
if (alreadyInstalled) {
continue pkgFor
}
}
}
if (softwarePackages[pkg + ":" + osID]) { if (softwarePackages[pkg + ":" + osID]) {
packageKey = pkg + ":" + osID; packageKey = pkg + ":" + osID;
} else if (softwarePackages[pkg + ":" + osType]) { } else if (softwarePackages[pkg + ":" + osType]) {
@ -123,8 +125,8 @@ async function generateInstallOrders() {
} else if (softwarePackages[pkg]) { } else if (softwarePackages[pkg]) {
packageKey = pkg packageKey = pkg
} else { } else {
log('warn', 'Install Orders', `The package \`${pkg}\` was not found in the installation map`) log('warn', logStage, `The package \`${pkg}\` was not found in the installation map`)
continue; continue
} }
for (let preference of preferenceOrder) { for (let preference of preferenceOrder) {
if (softwarePackages[packageKey][preference + ":" + osID]) { if (softwarePackages[packageKey][preference + ":" + osID]) {
@ -171,9 +173,8 @@ async function updateInstallMaps(preference, packages, scopedPreference, pkg, pa
} }
log('info', 'Install Orders', `Found a match for the package \`${pkg}\` (${packageKey} via ${scopedPreference})`) log('info', 'Install Orders', `Found a match for the package \`${pkg}\` (${packageKey} via ${scopedPreference})`)
const newPackages = packages[scopedPreference]; const newPackages = packages[scopedPreference];
installOrders[preference] = installOrders[preference].concat( const newPkgs = typeof newPackages === "string" ? [newPackages] : newPackages
typeof newPackages === "string" ? [newPackages] : newPackages installOrders[preference] = installOrders[preference].concat(newPkgs);
);
} }
// Get pre / post install hooks // Get pre / post install hooks
@ -248,9 +249,10 @@ async function releaseID() {
// Post-install hook // Post-install hook
async function afterInstall(packageManager) { async function afterInstall(packageManager) {
const logStage = 'Post-Install Package Manager'
if (packageManager === 'appimage') { if (packageManager === 'appimage') {
} else if (packageManager === 'ansible') { } else if (packageManager === 'ansible') {
log('info', 'Post-Install', `Ensuring temporary passwordless sudo privileges used by Ansible are removed`) log('info', logStage, `Ensuring temporary passwordless sudo privileges used by Ansible are removed`)
const gsed = which.sync('gsed', { nothrow: true }) const gsed = which.sync('gsed', { nothrow: true })
if (gsed) { if (gsed) {
await $`sudo gsed -i '/# TEMPORARY FOR ANSIBLE INSTALL/dg' /etc/sudoers` await $`sudo gsed -i '/# TEMPORARY FOR ANSIBLE INSTALL/dg' /etc/sudoers`
@ -289,9 +291,10 @@ async function afterInstall(packageManager) {
// Pre-install hook // Pre-install hook
async function beforeInstall(packageManager) { async function beforeInstall(packageManager) {
const logStage = 'Pre-Install Package Manager'
if (packageManager === 'appimage') { if (packageManager === 'appimage') {
} else if (packageManager === 'ansible') { } else if (packageManager === 'ansible') {
log('info', 'Pre-Install', `Temporarily enabling passwordless sudo for Ansible role installations`) log('info', logStage, `Temporarily enabling passwordless sudo for Ansible role installations`)
await $`echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR ANSIBLE INSTALL" | sudo tee -a` await $`echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR ANSIBLE INSTALL" | sudo tee -a`
} else if (packageManager === 'apk') { } else if (packageManager === 'apk') {
} else if (packageManager === 'apt') { } else if (packageManager === 'apt') {
@ -327,9 +330,9 @@ async function beforeInstall(packageManager) {
try { try {
await $`docker run --rm hello-world` await $`docker run --rm hello-world`
} catch (e) { } catch (e) {
log('warn', `The command \`docker run --rm hello-world\` failed`) log('warn', logStage, `The command \`docker run --rm hello-world\` failed`)
try { try {
log('info', 'Package Manager', 'Attempting to open \`/Applications/Docker.app\` (Docker Desktop for macOS). This should take about 30 seconds.') log('info', logStage, 'Attempting to open \`/Applications/Docker.app\` (Docker Desktop for macOS). This should take about 30 seconds.')
const promises = [$`test -d /Applications/Docker.app`, $`open /Applications/Docker.app`] const promises = [$`test -d /Applications/Docker.app`, $`open /Applications/Docker.app`]
await Promise.all(promises) await Promise.all(promises)
const gum = which.sync('gum', { nothrow: true }) const gum = which.sync('gum', { nothrow: true })
@ -339,7 +342,7 @@ async function beforeInstall(packageManager) {
await $`sleep 30` await $`sleep 30`
} }
} catch (e) { } catch (e) {
log('warn', 'Package Manager', `Docker Desktop appears to not be installed!`) log('warn', logStage, `Docker Desktop appears to not be installed!`)
} }
} }
} }
@ -350,15 +353,16 @@ async function beforeInstall(packageManager) {
} }
async function ensureInstalled(bin, callback) { async function ensureInstalled(bin, callback) {
const logStage = 'Package Manager Install'
const installed = which.sync(bin, { nothrow: true }) const installed = which.sync(bin, { nothrow: true })
if (installed) { if (installed) {
log('info', `\`${bin}\` is available`) log('info', logStage, `\`${bin}\` is available`)
} else { } else {
log('warn', `\`${bin}\` is not installed!`) log('warn', logStage, `\`${bin}\` is not installed!`)
if (callback) { if (callback) {
await callback await callback
} else { } else {
log('error', `There does not appear to be an installation method available for \`${bin}\``) log('error', logStage, `There does not appear to be an installation method available for \`${bin}\``)
} }
} }
} }
@ -368,13 +372,14 @@ async function ensurePackageManagerAnsible() {
await $`pipx inject ansible PyObjC PyObjC-core docker lxml netaddr pexpect python-vagrant pywinrm requests-credssp watchdog` await $`pipx inject ansible PyObjC PyObjC-core docker lxml netaddr pexpect python-vagrant pywinrm requests-credssp watchdog`
await $`mkdir -p "$HOME/.cache/megabyte-labs"` await $`mkdir -p "$HOME/.cache/megabyte-labs"`
await $`touch "$HOME/.cache/megabyte-labs/ansible-installed"` await $`touch "$HOME/.cache/megabyte-labs/ansible-installed"`
log('info', `Ansible and its supporting packages are now installed via pipx`) log('info', 'Package Manager Install', `Ansible and its supporting packages are now installed via pipx`)
} }
// Ensure the package manager is available // Ensure the package manager is available
let packageManagerInstalled = {}; let packageManagerInstalled = {};
async function ensurePackageManager(packageManager) { async function ensurePackageManager(packageManager) {
log('info', `Ensuring \`${packageManager}\` is set up`) const logStage = 'Package Manager Install'
log('info', logStage, `Ensuring \`${packageManager}\` is set up`)
if (packageManagerInstalled[packageManager]) { if (packageManagerInstalled[packageManager]) {
return; return;
} else { } else {
@ -398,7 +403,7 @@ async function ensurePackageManager(packageManager) {
await $`test -f "$HOME/.cache/megabyte-labs/ansible-installed"` await $`test -f "$HOME/.cache/megabyte-labs/ansible-installed"`
const ansible = which.sync('ansible', { nothrow: true }) const ansible = which.sync('ansible', { nothrow: true })
if (ansible) { if (ansible) {
log('info', `\`ansible\` and its supporting packages appear to be installed`) log('info', logStage, `\`ansible\` and its supporting packages appear to be installed`)
} else { } else {
await ensurePackageManagerAnsible() await ensurePackageManagerAnsible()
} }
@ -427,11 +432,11 @@ async function ensurePackageManager(packageManager) {
if command -v sudo > /dev/null && sudo -n true; then if command -v sudo > /dev/null && sudo -n true; then
echo | bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" echo | bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
else else
log('info', 'Package Manager', 'Homebrew is not installed. Password may be required.') log('info', logStage, 'Homebrew is not installed. Password may be required.')
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || BREW_EXIT_CODE="$?" bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || BREW_EXIT_CODE="$?"
if [ -n "$BREW_EXIT_CODE" ]; then if [ -n "$BREW_EXIT_CODE" ]; then
if command -v brew > /dev/null; then if command -v brew > /dev/null; then
log('warn', 'Package Manager', 'Homebrew was installed but part of the installation failed. Attempting to fix..') log('warn', logStage, 'Homebrew was installed but part of the installation failed. Attempting to fix..')
BREW_DIRS="share/man share/doc share/zsh/site-functions etc/bash_completion.d" BREW_DIRS="share/man share/doc share/zsh/site-functions etc/bash_completion.d"
for BREW_DIR in $BREW_DIRS; do for BREW_DIR in $BREW_DIRS; do
if [ -d "$(brew --prefix)/$BREW_DIR" ]; then if [ -d "$(brew --prefix)/$BREW_DIR" ]; then
@ -462,16 +467,16 @@ async function ensurePackageManager(packageManager) {
const dnf = which.sync('dnf', { nothrow: true }) const dnf = which.sync('dnf', { nothrow: true })
const yum = which.sync('yum', { nothrow: true }) const yum = which.sync('yum', { nothrow: true })
if (dnf) { if (dnf) {
log('info', 'Package Manager', `\`dnf\` is available`) log('info', logStage, `\`dnf\` is available`)
} else if (yum) { } else if (yum) {
log('info', 'Package Manager', `\`yum\` is available`) log('info', logStage, `\`yum\` is available`)
} else { } else {
log('error', 'Package Manager', `Both \`dnf\` and \`yum\` are not available`) log('error', logStage, `Both \`dnf\` and \`yum\` are not available`)
} }
} else if (packageManager === 'flatpak') { } else if (packageManager === 'flatpak') {
const flatpak = which.sync('flatpak', { nothrow: true }) const flatpak = which.sync('flatpak', { nothrow: true })
if (flatpak) { if (flatpak) {
log('info', 'Package Manager', `\`flatpak\` is available`) log('info', logStage, `\`flatpak\` is available`)
} else { } else {
const apk = which.sync('apk', { nothrow: true }) const apk = which.sync('apk', { nothrow: true })
const apt = which.sync('apt', { nothrow: true }) const apt = which.sync('apt', { nothrow: true })
@ -504,9 +509,9 @@ async function ensurePackageManager(packageManager) {
if (flatpakPost) { if (flatpakPost) {
await $`flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo` await $`flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo`
} else { } else {
log('error', 'Package Manager', `\`flatpak\` failed to install!`) log('error', logStage, `\`flatpak\` failed to install!`)
} }
log('info', 'Package Manager', `\`flatpak\` was installed. It may require a reboot to function correctly.`) log('info', logStage, `\`flatpak\` was installed. It may require a reboot to function correctly.`)
} }
} else if (packageManager === 'gem') { } else if (packageManager === 'gem') {
await ensureInstalled('gem', $`brew install ruby`) await ensureInstalled('gem', $`brew install ruby`)
@ -525,7 +530,7 @@ async function ensurePackageManager(packageManager) {
const node = which('node', { nothrow: true }) const node = which('node', { nothrow: true })
const volta = which('volta', { nothrow: true }) const volta = which('volta', { nothrow: true })
if (npm && node && volta) { if (npm && node && volta) {
log('info', 'Package Manager', `\`npm\`, \`node\`, and \`volta\` are available`) log('info', logStage, `\`npm\`, \`node\`, and \`volta\` are available`)
} else { } else {
if (!volta) { if (!volta) {
await $`brew install volta` await $`brew install volta`
@ -631,6 +636,7 @@ async function ensurePackageManager(packageManager) {
// Installs a list of packages via the specified package manager // Installs a list of packages via the specified package manager
async function installPackageList(packageManager, packages) { async function installPackageList(packageManager, packages) {
const logStage = 'Package Install'
let pkg = packages let pkg = packages
try { try {
if (packageManager === 'appimage') { if (packageManager === 'appimage') {
@ -730,15 +736,15 @@ async function installPackageList(packageManager, packages) {
} }
} }
} catch (e) { } catch (e) {
log('error', 'Package Install', `Possibly encountered an error while installing via \`${packageManager}\``) log('error', logStage, `Possibly encountered an error while installing via \`${packageManager}\``)
log('info', 'Package Install', `Error was encountered while installing: ${pkg}`) log('info', logStage, `Error was encountered while installing: ${pkg}`)
log('info', 'Package Install', `Proceeding with the installation..`) log('info', logStage, `Proceeding with the installation..`)
} }
} }
// main process // main process
async function main() { async function main() {
log('info', `Fetching the latest version of the installation map`) log('info', 'Catalog Download', `Fetching the latest version of the installation map`)
installData = await downloadInstallData(); installData = await downloadInstallData();
log('info', 'Install Orders', `Calculating the install orders`) log('info', 'Install Orders', `Calculating the install orders`)
await generateInstallOrders(); await generateInstallOrders();
@ -749,7 +755,7 @@ async function main() {
} }
log('info', 'Install Orders', `The install orders were generated:`) log('info', 'Install Orders', `The install orders were generated:`)
console.log(installOrders) console.log(installOrders)
log('info', 'Package Manager', `Running package manager pre-installation steps`) log('info', 'Package Manager Pre-Install', `Running package manager pre-installation steps`)
for (const packageManager of packageManagers) { for (const packageManager of packageManagers) {
await beforeInstall(packageManager); await beforeInstall(packageManager);
} }
@ -771,7 +777,7 @@ async function main() {
for (const script of installOrdersPost) { for (const script of installOrdersPost) {
await $`${script}`; await $`${script}`;
} }
log('info', 'Post-Install Package Manager', `Running package manager post-installation steps`) log('info', 'Package Manager Post-Install', `Running package manager post-installation steps`)
for (const packageManager of packageManagers) { for (const packageManager of packageManagers) {
await afterInstall(packageManager); await afterInstall(packageManager);
} }

View file

@ -159,7 +159,7 @@ installerPreference:
softwarePackages: softwarePackages:
act: act:
_bin: null _bin: act
_desc: null _desc: null
_docs: null _docs: null
_github: null _github: null
@ -173,7 +173,7 @@ softwarePackages:
scoop: act scoop: act
yay: act yay: act
allure: allure:
_bin: null _bin: allure
_desc: null _desc: null
_docs: null _docs: null
_github: null _github: null
@ -203,28 +203,20 @@ softwarePackages:
snap: altair snap: altair
yay: altair yay: altair
android-studio: android-studio:
_bin: null
_desc: null
_docs: null
_github: null
_home: null
_name: null
_service: null
brew: android-studio
choco: androidstudio
snap: android-studio
yay: android-studio
androidstudio:
_bin: null _bin: null
_desc: >- _desc: >-
[Android Studio](https://developer.android.com/studio) is the official integrated development environment for Google's Android operating system, built on JetBrains' IntelliJ IDEA software and designed specifically for Android development. This role installs Android Studio on nearly any operating system and also ensures a configurable list of command-line tools and SDKs are installed and seamlessly integrated with the system (i.e. the role adds the appropriate items to the `PATH` environment [Android Studio](https://developer.android.com/studio) is the official integrated development environment for Google's Android operating system, built on JetBrains' IntelliJ IDEA software and designed specifically for Android development. This role installs Android Studio on nearly any operating system and also ensures a configurable list of command-line tools and SDKs are installed and seamlessly integrated with the system (i.e. the role adds the appropriate items to the `PATH` environment
variable). variable).
_docs: https://developer.android.com/docs _docs: https://developer.android.com/docs
_github: Not open-source _github: null
_home: https://developer.android.com/studio _home: https://developer.android.com/studio
_name: Android Studio _name: Android Studio
_service: null _service: null
ansible: professormanhattan.androidstudio ansible: professormanhattan.androidstudio
brew: android-studio
choco: androidstudio
snap: android-studio
yay: android-studio
angular-cli: angular-cli:
_bin: ng _bin: ng
_desc: null _desc: null
@ -236,7 +228,7 @@ softwarePackages:
brew: angular-cli brew: angular-cli
npm: '@angular/cli' npm: '@angular/cli'
ansible: ansible:
_bin: null _bin: ansible
_desc: null _desc: null
_docs: null _docs: null
_github: null _github: null
@ -247,7 +239,7 @@ softwarePackages:
dnf: ansible dnf: ansible
pipx: ansible pipx: ansible
ansible-lint: ansible-lint:
_bin: null _bin: ansible-lint
_desc: null _desc: null
_docs: null _docs: null
_github: null _github: null
@ -256,7 +248,7 @@ softwarePackages:
_service: null _service: null
pipx: ansible-lint pipx: ansible-lint
ansibleconnect: ansibleconnect:
_bin: null _bin: ansibleconnect
_deps: _deps:
- sshpass - sshpass
- tmux - tmux
@ -338,7 +330,7 @@ softwarePackages:
_service: null _service: null
pipx: asciicinema pipx: asciicinema
asdf: asdf:
_bin: null _bin: asdf
_desc: '[asdf](https://asdf-vm.com/#/) is a CLI tool that can manage multiple language runtime versions on a per-project basis or globally. It is like gvm, nvm, rbenv, and pyenv all in one. This role installs asdf on Linux or macOS.' _desc: '[asdf](https://asdf-vm.com/#/) is a CLI tool that can manage multiple language runtime versions on a per-project basis or globally. It is like gvm, nvm, rbenv, and pyenv all in one. This role installs asdf on Linux or macOS.'
_docs: https://asdf-vm.com/guide/introduction.html _docs: https://asdf-vm.com/guide/introduction.html
_github: https://github.com/asdf-vm/asdf _github: https://github.com/asdf-vm/asdf
@ -523,7 +515,7 @@ softwarePackages:
_service: null _service: null
brew: bash-completion brew: bash-completion
bat: bat:
_bin: null _bin: bat
_desc: '[bat](https://github.com/sharkdp/bat) is a cat(1) clone with syntax highlighting and Git integration.' _desc: '[bat](https://github.com/sharkdp/bat) is a cat(1) clone with syntax highlighting and Git integration.'
_docs: null _docs: null
_github: https://github.com/sharkdp/bat _github: https://github.com/sharkdp/bat
@ -3132,7 +3124,7 @@ softwarePackages:
snap: jo snap: jo
yay: jo yay: jo
jq: jq:
_bin: null _bin: jq
_desc: '[jq](https://github.com/stedolan/jq) is like sed for JSON data. You can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.' _desc: '[jq](https://github.com/stedolan/jq) is like sed for JSON data. You can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.'
_docs: null _docs: null
_github: https://github.com/stedolan/jq _github: https://github.com/stedolan/jq
@ -3926,12 +3918,12 @@ softwarePackages:
_service: null _service: null
npm: nativefier npm: nativefier
nb: nb:
_bin: null _bin: nb
_deps: _deps:
- bat - bat
- netcat - netcat
- pandoc - pandoc
- rg - ripgrep
- tig - tig
- w3m - w3m
_desc: '[nb](https://xwmx.github.io/nb) is a command line and local web notetaking, bookmarking, archiving, and knowledge base application with plain text data storage, encryption, filtering, pinning, #tagging, search, Git-backed versioning and syncing, Pandoc-backed conversion and many more features.' _desc: '[nb](https://xwmx.github.io/nb) is a command line and local web notetaking, bookmarking, archiving, and knowledge base application with plain text data storage, encryption, filtering, pinning, #tagging, search, Git-backed versioning and syncing, Pandoc-backed conversion and many more features.'
@ -6042,7 +6034,7 @@ softwarePackages:
_service: null _service: null
ansible: professormanhattan.theme ansible: professormanhattan.theme
tig: tig:
_bin: null _bin: tig
_desc: null _desc: null
_docs: null _docs: null
_github: null _github: null