Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ flowchart TD
| `developer-gui` | VS Code, Ghostty, DBeaver. Privacy + AI-upsell de-nag profile for VSCode/VSCodium/Cursor off unless `-e vscode_privacy_enabled=true` |
| `developer-rust` / `-go` / `-python` / `-node` / `-typescript` / `-c` | Language toolchains |
| `infrastructure` | OpenTofu, OpenBao, AWS CLI, helm, terraform-docs, `k8s` (kubectl, kubectx, kubens, k9s, kind, argocd, dive, kustomize, kubeconform, kube-linter), `data` (clickhouse-client, confluent-cli, rpk, valkey-cli, vector), `cloudflare` (flarectl, wrangler) |
| `packer` | HashiCorp Packer from HashiCorp's repo or tap. Opt-in, in no persona |
| `contributor` | hyperi-ci + its check tools (semgrep, alint), gitleaks, trivy, hadolint, pip-audit, yamllint, ansible-lint, pre-commit, act, git-scrub, macbash |
| `soe` / `soe-gui` | HyperI org policy (opt-in) |
| `--full-stack` / `--infra` / `--languages [list]` | Persona bundles (see `--help`) |
Expand Down Expand Up @@ -185,7 +186,8 @@ digest. Read that before changing a role or adding a tool.
- `developer-gui`: VS Code, Ghostty (Solarized theme), DBeaver
- `vscode-privacy` (off by default): strips the Copilot/AI upsell UI and the telemetry that stock VSCode ships enabled, across VSCode, VSCodium and Cursor. Enable with `-e vscode_privacy_enabled=true`. It merges one marked block into `settings.json` and never touches a comment or a key it does not manage, backs the file up before its first write, and `-e vscode_privacy_uninstall=true` takes only its own keys back out. Where you have set one of those keys yourself further down the file, yours wins and the run tells you which ones -- so it cannot look applied while changing nothing
- Languages: Rust, Go, Python, C/C++, Node.js, TypeScript (the Astral suite -- uv, ruff, ty -- ships in the base, as does Node.js: it is core tooling that semantic-release and CI need)
- `infrastructure`: OpenTofu + OpenBao (the OSS forks, no HashiCorp BUSL tools), AWS CLI v2, checkov, and terraform-docs for generating IaC module reference docs (engine-agnostic -- it reads `.tf` whichever binary runs it, and OpenTofu has no native `tofu docs`). Under `k8s`: kubectl + kubectx + kubens + k9s + kind + argocd + dive + kustomize + kubeconform + kube-linter. helm is NOT in that group -- it sits in `cloud`, so plain `--tags infrastructure` gets it whether or not you select `k8s`. The `data` group: clickhouse-client, confluent-cli, rpk, valkey-cli, vector. The `cloudflare` group: flarectl + wrangler (flarectl builds from source on both platforms -- Cloudflare ships no binary -- so Linux needs `developer-go`)
- `infrastructure`: OpenTofu + OpenBao (the OSS forks in place of terraform and vault), AWS CLI v2, checkov, and terraform-docs for generating IaC module reference docs (engine-agnostic -- it reads `.tf` whichever binary runs it, and OpenTofu has no native `tofu docs`). Under `k8s`: kubectl + kubectx + kubens + k9s + kind + argocd + dive + kustomize + kubeconform + kube-linter. helm is NOT in that group -- it sits in `cloud`, so plain `--tags infrastructure` gets it whether or not you select `k8s`. The `data` group: clickhouse-client, confluent-cli, rpk, valkey-cli, vector. The `cloudflare` group: flarectl + wrangler (flarectl builds from source on both platforms -- Cloudflare ships no binary -- so Linux needs `developer-go`)
- `packer` (off by default, and in no persona): HashiCorp Packer, the one HashiCorp tool we install, because it is BUSL with no open-source fork. Ubuntu and Fedora take HashiCorp's repo, with its signing key checked against the fingerprint HashiCorp publishes before it is trusted; macOS takes `hashicorp/tap`. `--tags removals` removes that repo only where Packer is not installed -- see [roles/packer/README.md](ansible/roles/packer/README.md)
- `contributor`: hyperi-ci and the tools its checks drive (semgrep, alint), gitleaks, trivy, hadolint, pip-audit, ansible-lint, pre-commit, act, and git-scrub for rewriting AI residue or a leaked secret out of git history -- gitleaks scans FULL history, so a secret removed from HEAD still fails the gate. macbash is here too: it flags the GNU-only bash constructs that break on macOS, and CONTRIBUTING.md asks for it on every shell change
- `soe` / `soe-gui`: HyperI org policy: VPN clients, Claude Code, Slack, LibreOffice, RDP client, telemetry-disable, auto-updates, GNOME taskbar
- `developer-ai` (off by default): the OpenAI Codex CLI as a second opinion alongside Claude Code rather than a replacement for it, plus OpenAI's Codex plugin FOR Claude Code, so `/codex:review` and `/codex:adversarial-review` are things Claude asks Codex for. The plugin is skipped -- with a warning naming the tag that fixes it -- unless claude, codex and a new enough node are all present for that user, because it installs happily without them and then throws on every invocation. Sign-in stays the person's: `codex login --device-auth` on a box with no browser
Expand Down
5 changes: 3 additions & 2 deletions ansible/molecule/matrix/molecule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# installs, which is why Fedora belongs here -- there are no deployed Fedora
# clients to have drifted, but a fresh Fedora box must come up correctly.
#
# Scoped to the CLI base (`repository`, `utilities`, `git`). Docker, snap and the
# Scoped to the CLI base (`repository`, `utilities`, `git`) plus `packer`, the
# opt-in role with its own vendor repo on every platform. Docker, snap and the
# GNOME paths need a daemon or a session a container does not have, so widening
# the tag set means solving that first -- privileged containers or systemd
# images -- not just adding a tag.
Expand Down Expand Up @@ -69,7 +70,7 @@ ansible:
ansible_playbook:
- --diff
- --tags
- repository,utilities,git
- repository,utilities,git,packer
playbooks:
converge: converge.yml
verify: verify.yml
Expand Down
1 change: 1 addition & 0 deletions ansible/molecule/matrix/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- {name: git, cmd: git --version}
- {name: tmux, cmd: tmux -V}
- {name: age, cmd: age --version}
- {name: packer, cmd: packer version}
loop_control:
label: "{{ item.name }}"
register: matrix_tools
Expand Down
51 changes: 47 additions & 4 deletions ansible/molecule/remediation/prepare.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,8 @@
# The Fedora yq superseded by dnf is NOT planted: its removal sits in the
# install path of utilities.yml, which a removals-only run never reaches.
# kustomize is planted above, in the superseded-by-a-package section.
# The HashiCorp repo is the half that matters: left behind it keeps serving
# BUSL packages and updates. Removing it is a remediation, so only a
# `--tags removals` run does it -- which is what this scenario converges.
# The HashiCorp repo goes on a removals run unless Packer is installed, which
# the gosrc host fakes below so both branches are exercised.
- name: Plant the HashiCorp apt repository (Ubuntu)
ansible.builtin.copy:
content: |
Expand All @@ -309,7 +308,7 @@
# provisioned before that migration carries both.
# A parseable line, because apt refuses EVERY operation when any source list
# is malformed -- including the removal that is supposed to clear it.
# `trusted=yes` keeps the fixture off the network and off the keyring.
# `trusted=yes` keeps the fixture off the keyring.
- name: Plant the legacy HashiCorp .list (Ubuntu)
ansible.builtin.copy:
content: "deb [trusted=yes] https://apt.releases.hashicorp.com noble main\n"
Expand All @@ -331,6 +330,50 @@
- /usr/share/keyrings/hashicorp-archive-keyring.gpg
when: ansible_facts['distribution'] == 'Ubuntu'

