├── .gitignore ├── modules ├── util │ └── manifests │ │ ├── init.pp │ │ ├── pip.pp │ │ └── virtualenv.pp ├── packages │ └── manifests │ │ ├── init.pp │ │ ├── vlc.pp │ │ ├── editors.pp │ │ ├── podman.pp │ │ ├── arduino.pp │ │ ├── gnome_terminal.pp │ │ ├── debian_packaging.pp │ │ ├── utilities.pp │ │ ├── firefox.pp │ │ ├── build_deps.pp │ │ └── python.pp ├── gdebi │ ├── manifests │ │ └── init.pp │ └── lib │ │ └── puppet │ │ └── provider │ │ └── package │ │ └── gdebi.rb ├── desktop │ ├── manifests │ │ ├── keyboard.pp │ │ ├── wayland.pp │ │ ├── purged.pp │ │ ├── podman.pp │ │ ├── sshd.pp │ │ ├── touchpad.pp │ │ ├── arduino.pp │ │ ├── background.pp │ │ ├── screensaver.pp │ │ ├── virtualbox.pp │ │ ├── homedir.pp │ │ ├── pidgin.pp │ │ ├── launcher.pp │ │ ├── init.pp │ │ ├── go.pp │ │ ├── pypy.pp │ │ ├── packer.pp │ │ ├── terraform.pp │ │ ├── node.pp │ │ ├── dotfiles.pp │ │ ├── venv.pp │ │ └── workspace.pp │ └── files │ │ ├── xorg │ │ └── 99-synaptics.conf │ │ └── pidgin │ │ ├── blist.xml │ │ └── accounts.xml └── gsettings │ └── lib │ └── puppet │ ├── type │ └── gsetting.rb │ └── provider │ └── gsetting │ └── default.rb ├── manifests └── site.pp ├── hiera.yaml ├── Puppetfile ├── README.md ├── setup.cfg ├── .rubocop.yml ├── data └── arch │ ├── amd64.yaml │ └── aarch64.yaml ├── LICENSE ├── .pre-commit-config.yaml └── run-puppet /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | -------------------------------------------------------------------------------- /modules/util/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class util {} 2 | -------------------------------------------------------------------------------- /modules/packages/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class packages {} 2 | -------------------------------------------------------------------------------- /modules/gdebi/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class gdebi { 2 | package { 'gdebi-core': ensure => 'latest' } 3 | } 4 | -------------------------------------------------------------------------------- /manifests/site.pp: -------------------------------------------------------------------------------- 1 | node default { 2 | require ::apt 3 | require ::gdebi 4 | 5 | include desktop 6 | } 7 | -------------------------------------------------------------------------------- /modules/packages/manifests/vlc.pp: -------------------------------------------------------------------------------- 1 | class packages::vlc { 2 | package { 'vlc': ensure => 'latest' } 3 | } 4 | -------------------------------------------------------------------------------- /hiera.yaml: -------------------------------------------------------------------------------- 1 | version: 5 2 | hierarchy: 3 | - name: "by architecture" 4 | path: arch/%{facts.os.architecture}.yaml 5 | -------------------------------------------------------------------------------- /modules/packages/manifests/editors.pp: -------------------------------------------------------------------------------- 1 | class packages::editors { 2 | package { ['nano', 'vim-nox']: ensure => 'latest' } 3 | } 4 | -------------------------------------------------------------------------------- /modules/packages/manifests/podman.pp: -------------------------------------------------------------------------------- 1 | class packages::podman { 2 | package { ['buildah', 'podman', 'fuse-overlayfs'] : ensure => 'latest' } 3 | } 4 | -------------------------------------------------------------------------------- /Puppetfile: -------------------------------------------------------------------------------- 1 | moduledir 'vendor' 2 | 3 | mod 'puppet-archive', '8.1.0' 4 | mod 'puppetlabs-apt', '11.1.0' 5 | mod 'puppetlabs-stdlib', '9.7.0' 6 | mod 'puppetlabs-vcsrepo', '7.0.0' 7 | -------------------------------------------------------------------------------- /modules/packages/manifests/arduino.pp: -------------------------------------------------------------------------------- 1 | class packages::arduino { 2 | $packages = ['arduino-core-avr', 'avr-libc', 'avrdude', 'gcc-avr'] 3 | package { $packages : ensure => 'latest'} 4 | } 5 | -------------------------------------------------------------------------------- /modules/desktop/manifests/keyboard.pp: -------------------------------------------------------------------------------- 1 | class desktop::keyboard { 2 | gsetting { 'org.gnome.desktop.input-sources xkb-options': 3 | ensure => ['caps:none'], 4 | user => 'asottile', 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/desktop/manifests/wayland.pp: -------------------------------------------------------------------------------- 1 | class desktop::wayland { 2 | file { '/etc/gdm3/custom.conf': 3 | ensure => 'file', 4 | content => "[daemon]\nWaylandEnable=false\n", 5 | mode => '0644', 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /modules/packages/manifests/gnome_terminal.pp: -------------------------------------------------------------------------------- 1 | class packages::gnome_terminal { 2 | apt::ppa { 'ppa:asottile/gnome-terminal-tab-tearing': } -> 3 | package { ['gnome-terminal', 'gnome-terminal-data']: ensure => 'latest' } 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/asottile/personal-puppet/main.svg)](https://results.pre-commit.ci/latest/github/asottile/personal-puppet/main) 2 | 3 | personal-puppet 4 | =============== 5 | -------------------------------------------------------------------------------- /modules/desktop/manifests/purged.pp: -------------------------------------------------------------------------------- 1 | class desktop::purged { 2 | $purged = [ 3 | 'command-not-found', 'gitk', 'libreoffice-common', 4 | 'printer-driver-foo2zjs', 'snapd', 5 | ] 6 | package {$purged: ensure => 'purged'} 7 | } 8 | -------------------------------------------------------------------------------- /modules/packages/manifests/debian_packaging.pp: -------------------------------------------------------------------------------- 1 | class packages::debian_packaging { 2 | package {'aptitude': ensure => 'latest' } 3 | package {'devscripts': ensure => 'latest' } 4 | package {'git-buildpackage': ensure => 'latest' } 5 | } 6 | -------------------------------------------------------------------------------- /modules/desktop/files/xorg/99-synaptics.conf: -------------------------------------------------------------------------------- 1 | Section "InputClass" 2 | Identifier "Make apples touchpad scroll less wild" 3 | MatchProduct "Apple|bcm5974" 4 | MatchDriver "synaptics" 5 | Option "CoastingSpeed" "120" 6 | Option "CoastingFriction" "200" 7 | EndSection 8 | -------------------------------------------------------------------------------- /modules/desktop/manifests/podman.pp: -------------------------------------------------------------------------------- 1 | class desktop::podman { 2 | file { '/home/asottile/bin/docker': 3 | ensure => 'link', 4 | target => '/usr/bin/podman', 5 | owner => 'asottile', 6 | group => 'asottile', 7 | require => [Package['podman'], File['/home/asottile/bin']], 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /modules/packages/manifests/utilities.pp: -------------------------------------------------------------------------------- 1 | class packages::utilities { 2 | $tools = [ 3 | 'curl', 'dos2unix', 'ffmpeg', 'graphviz', 'inotify-tools', 'jq', 'ncdu', 4 | 'neofetch', 'net-tools', 'plocate', 'rlwrap', 'sqlite3', 'tmux', 'tree', 5 | 'xclip', 6 | ] 7 | package { $tools: ensure => 'latest' } 8 | } 9 | -------------------------------------------------------------------------------- /modules/util/manifests/pip.pp: -------------------------------------------------------------------------------- 1 | define util::pip(String $pkg, String $venv) { 2 | $pkg_re = regsubst($pkg, '-', '[-_]') 3 | exec { "pip install ${pkg} into ${venv}": 4 | command => "${venv}/bin/pip install ${pkg}", 5 | unless => "${venv}/bin/pip freeze | grep '^${pkg_re}=='", 6 | user => 'asottile', 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [mypy] 2 | check_untyped_defs = true 3 | disallow_any_generics = true 4 | disallow_incomplete_defs = true 5 | disallow_untyped_defs = true 6 | warn_redundant_casts = true 7 | warn_unused_ignores = true 8 | 9 | [mypy-testing.*] 10 | disallow_untyped_defs = false 11 | 12 | [mypy-tests.*] 13 | disallow_untyped_defs = false 14 | -------------------------------------------------------------------------------- /modules/desktop/manifests/sshd.pp: -------------------------------------------------------------------------------- 1 | class desktop::sshd { 2 | # if I install sshd, don't accidentally enable password auth 3 | file { ['/etc/ssh', '/etc/ssh/sshd_config.d']: ensure => 'directory' } -> 4 | file { '/etc/ssh/sshd_config.d/no-password-auth.conf': 5 | ensure => 'file', 6 | content => 'PasswordAuthentication no', 7 | mode => '0644', 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | NewCops: enable 3 | TargetRubyVersion: '2.6' 4 | 5 | Layout: 6 | Metrics/LineLength: 7 | IgnoreCopDirectives: true 8 | 9 | Metrics/AbcSize: 10 | Enabled: false 11 | Metrics/BlockLength: 12 | Max: 30 13 | Metrics/CyclomaticComplexity: 14 | Enabled: false 15 | Metrics/MethodLength: 16 | Max: 30 17 | Naming/MethodParameterName: 18 | Enabled: false 19 | -------------------------------------------------------------------------------- /modules/desktop/manifests/touchpad.pp: -------------------------------------------------------------------------------- 1 | class desktop::touchpad { 2 | file { '/etc/X11/xorg.conf.d': ensure => 'directory' } -> 3 | file { '/etc/X11/xorg.conf.d/99-synaptics.conf': 4 | source => 'puppet:///modules/desktop/xorg/99-synaptics.conf' 5 | } 6 | 7 | gsetting { 'org.gnome.desktop.peripherals.touchpad natural-scroll': 8 | ensure => ':false', 9 | user => 'asottile', 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /modules/desktop/manifests/arduino.pp: -------------------------------------------------------------------------------- 1 | class desktop::arduino { 2 | include packages::arduino 3 | 4 | exec { 'add asottile to dialout group': 5 | command => 'usermod --append --groups dialout asottile', 6 | unless => join([ 7 | 'id --name --groups --zero asottile | ', 8 | 'grep --quiet --null-data --line-regexp dialout', 9 | ]), 10 | path => '/usr/sbin:/usr/bin:/bin', 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /modules/packages/manifests/firefox.pp: -------------------------------------------------------------------------------- 1 | class packages::firefox { 2 | apt::ppa { 'ppa:mozillateam/ppa': } -> 3 | apt::pin { 'firefox-ppa-pin': 4 | originator => 'LP-PPA-mozillateam', 5 | priority => 700, 6 | } -> 7 | package { 'firefox': 8 | ensure => 'latest', 9 | require => Exec['apt_update'], 10 | } 11 | 12 | # unattended-upgrades does not respect apt pinning 13 | package { 'unattended-upgrades': ensure => 'purged' } 14 | } 15 | -------------------------------------------------------------------------------- /modules/desktop/manifests/background.pp: -------------------------------------------------------------------------------- 1 | class desktop::background { 2 | $bg = 'file:///usr/share/backgrounds/Twilight_Frost_by_Phil_Jackson.jpg' 3 | 4 | package { 'ubuntu-wallpapers-precise': ensure => 'present' } -> 5 | gsetting { 'org.gnome.desktop.background picture-uri': 6 | ensure => $bg, 7 | user => 'asottile', 8 | } -> 9 | gsetting { 'org.gnome.desktop.screensaver picture-uri': 10 | ensure => $bg, 11 | user => 'asottile', 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/desktop/manifests/screensaver.pp: -------------------------------------------------------------------------------- 1 | class desktop::screensaver { 2 | gsetting { 'org.gnome.desktop.session idle-delay': 3 | ensure => 0, 4 | user => 'asottile', 5 | } 6 | gsetting { 'org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type': 7 | ensure => 'nothing', 8 | user => 'asottile', 9 | } 10 | gsetting { 'org.gnome.desktop.screensaver lock-enabled': 11 | ensure => ':false', 12 | user => 'asottile', 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/desktop/manifests/virtualbox.pp: -------------------------------------------------------------------------------- 1 | class desktop::virtualbox { 2 | if $facts['is_virtual'] and $facts['virtual'] == 'virtualbox' { 3 | exec { 'add asottile to vboxsf group': 4 | command => 'usermod --append --groups vboxsf asottile', 5 | unless => join([ 6 | 'id --name --groups --zero asottile | ', 7 | 'grep --quiet --null-data --line-regexp vboxsf', 8 | ]), 9 | path => '/usr/sbin:/usr/bin:/bin', 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /modules/packages/manifests/build_deps.pp: -------------------------------------------------------------------------------- 1 | class packages::build_deps { 2 | $deps = [ 3 | # git 4 | 'libcurl4-openssl-dev', 'libssl-dev', 'zlib1g-dev', 5 | # cpython 6 | 'libffi-dev', 'libreadline-dev', 'libsqlite3-dev', 7 | # oniguruma 8 | 'libtool', 9 | # pyyaml 10 | 'libyaml-dev', 11 | # pypy 12 | 'pkg-config', 13 | # scss_lint 14 | 'ruby-dev', 15 | # tmux 16 | 'autoconf', 'bison', 'libevent-dev', 17 | ] 18 | package { $deps: ensure => 'latest' } 19 | } 20 | -------------------------------------------------------------------------------- /modules/desktop/manifests/homedir.pp: -------------------------------------------------------------------------------- 1 | class desktop::homedir { 2 | file { [ 3 | '/home/asottile/Documents', 4 | '/home/asottile/Pictures', 5 | '/home/asottile/Public', 6 | '/home/asottile/Templates', 7 | '/home/asottile/Videos', 8 | '/home/asottile/snap', 9 | ]: 10 | ensure => 'absent', 11 | recurse => true, 12 | force => true, 13 | } 14 | 15 | file { ['/home/asottile/bin', '/home/asottile/opt']: 16 | ensure => 'directory', 17 | owner => 'asottile', 18 | group => 'asottile', 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/desktop/manifests/pidgin.pp: -------------------------------------------------------------------------------- 1 | class desktop::pidgin { 2 | package { ['pidgin', 'pidgin-plugin-pack']: ensure => 'latest' } -> 3 | file { '/home/asottile/.purple': 4 | ensure => 'directory', 5 | owner => 'asottile', 6 | group => 'asottile', 7 | mode => '0700', 8 | } -> 9 | file { 10 | default: 11 | owner => 'asottile', 12 | group => 'asottile', 13 | mode => '0600'; 14 | 15 | '/home/asottile/.purple/accounts.xml': 16 | source => 'puppet:///modules/desktop/pidgin/accounts.xml'; 17 | 18 | '/home/asottile/.purple/blist.xml': 19 | source => 'puppet:///modules/desktop/pidgin/blist.xml'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modules/packages/manifests/python.pp: -------------------------------------------------------------------------------- 1 | class packages::python { 2 | package { 'python3-dev': ensure => 'latest' } -> 3 | file { '/etc/python3.12/sitecustomize.py': 4 | ensure => present, 5 | content => '', 6 | } 7 | 8 | $deadsnakes_pkgs = [ 9 | 'python3.7-dev', 'python3.7-distutils', 10 | 'python3.8-dev', 'python3.8-distutils', 11 | 'python3.9-dev', 'python3.9-distutils', 12 | 'python3.10-dev', 'python3.10-distutils', 13 | 'python3.11-dev', 14 | 'python3.13-dev', 15 | 'python3.14-dev', 16 | 'python3.15-dev', 17 | ] 18 | apt::ppa { 'ppa:deadsnakes/ppa': } -> 19 | package { $deadsnakes_pkgs: 20 | ensure => 'latest', 21 | require => Exec['apt_update'], 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /modules/desktop/files/pidgin/blist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1 7 | 8 | #rebuild 9 | 1 10 | 11 | 12 | #webcore3 13 | 1 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /modules/util/manifests/virtualenv.pp: -------------------------------------------------------------------------------- 1 | define util::virtualenv(String $venv) { 2 | exec { "create ${venv}": 3 | command => join([ 4 | "rm -rf ${venv} && ", 5 | 'curl --silent --location --output /tmp/virtualenv.pyz https://bootstrap.pypa.io/virtualenv.pyz && ', 6 | "python3 /tmp/virtualenv.pyz ${venv} && ", 7 | 'rm -rf /tmp/virtualenv.pyz', 8 | ]), 9 | unless => join([ 10 | "test -x ${venv}/bin/python && ", 11 | "info=\"$(${venv}/bin/python -S -c 'import sys;print(\".\".join(str(p) for p in sys.version_info))')\" && ", 12 | "grep \"^version_info = \$info$\" ${venv}/pyvenv.cfg", 13 | ]), 14 | user => 'asottile', 15 | path => '/usr/sbin:/usr/bin:/bin', 16 | require => [Package['curl'], File[dirname($venv)]], 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /modules/desktop/manifests/launcher.pp: -------------------------------------------------------------------------------- 1 | class desktop::launcher { 2 | gsetting { 'org.gnome.shell favorite-apps': 3 | ensure => [ 4 | 'org.gnome.Nautilus.desktop', 5 | 'org.gnome.Terminal.desktop', 6 | 'firefox.desktop', 7 | 'pidgin.desktop', 8 | 'gnome-control-center.desktop', 9 | ], 10 | user => 'asottile', 11 | require => [Package['pidgin']], 12 | } 13 | gsetting { 'org.gnome.shell.extensions.dash-to-dock multi-monitor': 14 | ensure => ':true', 15 | user => 'asottile', 16 | } 17 | gsetting { 'org.gnome.shell.extensions.dash-to-dock dock-position': 18 | ensure => 'RIGHT', 19 | user => 'asottile', 20 | } 21 | gsetting { 'org.gnome.shell.extensions.dash-to-dock show-mounts': 22 | ensure => ':false', 23 | user => 'asottile', 24 | } 25 | gsetting { 'org.gnome.shell.extensions.ding show-home': 26 | ensure => ':false', 27 | user => 'asottile', 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /modules/gdebi/lib/puppet/provider/package/gdebi.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cgi' 4 | require 'digest' 5 | require 'open-uri' 6 | 7 | def download_and_verify(uri, path, sha256) 8 | File.open(path, 'wb') do |io| 9 | uri.open do |f| 10 | io.write f.read 11 | end 12 | end 13 | 14 | sum = Digest::SHA256.file path 15 | return if sum.hexdigest == sha256 16 | 17 | raise "checksum mismatch #{sha256} #{sum.hexdigest}" 18 | end 19 | 20 | Puppet::Type.type(:package).provide :gdebi, parent: :dpkg do 21 | has_feature :versionable 22 | 23 | commands gdebi: '/usr/bin/gdebi' 24 | 25 | def install 26 | raise ArgumentError, 'Specify url as `source`' unless @resource[:source] 27 | 28 | uri = URI.parse(@resource[:source]) 29 | sha256 = CGI.parse(uri.fragment)['sha256'][0] 30 | Dir.mktmpdir('gdebi-download') do |dir| 31 | deb = File.join(dir, "#{@resource[:name]}.deb") 32 | download_and_verify(uri, deb, sha256) 33 | gdebi('-n', deb) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /modules/desktop/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class desktop { 2 | include desktop::arduino 3 | include desktop::background 4 | include desktop::dotfiles 5 | include desktop::go 6 | include desktop::homedir 7 | include desktop::keyboard 8 | include desktop::launcher 9 | include desktop::node 10 | include desktop::packer 11 | include desktop::pidgin 12 | include desktop::podman 13 | include desktop::purged 14 | include desktop::pypy 15 | include desktop::screensaver 16 | include desktop::sshd 17 | include desktop::terraform 18 | include desktop::touchpad 19 | include desktop::venv 20 | include desktop::virtualbox 21 | include desktop::wayland 22 | include desktop::workspace 23 | 24 | include packages::debian_packaging 25 | include packages::build_deps 26 | include packages::editors 27 | include packages::firefox 28 | include packages::gnome_terminal 29 | include packages::podman 30 | include packages::python 31 | include packages::utilities 32 | 33 | if ! $facts['is_virtual'] { 34 | include packages::vlc 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /data/arch/amd64.yaml: -------------------------------------------------------------------------------- 1 | # https://golang.org/dl/ 2 | desktop::go::go: 'go1.23.4.linux-amd64' 3 | desktop::go::sha256: '6924efde5de86fe277676e929dc9917d466efa02fb934197bc2eba35d5680971' 4 | 5 | # https://nodejs.org/dist 6 | desktop::node::node: 'v18.19.0' 7 | desktop::node::archive_name: 'node-v18.19.0-linux-x64' 8 | desktop::node::sha256: '61632bb78ee828d6e8f42adc0bc2238a6b8200007093988d3927176a372281e8' 9 | 10 | # https://releases.hashicorp.com/packer/ 11 | desktop::packer::version: '1.10.0' 12 | desktop::packer::archive_name: 'packer_1.10.0_linux_amd64' 13 | desktop::packer::sha256: 'a8442e7041db0a7db48f468e353ee07fa6a7b35276ec62f60813c518ca3296c1' 14 | 15 | # https://pypy.org/download.html 16 | desktop::pypy::pypy3: 'pypy3.11-v7.3.20-linux64' 17 | desktop::pypy::sha256: '1410db3a7ae47603e2b7cbfd7ff6390b891b2e041c9eb4f1599f333677bccb3e' 18 | 19 | # https://www.terraform.io/downloads.html 20 | desktop::terraform::version: '1.3.2' 21 | desktop::terraform::archive_name: 'terraform_1.3.2_linux_amd64' 22 | desktop::terraform::sha256: '6372e02a7f04bef9dac4a7a12f4580a0ad96a37b5997e80738e070be330cb11c' 23 | -------------------------------------------------------------------------------- /data/arch/aarch64.yaml: -------------------------------------------------------------------------------- 1 | # https://golang.org/dl/ 2 | desktop::go::go: 'go1.23.4.linux-arm64' 3 | desktop::go::sha256: '16e5017863a7f6071363782b1b8042eb12c6ca4f4cd71528b2123f0a1275b13e' 4 | 5 | # https://nodejs.org/dist 6 | desktop::node::node: 'v18.19.0' 7 | desktop::node::archive_name: 'node-v18.19.0-linux-arm64' 8 | desktop::node::sha256: 'cf94ab72e45b855257545fec1c017bdf30a9e23611561382eaf64576b999e72d' 9 | 10 | # https://releases.hashicorp.com/packer/ 11 | desktop::packer::version: '1.10.0' 12 | desktop::packer::archive_name: 'packer_1.10.0_linux_arm64' 13 | desktop::packer::sha256: '37149ec9ca322aa50f249a645e43e67498b67e785b356c59f9b0f0e6519da78e' 14 | 15 | # https://pypy.org/download.html 16 | desktop::pypy::pypy3: 'pypy3.11-v7.3.20-aarch64' 17 | desktop::pypy::sha256: '9347fe691a07fd9df17a1b186554fb9d9e6210178ffef19520a579ce1f9eb741' 18 | 19 | # https://releases.hashicorp.com/terraform/ 20 | desktop::terraform::version: '1.3.2' 21 | desktop::terraform::archive_name: 'terraform_1.3.2_linux_arm64' 22 | desktop::terraform::sha256: 'ce1a8770aaf27736a3352c5c31e95fb10d0944729b9d81013bf6848f8657da5f' 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Anthony Sottile 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /modules/desktop/files/pidgin/accounts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | prpl-irc 6 | asottile@irc.ocf.berkeley.edu 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 6697 22 | 0 23 | 0 24 | UTF-8 25 | 0 26 | 1 27 | 28 | 29 | 1 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /modules/gsettings/lib/puppet/type/gsetting.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | NAME_RE = /^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)* [a-zA-Z0-9_-]+$/.freeze 4 | 5 | def _bool_to_puppet(b) 6 | { true => ':true', false => ':false', nil => ':nil' }.fetch(b, b) 7 | end 8 | 9 | def _puppet_to_bool(s) 10 | { ':true' => true, ':false' => false, ':nil' => nil }.fetch(s, s) 11 | end 12 | 13 | Puppet::Type.newtype(:gsetting) do 14 | newparam(:name, namevar: true) do 15 | desc 'In the form: `SCHEMA KEY`' 16 | 17 | validate do |value| 18 | raise ArgumentError, "#{value} must be `SCHEMA KEY`" unless value.is_a?(String) && value =~ NAME_RE 19 | end 20 | end 21 | 22 | newproperty(:ensure) do 23 | def sync 24 | @resource.provider.set 25 | end 26 | 27 | def retrieve 28 | _bool_to_puppet(@resource.provider.get) 29 | end 30 | 31 | def insync?(is) 32 | @rawvalue == is 33 | end 34 | 35 | def should_to_s(*) 36 | self.class.format_value_for_display(@rawvalue) 37 | end 38 | 39 | def value 40 | _puppet_to_bool(@rawvalue) 41 | end 42 | 43 | def value=(x) 44 | @rawvalue = x 45 | super 46 | end 47 | end 48 | 49 | newparam(:user) 50 | end 51 | -------------------------------------------------------------------------------- /modules/desktop/manifests/go.pp: -------------------------------------------------------------------------------- 1 | class desktop::go(String $go, String $sha256) { 2 | file { "/home/asottile/opt/${go}": 3 | ensure => 'directory', 4 | owner => 'asottile', 5 | group => 'asottile', 6 | require => File['/home/asottile/opt'], 7 | } -> 8 | archive { "/tmp/${go}.tar.gz": 9 | ensure => 'present', 10 | source => "https://dl.google.com/go/${go}.tar.gz", 11 | checksum => $sha256, 12 | checksum_type => 'sha256', 13 | extract => true, 14 | extract_path => "/home/asottile/opt/${go}", 15 | creates => "/home/asottile/opt/${go}/go/bin/go", 16 | user => 'asottile', 17 | group => 'asottile', 18 | require => Package['curl'], 19 | } 20 | ['go', 'gofmt'].each |$bin| { 21 | file { "/home/asottile/bin/${bin}": 22 | ensure => 'link', 23 | target => "/home/asottile/opt/${go}/go/bin/${bin}", 24 | owner => 'asottile', 25 | group => 'asottile', 26 | require => [File['/home/asottile/bin'], Archive["/tmp/${go}.tar.gz"]], 27 | } 28 | } 29 | 30 | tidy { 'purge old go versions': 31 | path => '/home/asottile/opt', 32 | recurse => 1, 33 | rmdirs => true, 34 | matches => ['go*'], 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/desktop/manifests/pypy.pp: -------------------------------------------------------------------------------- 1 | class desktop::pypy(String $pypy3, String $sha256) { 2 | file { "/home/asottile/opt/${pypy3}": 3 | ensure => 'directory', 4 | owner => 'asottile', 5 | group => 'asottile', 6 | require => File['/home/asottile/opt'], 7 | } -> 8 | archive { "/tmp/${pypy3}.tar.bz2": 9 | ensure => 'present', 10 | source => "https://downloads.python.org/pypy/${pypy3}.tar.bz2", 11 | checksum => $sha256, 12 | checksum_type => 'sha256', 13 | extract => true, 14 | extract_flags => {'tar' => '--strip-components=1 -xf'}, 15 | extract_path => "/home/asottile/opt/${pypy3}", 16 | creates => "/home/asottile/opt/${pypy3}/bin/pypy3", 17 | user => 'asottile', 18 | group => 'asottile', 19 | require => Package['curl'], 20 | } -> 21 | file { '/home/asottile/bin/pypy3': 22 | ensure => 'link', 23 | target => "/home/asottile/opt/${pypy3}/bin/pypy3", 24 | owner => 'asottile', 25 | group => 'asottile', 26 | require => File['/home/asottile/bin'], 27 | } 28 | 29 | tidy { 'purge old pypy versions': 30 | path => '/home/asottile/opt', 31 | recurse => 1, 32 | rmdirs => true, 33 | matches => ['pypy*'], 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/desktop/manifests/packer.pp: -------------------------------------------------------------------------------- 1 | class desktop::packer(String $version, String $archive_name, String $sha256) { 2 | file { "/home/asottile/opt/${archive_name}": 3 | ensure => 'directory', 4 | owner => 'asottile', 5 | group => 'asottile', 6 | require => File['/home/asottile/opt'], 7 | } -> 8 | archive { "/tmp/${archive_name}.zip": 9 | ensure => 'present', 10 | source => "https://releases.hashicorp.com/packer/${version}/${archive_name}.zip", 11 | checksum => $sha256, 12 | checksum_type => 'sha256', 13 | extract => true, 14 | extract_path => "/home/asottile/opt/${archive_name}", 15 | creates => "/home/asottile/opt/${archive_name}/packer", 16 | user => 'asottile', 17 | group => 'asottile', 18 | require => Package['curl'], 19 | } -> 20 | file { '/home/asottile/bin/packer': 21 | ensure => 'link', 22 | target => "/home/asottile/opt/${archive_name}/packer", 23 | owner => 'asottile', 24 | group => 'asottile', 25 | require => File['/home/asottile/bin'], 26 | } 27 | 28 | tidy { 'purge old packer versions': 29 | path => '/home/asottile/opt', 30 | recurse => 1, 31 | rmdirs => true, 32 | matches => ['packer-*'], 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /modules/desktop/manifests/terraform.pp: -------------------------------------------------------------------------------- 1 | class desktop::terraform(String $version, String $archive_name, String $sha256) { 2 | file { "/home/asottile/opt/${archive_name}": 3 | ensure => 'directory', 4 | owner => 'asottile', 5 | group => 'asottile', 6 | require => File['/home/asottile/opt'], 7 | } -> 8 | archive { "/tmp/${archive_name}.zip": 9 | ensure => 'present', 10 | source => "https://releases.hashicorp.com/terraform/${version}/${archive_name}.zip", 11 | checksum => $sha256, 12 | checksum_type => 'sha256', 13 | extract => true, 14 | extract_path => "/home/asottile/opt/${archive_name}", 15 | creates => "/home/asottile/opt/${archive_name}/terraform", 16 | user => 'asottile', 17 | group => 'asottile', 18 | require => Package['curl'], 19 | } -> 20 | file { '/home/asottile/bin/terraform': 21 | ensure => 'link', 22 | target => "/home/asottile/opt/${archive_name}/terraform", 23 | owner => 'asottile', 24 | group => 'asottile', 25 | require => File['/home/asottile/bin'], 26 | } 27 | 28 | tidy { 'purge old terraform versions': 29 | path => '/home/asottile/opt', 30 | recurse => 1, 31 | rmdirs => true, 32 | matches => ['terraform_*'], 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /modules/desktop/manifests/node.pp: -------------------------------------------------------------------------------- 1 | class desktop::node(String $node, String $archive_name, String $sha256) { 2 | file { "/home/asottile/opt/${archive_name}": 3 | ensure => 'directory', 4 | owner => 'asottile', 5 | group => 'asottile', 6 | require => File['/home/asottile/opt'], 7 | } -> 8 | archive { "/tmp/${archive_name}.tar.xz": 9 | ensure => 'present', 10 | source => "https://nodejs.org/dist/${node}/${archive_name}.tar.xz", 11 | checksum => $sha256, 12 | checksum_type => 'sha256', 13 | extract => true, 14 | extract_flags => {'unxz' => '-c', 'tar' => '--strip-components=1 -xf'}, 15 | extract_path => "/home/asottile/opt/${archive_name}", 16 | creates => "/home/asottile/opt/${archive_name}/bin/node", 17 | user => 'asottile', 18 | group => 'asottile', 19 | require => Package['curl'], 20 | } 21 | 22 | ['node', 'npm', 'npx', 'corepack'].each |$bin| { 23 | file { "/home/asottile/bin/${bin}": 24 | ensure => 'link', 25 | target => "/home/asottile/opt/${archive_name}/bin/${bin}", 26 | owner => 'asottile', 27 | group => 'asottile', 28 | require => [ 29 | File['/home/asottile/bin'], 30 | Archive["/tmp/${archive_name}.tar.xz"], 31 | ], 32 | } 33 | } 34 | 35 | tidy { 'purge old node versions': 36 | path => '/home/asottile/opt', 37 | recurse => 1, 38 | rmdirs => true, 39 | matches => ['node-*'], 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/desktop/manifests/dotfiles.pp: -------------------------------------------------------------------------------- 1 | class desktop::dotfiles { 2 | $dotfiles = [ 3 | '.bashrc', '.bash_aliases', '.gitconfig', '.hgrc', '.nanorc', '.pdbrc', 4 | '.pypirc', '.pythonrc.py', '.tmux.conf', 5 | ] 6 | $binfiles = [ 7 | 'bash/git-happy-merge', 'python/best-of', 'python/bump', 8 | 'python/git-github-compare', 9 | 'python/git-github-fork', 'python/git-github-url', 10 | 'python/inotify-exec', 'python/prune-remote-branches', 11 | ] 12 | 13 | vcsrepo { '/home/asottile/workspace/scratch': 14 | ensure => 'present', 15 | user => 'asottile', 16 | provider => 'git', 17 | source => 'git@github.com:asottile/scratch', 18 | } 19 | 20 | $dotfiles.each |$f| { 21 | file { "/home/asottile/${f}": 22 | ensure => 'link', 23 | target => "/home/asottile/workspace/scratch/${f}", 24 | owner => 'asottile', 25 | group => 'asottile', 26 | require => Vcsrepo['/home/asottile/workspace/scratch'], 27 | } 28 | } 29 | 30 | $binfiles.each |$f| { 31 | file { "/home/asottile/bin/${basename($f)}": 32 | ensure => 'link', 33 | target => "/home/asottile/workspace/scratch/${f}", 34 | owner => 'asottile', 35 | group => 'asottile', 36 | require => [ 37 | Vcsrepo['/home/asottile/workspace/scratch'], 38 | File['/home/asottile/bin'], 39 | ], 40 | } 41 | } 42 | 43 | # many scripts use this, though we can't set contents quite yet 44 | file { '/home/asottile/.github-auth.json': 45 | ensure => 'present', 46 | owner => 'asottile', 47 | group => 'asottile', 48 | mode => '0600', 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v6.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: debug-statements 9 | - id: double-quote-string-fixer 10 | - id: name-tests-test 11 | - id: requirements-txt-fixer 12 | - repo: https://github.com/asottile/reorder-python-imports 13 | rev: v3.16.0 14 | hooks: 15 | - id: reorder-python-imports 16 | args: [--py310-plus, --add-import, 'from __future__ import annotations'] 17 | - repo: https://github.com/asottile/add-trailing-comma 18 | rev: v4.0.0 19 | hooks: 20 | - id: add-trailing-comma 21 | - repo: https://github.com/asottile/pyupgrade 22 | rev: v3.21.2 23 | hooks: 24 | - id: pyupgrade 25 | args: [--py310-plus] 26 | - repo: https://github.com/hhatto/autopep8 27 | rev: v2.3.2 28 | hooks: 29 | - id: autopep8 30 | - repo: https://github.com/PyCQA/flake8 31 | rev: 7.3.0 32 | hooks: 33 | - id: flake8 34 | - repo: https://github.com/pre-commit/mirrors-mypy 35 | rev: v1.19.1 36 | hooks: 37 | - id: mypy 38 | - repo: https://github.com/chriskuehl/puppet-pre-commit-hooks 39 | rev: v2.2.0 40 | hooks: 41 | - id: puppet-lint 42 | args: [ 43 | --fix, --fail-on-warnings, 44 | --no-documentation-check, 45 | --no-arrow_on_right_operand_line-check, 46 | ] 47 | - id: puppet-validate 48 | - repo: https://github.com/rubocop/rubocop 49 | rev: v1.81.7 50 | hooks: 51 | - id: rubocop 52 | -------------------------------------------------------------------------------- /modules/desktop/manifests/venv.pp: -------------------------------------------------------------------------------- 1 | class desktop::venv { 2 | $packages = [ 3 | 'aactivator', 'awshelp', 'babi', 'flake8', 'pre-commit', 'tox', 'twine', 4 | 'virtualenv', 5 | ] 6 | $libraries = ['setuptools', 'wheel'] 7 | $venv = '/home/asottile/opt/venv' 8 | 9 | util::virtualenv { $venv: venv => $venv } 10 | 11 | # TODO: this is quite slow, ideally I'd like something like 12 | # venv { '/home/asottile/opt/venv': 13 | # user => 'asottile', 14 | # packages => $packages, 15 | # } 16 | ($packages + $libraries).each |$pkg| { 17 | util::pip {"${venv}(${pkg})": 18 | pkg => $pkg, 19 | venv => $venv, 20 | require => Util::Virtualenv[$venv], 21 | } 22 | } 23 | 24 | $packages.each |$bin| { 25 | file { "/home/asottile/bin/${bin}": 26 | ensure => 'link', 27 | target => "${venv}/bin/${bin}", 28 | owner => 'asottile', 29 | group => 'asottile', 30 | require => [ 31 | File['/home/asottile/bin'], 32 | Util::Pip["${venv}(${bin})"], 33 | ], 34 | } 35 | } 36 | 37 | # awscli deps conflict a lot so put them in their own environment 38 | $venv_aws = '/home/asottile/opt/awscli' 39 | util::virtualenv { $venv_aws: venv => $venv_aws } -> 40 | util::pip { "${venv_aws}(awscli)": pkg => 'awscli', venv => $venv_aws} -> 41 | file { '/home/asottile/bin/aws': 42 | ensure => 'link', 43 | target => "${venv_aws}/bin/aws", 44 | owner => 'asottile', 45 | group => 'asottile', 46 | require => [ 47 | File['/home/asottile/bin'], 48 | Util::Pip["${venv_aws}(awscli)"], 49 | ], 50 | } 51 | 52 | file { ['/home/asottile/bin/az', '/home/asottile/opt/azcli']: 53 | ensure => 'absent', 54 | recurse => true, 55 | force => true, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /modules/gsettings/lib/puppet/provider/gsetting/default.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | require 'ripper' 5 | 6 | def _parse_inner(sexp) 7 | case sexp[0] 8 | when :program 9 | inner, = sexp[1] 10 | _parse_inner(inner) 11 | when :string_literal, :dyna_symbol 12 | sexp[1][1][1] 13 | when :@int 14 | sexp[1].to_i 15 | when :@float 16 | sexp[1].to_f 17 | when :var_ref 18 | { 'true' => true, 'false' => false, 'nil' => nil }[sexp[1][1]] 19 | when :array 20 | if sexp[1].nil? 21 | [] 22 | else 23 | sexp[1].map { |x| _parse_inner(x) } 24 | end 25 | when :hash 26 | kvs = sexp[1][1].map do |kv| 27 | [_parse_inner(kv[1]), _parse_inner(kv[2])] 28 | end 29 | kvs.to_h 30 | else 31 | raise "notimplemented #{sexp[0]}" 32 | end 33 | end 34 | 35 | def literal_eval(s) 36 | # gsettings has special integer types that don't actually matter 37 | s.sub!(/^uint32 /, '') 38 | # ignore structured types 39 | s.sub!(/^@as /, '') 40 | _parse_inner(Ripper.sexp(s)) 41 | end 42 | 43 | def run_gsettings(uid, *cmd) 44 | pid = `pgrep --uid asottile --newest gnome-session`.strip 45 | envv = `grep -z DBUS_SESSION_BUS_ADDRESS /proc/#{pid}/environ`.strip 46 | Puppet::Util::Execution.execute( 47 | ['env', envv, 'gsettings', *cmd], 48 | failonfail: true, 49 | uid: uid, 50 | combine: true 51 | ) 52 | end 53 | 54 | Puppet::Type.type(:gsetting).provide(:default) do 55 | commands gsettings: 'gsettings' 56 | 57 | def schema_key 58 | @resource[:name].split 59 | end 60 | 61 | def get 62 | schema, key = schema_key 63 | literal_eval(run_gsettings(@resource[:user], ['get', schema, key])) 64 | end 65 | 66 | def set 67 | schema, key = schema_key 68 | value = JSON.dump(@resource[:ensure]) 69 | run_gsettings(@resource[:user], ['set', schema, key, value]) 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /modules/desktop/manifests/workspace.pp: -------------------------------------------------------------------------------- 1 | class desktop::workspace { 2 | file { '/home/asottile/workspace': 3 | ensure => 'directory', 4 | mode => '0755', 5 | owner => 'asottile', 6 | group => 'asottile', 7 | } 8 | 9 | $repos = [ 10 | 'asottile/all-repos', 'asottile/babi', 'asottile/pyupgrade', 11 | 'pre-commit/pre-commit', 'pre-commit/pre-commit-hooks', 12 | ] 13 | 14 | $repos.each |$repo| { 15 | $name = basename($repo) 16 | vcsrepo { "/home/asottile/workspace/${name}": 17 | ensure => 'present', 18 | user => 'asottile', 19 | provider => 'git', 20 | source => "git@github.com:${repo}", 21 | } 22 | } 23 | 24 | vcsrepo { '/home/asottile/workspace/cpython': 25 | ensure => 'present', 26 | user => 'asottile', 27 | provider => 'git', 28 | source => { 29 | 'origin' => 'git@github.com:python/cpython', 30 | 'asottile' => 'git@github.com:asottile/cpython', 31 | } 32 | } 33 | 34 | file { '/home/asottile/workspace/deadsnakes': 35 | ensure => 'directory', 36 | owner => 'asottile', 37 | group => 'asottile', 38 | } -> 39 | vcsrepo { '/home/asottile/workspace/deadsnakes/runbooks': 40 | ensure => 'present', 41 | user => 'asottile', 42 | provider => 'git', 43 | source => 'git@github.com:deadsnakes/runbooks', 44 | } 45 | 46 | $awc_repos = ['thumbnails', 'twitch-chat-bot'] 47 | file { '/home/asottile/workspace/anthonywritescode': 48 | ensure => 'directory', 49 | owner => 'asottile', 50 | group => 'asottile', 51 | } 52 | $awc_repos.each |$awc_repo| { 53 | vcsrepo { "/home/asottile/workspace/anthonywritescode/${awc_repo}": 54 | ensure => 'present', 55 | user => 'asottile', 56 | provider => 'git', 57 | source => "git@github.com:anthonywritescode/${awc_repo}", 58 | require => File['/home/asottile/workspace/anthonywritescode'], 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /run-puppet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import annotations 3 | 4 | import os.path 5 | import subprocess 6 | import sys 7 | 8 | 9 | PERSONAL_PUPPET = os.path.join( 10 | os.environ.get('XDG_CACHE_DIR', os.path.expanduser('~/.cache')), 11 | 'personal-puppet', 12 | ) 13 | GEM_HOME = os.path.join(PERSONAL_PUPPET, 'gems') 14 | GEM_BIN = os.path.join(GEM_HOME, 'bin') 15 | 16 | HERE = os.path.dirname(os.path.realpath(__file__)) 17 | MODULE_PATH = ':'.join(os.path.join(HERE, d) for d in ('modules', 'vendor')) 18 | 19 | GEMS = ( 20 | ('facter', '4.10.0'), 21 | ('puppet', '8.10.0'), 22 | ('r10k', '5.0.2'), 23 | ) 24 | 25 | 26 | def _msg(s: str) -> None: 27 | print('=' * 79) 28 | print(s) 29 | print('=' * 79, flush=True) 30 | 31 | 32 | def main() -> int: 33 | if subprocess.call(('ssh-add', '-l'), stdout=subprocess.DEVNULL): 34 | subprocess.check_call('ssh-add') 35 | 36 | os.environ['GEM_HOME'] = GEM_HOME 37 | os.environ['PATH'] = GEM_BIN + os.pathsep + os.environ['PATH'] 38 | 39 | for gem, version in GEMS: 40 | if not os.path.exists(os.path.join(GEM_HOME, f'gems/{gem}-{version}')): 41 | _msg(f'Ensuring {gem} is installed...') 42 | cmd: tuple[str, ...] 43 | cmd = ('gem', 'install', gem, '-v', version, '--no-document') 44 | subprocess.check_call(cmd) 45 | 46 | _msg('Installing puppet modules...') 47 | subprocess.check_call(('r10k', 'puppetfile', 'install'), cwd=HERE) 48 | subprocess.check_call(('r10k', 'puppetfile', 'purge'), cwd=HERE) 49 | 50 | _msg('Execing puppet') 51 | ret = subprocess.call(( 52 | 'sudo', 'env', 53 | 'RUBYOPT=-W0', 54 | 'PATH={}'.format(os.environ['PATH']), 55 | 'GEM_HOME={}'.format(os.environ['GEM_HOME']), 56 | 'SSH_AUTH_SOCK={}'.format(os.environ['SSH_AUTH_SOCK']), 57 | 'puppet', 'apply', '-v', '--show_diff', '--detailed-exitcodes', 58 | '--modulepath', MODULE_PATH, 59 | '--hiera_config', os.path.join(HERE, 'hiera.yaml'), 60 | os.path.join(HERE, 'manifests/site.pp'), 61 | *sys.argv[1:], 62 | )) 63 | if ret in {0, 2}: # {no changes, changes} 64 | return 0 65 | else: 66 | return 1 67 | 68 | 69 | if __name__ == '__main__': 70 | raise SystemExit(main()) 71 | --------------------------------------------------------------------------------