├── .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 | [](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 |
--------------------------------------------------------------------------------