# An empty package named packer is all the removals check reads, and it
# keeps the fixture off HashiCorp's BUSL binary.
- name: Create the stand-in packer package tree (gosrc host)
ansible.builtin.file:
path: /root/packer-fixture/DEBIAN
state: directory
owner: root
group: root
mode: '0755'
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' in inventory_hostname"

- name: Write the control file for a stand-in packer package (gosrc host)
ansible.builtin.copy:
content: |
Package: packer
Version: 0.0.0-fixture
Architecture: all
Maintainer: fixture <fixture@example.invalid>
Description: Stand-in for HashiCorp Packer in the remediation fixture
dest: /root/packer-fixture/DEBIAN/control
owner: root
group: root
mode: '0644'
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' in inventory_hostname"

- name: Build the stand-in packer package (gosrc host)
ansible.builtin.command:
argv: [dpkg-deb, --build, /root/packer-fixture, /root/packer-fixture.deb]
creates: /root/packer-fixture.deb
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' in inventory_hostname"

- name: Install the stand-in packer package (gosrc host)
ansible.builtin.apt:
deb: /root/packer-fixture.deb
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' in inventory_hostname"

- name: Install the DBeaver flatpak superseded by the vendor apt repo (Ubuntu)
community.general.flatpak:
name: io.dbeaver.DBeaverCommunity
Expand Down
30 changes: 24 additions & 6 deletions ansible/molecule/remediation/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@
# The Fedora yq migration is not asserted here: its removal sits in the
# install path of utilities.yml, which a removals-only run never reaches.
# kustomize is covered in the superseded-by-a-package section above.
# The repo is the half that keeps serving BUSL packages if it survives.
# Without Packer the repo is unhooked, because one left trusting a superseded
# key fails every apt refresh. With Packer (the gosrc host) it stays.
- name: Stat the HashiCorp repository artefacts (Ubuntu)
ansible.builtin.stat:
path: "{{ item }}"
Expand All @@ -210,19 +211,36 @@
register: verify_hashicorp
when: ansible_facts['distribution'] == 'Ubuntu'

