Compare commits
2
Commits
4f868d4f12
...
c396c89efb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c396c89efb | ||
|
|
7450892a68 |
No files matched your search
@@ -63,6 +63,14 @@ astro_orbiter:
|
|||||||
hosts:
|
hosts:
|
||||||
astro-orbiter:
|
astro-orbiter:
|
||||||
|
|
||||||
|
claude_servers:
|
||||||
|
hosts:
|
||||||
|
haunted-mansion:
|
||||||
|
ansible_host: 10.1.71.137
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/ansible
|
||||||
|
|
||||||
hermes_server:
|
hermes_server:
|
||||||
hosts:
|
hosts:
|
||||||
carousel-of-progress:
|
carousel-of-progress:
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# install-claude.yml
|
||||||
|
---
|
||||||
|
# Usage: ansible-playbook -i inventory.ini install-claude.yml
|
||||||
|
- name: Install Claude Code on Ubuntu servers
|
||||||
|
hosts: claude_servers
|
||||||
|
become: false # the role escalates only where it needs to
|
||||||
|
roles:
|
||||||
|
- role: claude_code
|
||||||
|
vars:
|
||||||
|
claude_code_install_method: apt # or: native
|
||||||
|
claude_code_apt_channel: stable # or: latest
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# claude_code role
|
||||||
|
|
||||||
|
Installs [Claude Code](https://code.claude.com/docs/en/setup) on Ubuntu 20.04+ / Debian 10+ (x64 or ARM64, 4 GB+ RAM).
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
| `claude_code_install_method` | What it does | Updates |
|
||||||
|
|---|---|---|
|
||||||
|
| `apt` (default) | Adds Anthropic's signed apt repo (stable or latest channel) and installs the `claude-code` package system-wide. Verifies the signing-key fingerprint before trusting it. | `apt upgrade` / set `claude_code_package_state: latest` |
|
||||||
|
| `native` | Runs Anthropic's installer as `claude_code_user`; binary lands in `~/.local/bin/claude`. | Self-updates in the background |
|
||||||
|
|
||||||
|
## Key variables (see `defaults/main.yml`)
|
||||||
|
|
||||||
|
- `claude_code_apt_channel`: `stable` (about a week behind, skips bad releases) or `latest`
|
||||||
|
- `claude_code_package_state`: `present` or `latest`
|
||||||
|
- `claude_code_native_version`: `latest`, `stable`, or a pinned version like `2.1.89`
|
||||||
|
- `claude_code_settings`: optional dict written to `~/.claude/settings.json` (overwrites the file)
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp inventory.ini.example inventory.ini # edit hosts
|
||||||
|
ansible-playbook -i inventory.ini install-claude.yml --check # dry run
|
||||||
|
ansible-playbook -i inventory.ini install-claude.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
The remote user needs sudo for the `apt` method.
|
||||||
|
|
||||||
|
## After install: authenticate (not automated)
|
||||||
|
|
||||||
|
Run `claude` once on the server and follow the login prompt, or export `ANTHROPIC_API_KEY`
|
||||||
|
for the user (store it in Ansible Vault, not in the repo). A Pro, Max, Team, Enterprise or
|
||||||
|
Console account is required; the free claude.ai plan does not include Claude Code.
|
||||||
|
|
||||||
|
Verify with `claude --version` and `claude doctor`.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# roles/claude_code/defaults/main.yml
|
||||||
|
---
|
||||||
|
# How to install Claude Code:
|
||||||
|
# apt - signed Anthropic apt repo, system-wide, upgraded via apt (recommended for servers)
|
||||||
|
# native - Anthropic's installer script, per-user in ~/.local/bin, self-updating
|
||||||
|
claude_code_install_method: apt
|
||||||
|
|
||||||
|
# ---- apt method -------------------------------------------------------------
|
||||||
|
# Release channel: "stable" (~1 week behind, skips bad releases) or "latest"
|
||||||
|
claude_code_apt_channel: stable
|
||||||
|
# "present" installs once; "latest" upgrades on every run
|
||||||
|
claude_code_package_state: present
|
||||||
|
|
||||||
|
claude_code_apt_key_url: https://downloads.claude.ai/keys/claude-code.asc
|
||||||
|
claude_code_apt_key_path: /etc/apt/keyrings/claude-code.asc
|
||||||
|
# Fingerprint published in Anthropic's docs; the role refuses to continue if it differs
|
||||||
|
claude_code_apt_key_fingerprint: "31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE"
|
||||||
|
claude_code_apt_repo_base: https://downloads.claude.ai/claude-code/apt
|
||||||
|
|
||||||
|
# ---- native method ----------------------------------------------------------
|
||||||
|
# User the native installer runs as
|
||||||
|
claude_code_user: "{{ ansible_user_id }}"
|
||||||
|
# "latest", "stable", or a specific version such as "2.1.89"
|
||||||
|
claude_code_native_version: latest
|
||||||
|
|
||||||
|
# ---- common -----------------------------------------------------------------
|
||||||
|
claude_code_prereq_packages:
|
||||||
|
- curl
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
|
||||||
|
# Optional: content for ~/.claude/settings.json of claude_code_settings_user.
|
||||||
|
# Empty dict = don't touch the file. NOTE: when set, the file is overwritten.
|
||||||
|
# Example: { "autoUpdatesChannel": "stable", "env": { "DISABLE_AUTOUPDATER": "1" } }
|
||||||
|
claude_code_settings: {}
|
||||||
|
claude_code_settings_user: "{{ claude_code_user }}"
|
||||||
|
|
||||||
|
# Run `claude --version` at the end and fail if it doesn't work
|
||||||
|
claude_code_verify: true
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# roles/claude_code/meta/main.yml
|
||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: claude_code
|
||||||
|
author: ryan
|
||||||
|
description: Install Claude Code on Ubuntu/Debian servers (signed apt repo or native installer)
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.14"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions: [focal, jammy, noble]
|
||||||
|
- name: Debian
|
||||||
|
versions: [buster, bullseye, bookworm]
|
||||||
|
galaxy_tags: [claude, ai, cli]
|
||||||
|
dependencies: []
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# roles/claude_code/tasks/apt.yml
|
||||||
|
---
|
||||||
|
- name: Create apt keyrings directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/apt/keyrings
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Download Anthropic release signing key
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ claude_code_apt_key_url }}"
|
||||||
|
dest: "{{ claude_code_apt_key_path }}"
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Read key fingerprint
|
||||||
|
ansible.builtin.shell:
|
||||||
|
cmd: |
|
||||||
|
set -o pipefail
|
||||||
|
gpg --show-keys --with-colons {{ claude_code_apt_key_path | quote }} | awk -F':' '/^fpr/ {print $10; exit}'
|
||||||
|
executable: /bin/bash
|
||||||
|
register: claude_code_key_fpr
|
||||||
|
changed_when: false
|
||||||
|
check_mode: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Refuse to continue if the key fingerprint does not match
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- claude_code_key_fpr.stdout | trim | upper == claude_code_apt_key_fingerprint | upper
|
||||||
|
fail_msg: >-
|
||||||
|
Signing key fingerprint mismatch: got '{{ claude_code_key_fpr.stdout | trim }}',
|
||||||
|
expected '{{ claude_code_apt_key_fingerprint }}'. Removing the key file.
|
||||||
|
quiet: true
|
||||||
|
register: claude_code_key_check
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Remove untrusted key file
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ claude_code_apt_key_path }}"
|
||||||
|
state: absent
|
||||||
|
become: true
|
||||||
|
when: claude_code_key_check is failed
|
||||||
|
|
||||||
|
- name: Stop if key check failed
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "{{ claude_code_key_check.msg }}"
|
||||||
|
when: claude_code_key_check is failed
|
||||||
|
|
||||||
|
- name: Configure Claude Code apt repository
|
||||||
|
ansible.builtin.apt_repository:
|
||||||
|
repo: >-
|
||||||
|
deb [signed-by={{ claude_code_apt_key_path }}]
|
||||||
|
{{ claude_code_apt_repo_base }}/{{ claude_code_apt_channel }}
|
||||||
|
{{ claude_code_apt_channel }} main
|
||||||
|
filename: claude-code
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Install claude-code package
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: claude-code
|
||||||
|
state: "{{ claude_code_package_state }}"
|
||||||
|
become: true
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# roles/claude_code/tasks/main.yml
|
||||||
|
---
|
||||||
|
- name: Assert a supported install method
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- claude_code_install_method in ['apt', 'native']
|
||||||
|
- claude_code_apt_channel in ['stable', 'latest']
|
||||||
|
fail_msg: "claude_code_install_method must be apt|native and claude_code_apt_channel stable|latest"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Assert a supported OS (Ubuntu 20.04+ / Debian 10+)
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_facts['os_family'] == 'Debian'
|
||||||
|
- >-
|
||||||
|
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_version'] is version('20.04', '>='))
|
||||||
|
or (ansible_facts['distribution'] == 'Debian' and ansible_facts['distribution_version'] is version('10', '>='))
|
||||||
|
- ansible_facts['architecture'] in ['x86_64', 'aarch64']
|
||||||
|
fail_msg: "Claude Code needs Ubuntu 20.04+/Debian 10+ on x64 or ARM64"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Install prerequisite packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ claude_code_prereq_packages }}"
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 3600
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Install via apt repository
|
||||||
|
ansible.builtin.include_tasks: apt.yml
|
||||||
|
when: claude_code_install_method == 'apt'
|
||||||
|
|
||||||
|
- name: Install via native installer
|
||||||
|
ansible.builtin.include_tasks: native.yml
|
||||||
|
when: claude_code_install_method == 'native'
|
||||||
|
|
||||||
|
- name: Write Claude Code settings
|
||||||
|
ansible.builtin.include_tasks: settings.yml
|
||||||
|
when: claude_code_settings | length > 0
|
||||||
|
|
||||||
|
- name: Verify installation
|
||||||
|
ansible.builtin.include_tasks: verify.yml
|
||||||
|
when: claude_code_verify | bool
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# roles/claude_code/tasks/native.yml
|
||||||
|
---
|
||||||
|
- name: Install Claude Code with the native installer
|
||||||
|
ansible.builtin.shell:
|
||||||
|
cmd: set -o pipefail; curl -fsSL https://claude.ai/install.sh | bash -s {{ claude_code_native_version | quote }}
|
||||||
|
executable: /bin/bash
|
||||||
|
creates: "~{{ claude_code_user }}/.local/bin/claude"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ claude_code_user }}"
|
||||||
|
|
||||||
|
- name: Put ~/.local/bin on PATH for login shells
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/profile.d/claude-code.sh
|
||||||
|
content: |
|
||||||
|
# Managed by Ansible (claude_code role)
|
||||||
|
case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# roles/claude_code/tasks/settings.yml
|
||||||
|
---
|
||||||
|
- name: Look up settings user
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ claude_code_settings_user }}"
|
||||||
|
|
||||||
|
- name: Create ~/.claude directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ ansible_facts['getent_passwd'][claude_code_settings_user][4] }}/.claude"
|
||||||
|
state: directory
|
||||||
|
mode: "0700"
|
||||||
|
owner: "{{ claude_code_settings_user }}"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Write ~/.claude/settings.json
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ ansible_facts['getent_passwd'][claude_code_settings_user][4] }}/.claude/settings.json"
|
||||||
|
content: "{{ claude_code_settings | to_nice_json }}\n"
|
||||||
|
mode: "0600"
|
||||||
|
owner: "{{ claude_code_settings_user }}"
|
||||||
|
become: true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# roles/claude_code/tasks/verify.yml
|
||||||
|
---
|
||||||
|
- name: Check claude --version
|
||||||
|
ansible.builtin.shell:
|
||||||
|
cmd: |
|
||||||
|
if command -v claude >/dev/null 2>&1; then claude --version
|
||||||
|
else "$HOME/.local/bin/claude" --version; fi
|
||||||
|
executable: /bin/bash
|
||||||
|
become: "{{ claude_code_install_method == 'native' }}"
|
||||||
|
become_user: "{{ claude_code_user if claude_code_install_method == 'native' else omit }}"
|
||||||
|
register: claude_code_version_out
|
||||||
|
changed_when: false
|
||||||
|
check_mode: false
|
||||||
|
|
||||||
|
- name: Show installed version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ claude_code_version_out.stdout }}"
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
# Paperclip Official image
|
||||||
|
paperclip_image: ghcr.io/paperclipai/paperclip
|
||||||
|
paperclip_version: "2026.1001.0" # no "v" prefix; also accepts stable, beta, nightly, canary, sha-<short>
|
||||||
|
|
||||||
# Paperclip deployment paths and configuration
|
# Paperclip deployment paths and configuration
|
||||||
paperclip_base_path: /opt/paperclip
|
paperclip_base_path: /opt/paperclip
|
||||||
paperclip_data_dir: "{{ paperclip_base_path }}/data"
|
paperclip_data_dir: "{{ paperclip_base_path }}/data"
|
||||||
|
|||||||
@@ -77,16 +77,16 @@
|
|||||||
become: true
|
become: true
|
||||||
tags: [paperclip, files]
|
tags: [paperclip, files]
|
||||||
|
|
||||||
- name: Template OpenCode config
|
# - name: Template OpenCode config
|
||||||
ansible.builtin.template:
|
# ansible.builtin.template:
|
||||||
src: opencode.jsonc.j2
|
# src: opencode.jsonc.j2
|
||||||
dest: "{{ paperclip_base_path }}/.config/opencode/opencode.jsonc"
|
# dest: "{{ paperclip_base_path }}/.config/opencode/opencode.jsonc"
|
||||||
mode: '0644'
|
# mode: '0644'
|
||||||
owner: 1000
|
# owner: 1000
|
||||||
group: 1000
|
# group: 1000
|
||||||
become: true
|
# become: true
|
||||||
notify: Restart paperclip stack
|
# notify: Restart paperclip stack
|
||||||
tags: [paperclip, files]
|
# tags: [paperclip, files]
|
||||||
|
|
||||||
# Idempotent: only changed services are recreated. remove_orphans also cleans
|
# Idempotent: only changed services are recreated. remove_orphans also cleans
|
||||||
# up the old paperclip-nginx / paperclip-certbot containers if they still exist.
|
# up the old paperclip-nginx / paperclip-certbot containers if they still exist.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
paperclip:
|
paperclip:
|
||||||
image: paperclip-local:latest
|
image: {{ paperclip_image }}:{{ paperclip_version }}
|
||||||
container_name: paperclip
|
container_name: paperclip
|
||||||
network_mode: host
|
network_mode: host
|
||||||
env_file:
|
env_file:
|
||||||
|
|||||||
Reference in new issue
Block a user