- name: Assert the HashiCorp repository was unhooked
- name: Assert the HashiCorp repository was unhooked where Packer is absent
ansible.builtin.assert:
that:
- not item.stat.exists
fail_msg: >-
{{ item.item }} survived remediation. The HashiCorp repo goes on
serving BUSL packages and updates for as long as it is configured,
so leaving it is worse than leaving the binaries.
{{ item.item }} survived remediation on a host without Packer. A
HashiCorp repo whose key the host no longer trusts fails every apt
refresh, and nothing else here removes it.
success_msg: "{{ item.item }} removed"
loop: "{{ verify_hashicorp.results | default([]) }}"
loop_control:
label: "{{ item.item | default('skipped') }}"
when: ansible_facts['distribution'] == 'Ubuntu'
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' not in inventory_hostname"

- name: Assert the HashiCorp repository was kept where Packer is installed
ansible.builtin.assert:
that:
- item.stat.exists
fail_msg: >-
{{ item.item }} was removed from a host with Packer installed, which
leaves Packer with no update source.
success_msg: "{{ item.item }} kept"
loop: "{{ verify_hashicorp.results | default([]) }}"
loop_control:
label: "{{ item.item | default('skipped') }}"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'gosrc' in inventory_hostname"

- name: Stat the retired vendor repository artefacts (Ubuntu)
ansible.builtin.stat:
Expand Down
5 changes: 5 additions & 0 deletions ansible/playbooks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@
become: "{{ ansible_facts['distribution'] != 'MacOSX' }}"
tags: ['infrastructure', 'never']

# Packer from HashiCorp's repo (opt-in, in no persona): BUSL, no fork.
- role: packer
become: "{{ ansible_facts['distribution'] != 'MacOSX' }}"
tags: ['packer', 'never']

# ========================================================================
# PERSONAS (meta-roles) — opinionated bundles that resolve to the roles
# above via meta/dependencies. They pull the CLEAN developer base: its
Expand Down
11 changes: 5 additions & 6 deletions ansible/roles/infrastructure/tasks/cloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
# `aws-vault` below is NOT HashiCorp Vault -- it is aws-vault (the ByteNess fork
# of 99designs/aws-vault), an unrelated tool for keeping AWS credentials in the OS keychain. It stays.
#
# Packer has no viable fork (the community never produced one) and is
# deliberately NOT installed here. hyperi-infra needs it for image builds; its
# developers install it themselves rather than have us ship a BUSL binary to
# every workstation.
# Packer has no viable fork (the community never produced one), so it comes
# from HashiCorp's repo in its own opt-in role, `packer`, rather than shipping a
# BUSL binary to every box that asks for the IaC tools.

# ============================================================================
# OPENTOFU REPOSITORY (Ubuntu and Fedora)
Expand All @@ -27,8 +26,8 @@
# upstream 1.12.6), which is the wrong side of "almost current" for the tool
# that plans and applies infrastructure.
#
# The apt suite is literally "any" and the packages are arch-generic, so unlike
# the HashiCorp repo this needs no LTS-codename mapping and no arch handling.
# The apt suite is literally "any" and the packages are arch-generic, so this
# needs no codename and no arch handling.
#
# The package is `tofu` on both. Fedora's own package is `opentofu`, and the two
# conflict over /usr/bin/tofu, so the distro one is removed below.
Expand Down
8 changes: 3 additions & 5 deletions ansible/roles/infrastructure/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
# Not HyperI-specific. Org tooling lives in `soe`.

# Before the installs: removing terraform/vault must not race the tofu/bao
# install, and the HashiCorp repo has to go before an apt update reads it again.
# install.
#
# `removals` ONLY, and never on a propagated tag. Installing the IaC tools is
# not a request to delete someone's Terraform or unhook their HashiCorp repo --
# that is a remediation, asked for explicitly. `--tags infrastructure` and
# `--tags cloud` used to fire it too, which meant a routine IaC install silently
# took both away.
# not a request to delete someone's Terraform -- that is a remediation, asked
# for explicitly.
- name: Remove the retired HashiCorp tools (opt-in)
ansible.builtin.include_tasks:
file: removals.yml
Expand Down
102 changes: 70 additions & 32 deletions ansible/roles/infrastructure/tasks/removals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,43 @@
# vault -> bao (OpenBao, MPL-2.0)
#
# Ansible cannot remove what we simply stop declaring, so every host we have
# ever provisioned keeps terraform, vault AND the HashiCorp repo until we say
# otherwise. Leaving the repo behind is the worse half: it would go on serving
# BUSL packages and updates forever.
# ever provisioned keeps terraform and vault until we say otherwise.
#
# The HashiCorp repo, its key and the Homebrew tap go only where Packer is not
# installed, since the opt-in `packer` role installs from them. A repo left
# behind with a key the host no longer trusts fails every apt refresh.
#
# Removing terraform WILL break tooling that still shells out to it -- notably
# hyperi-infra, which invokes `terraform` directly. That is why this runs on
# `--tags removals` alone: installing the IaC tools is not a request to delete
# somebody's Terraform.
#
# apt refuses every operation while any source list is malformed, so a host with
# a broken hashicorp.list cannot run the removal that would clear it.

- name: Remove Terraform and Vault (Ubuntu)
ansible.builtin.apt:
name:
- terraform
- vault
state: absent
purge: false
# Before the package removals, which refresh the apt cache and fail on a repo
# signed by a key the host no longer trusts.
- name: Check whether Packer is installed (Ubuntu)
ansible.builtin.command:
argv: [dpkg-query, --show, '--showformat=${Status}', packer]
register: infrastructure_packer_deb
changed_when: false
failed_when: false
check_mode: false
when: ansible_facts['distribution'] == 'Ubuntu'

- name: Remove Terraform and Vault (Fedora)
ansible.builtin.dnf:
name:
- terraform
- vault
state: absent
when: ansible_facts['distribution'] == 'Fedora'

# The repo itself. Both filenames are removed: deb822_repository writes
# .sources, and older revisions of this role used apt_repository, which wrote
# .list. A host provisioned before that migration carries the .list.
- name: Remove the HashiCorp APT repository (Ubuntu)
# Both filenames: deb822_repository writes .sources, and older setups wrote .list.
- name: Remove the HashiCorp APT repository where Packer is not installed (Ubuntu)
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/hashicorp.sources
- /etc/apt/sources.list.d/hashicorp.list
- /usr/share/keyrings/hashicorp-archive-keyring.asc
- /usr/share/keyrings/hashicorp-archive-keyring.asc.unverified
- /usr/share/keyrings/hashicorp-archive-keyring.gpg
register: infrastructure_hashicorp_repo_removed
when: ansible_facts['distribution'] == 'Ubuntu'
when:
- ansible_facts['distribution'] == 'Ubuntu'
- "'install ok installed' not in infrastructure_packer_deb.stdout | default('')"

- name: Refresh the APT cache after removing the repository
ansible.builtin.apt:
Expand All @@ -56,14 +50,44 @@
- ansible_facts['distribution'] == 'Ubuntu'
- infrastructure_hashicorp_repo_removed is changed

- name: Remove the HashiCorp YUM repository (Fedora)
- name: Check whether Packer is installed (Fedora)
ansible.builtin.command:
argv: [rpm, -q, packer]
register: infrastructure_packer_rpm
changed_when: false
failed_when: false
check_mode: false
when: ansible_facts['distribution'] == 'Fedora'

- name: Remove the HashiCorp YUM repository where Packer is not installed (Fedora)
ansible.builtin.file:
path: /etc/yum.repos.d/hashicorp.repo
path: "{{ item }}"
state: absent
loop:
- /etc/yum.repos.d/hashicorp.repo
- /etc/pki/rpm-gpg/RPM-GPG-KEY-hashicorp
- /etc/pki/rpm-gpg/RPM-GPG-KEY-hashicorp.unverified
when:
- ansible_facts['distribution'] == 'Fedora'
- infrastructure_packer_rpm.rc | default(0) != 0

- name: Remove Terraform and Vault (Ubuntu)
ansible.builtin.apt:
name:
- terraform
- vault
state: absent
purge: false
when: ansible_facts['distribution'] == 'Ubuntu'

- name: Remove Terraform and Vault (Fedora)
ansible.builtin.dnf:
name:
- terraform
- vault
state: absent
when: ansible_facts['distribution'] == 'Fedora'

# macOS. Untap only after the formulae are gone, or brew refuses.
#
# NOT touched: `aws-vault`. It is the ByteNess aws-vault, nothing to do with
# HashiCorp Vault despite the name, and it is still installed on purpose.
- name: Remove Terraform and Vault (macOS)
Expand All @@ -77,11 +101,25 @@
failed_when: false
when: ansible_facts['distribution'] == 'MacOSX'

- name: Remove the HashiCorp Homebrew tap (macOS)
- name: Check whether Packer is installed (macOS)
ansible.builtin.command:
argv: [brew, list, hashicorp/tap/packer]
register: infrastructure_packer_brew
become: false
environment: "{{ homebrew_env }}"
changed_when: false
failed_when: false
check_mode: false
when: ansible_facts['distribution'] == 'MacOSX'

# Untap only after the formulae are gone, or brew refuses.
- name: Remove the HashiCorp Homebrew tap where Packer is not installed (macOS)
community.general.homebrew_tap:
name: hashicorp/tap
state: absent
become: false
environment: "{{ homebrew_env }}"
failed_when: false
when: ansible_facts['distribution'] == 'MacOSX'
when:
- ansible_facts['distribution'] == 'MacOSX'
- infrastructure_packer_brew.rc | default(0) != 0
Loading
Loading