#!/usr/bin/env bash # Chatleady — instalação numa VPS limpa. # # curl -fsSL https://get.chatleady.com.br | bash # # ESTE arquivo é o único público da Chatleady, e é público de propósito: ele não contém nada. # Instala o Docker, pede o seu token e puxa a imagem privada do instalador. O instalador de # verdade, os templates e o prompt da secretária estão dentro dela, atrás do `read:packages`. # Quem ler este script daqui aprende que existe uma imagem — e não consegue baixá-la. set -euo pipefail IMAGEM="${CHATLEADY_IMAGEM:-ghcr.io/cairojr/chatleady-installer:latest}" DESTINO="${CHATLEADY_DIR:-/opt/chatleady}" # O GitHub aceita o escopo e o nome pela URL: a tela abre com read:packages JÁ marcado e nada mais. # É o que evita a pessoa marcar `repo` "por garantia" — token amplo numa VPS de cliente entrega os # repositórios privados junto com a máquina, e o preflight do instalador recusa por isso. LINK_TOKEN='https://github.com/settings/tokens/new?scopes=read:packages&description=Chatleady' vermelho() { printf '\033[31m%s\033[0m\n' "$*" >&2; } passo() { printf '\n\033[1m%s\033[0m\n' "$*"; } info() { printf '\033[36m›\033[0m %s\n' "$*"; } morre() { vermelho "erro: $*"; exit 1; } # Sob `curl | bash`, a entrada padrão é o próprio script chegando pelo cano: um `read` comum leria # as linhas seguintes do script em vez de esperar o teclado. /dev/tty fala com o terminal de verdade. TTY=/dev/tty # Não basta o arquivo existir: /dev/tty está lá em toda máquina Linux, e ABRIR é o que falha quando # não há terminal — um `ssh servidor 'comando'`, um cron, um pipeline de CI. Testar com -e deixava o # script morrer com "No such device or address" cru, em vez da explicação de como seguir. tem_terminal() { { : < "$TTY"; } 2>/dev/null; } pergunta() { local rotulo="$1" var="$2" secreto="${3:-}" valor="" tem_terminal || morre "preciso perguntar '${rotulo}' e não há terminal. Passe por variável de ambiente, ou baixe o script e rode: bash install.sh" if [ -n "$secreto" ]; then printf '%s: ' "$rotulo" > "$TTY" read -r -s valor < "$TTY" printf '\n' > "$TTY" else printf '%s: ' "$rotulo" > "$TTY" read -r valor < "$TTY" fi [ -n "$valor" ] || morre "'${rotulo}' não pode ficar em branco" printf -v "$var" '%s' "$valor" } # ── verificações mínimas ────────────────────────────────────────────────────────────────────── [ "$(id -u)" -eq 0 ] || morre "rode como root (ou com sudo -i)" command -v curl >/dev/null 2>&1 || morre "curl não encontrado" passo "Chatleady — preparando a máquina" # ── Docker ──────────────────────────────────────────────────────────────────────────────────── if docker info >/dev/null 2>&1; then info "Docker já está instalado" else info "instalando o Docker (script oficial da Docker)" curl -fsSL https://get.docker.com | sh >/dev/null 2>&1 \ || morre "não consegui instalar o Docker. Instale à mão e rode este comando de novo." docker info >/dev/null 2>&1 || morre "o Docker foi instalado mas não respondeu. Reinicie a máquina." info "Docker instalado: $(docker --version | sed 's/,.*//')" fi # ── acesso e instalador ─────────────────────────────────────────────────────────────────────── GHCR_USER="${GHCR_USER:-}" GHCR_TOKEN="${GHCR_TOKEN:-}" passo "Trazendo o instalador" # Tenta ANTES de perguntar: numa máquina que já instalou uma vez, o Docker continua autenticado e # não há por que pedir o token de novo. Reinstalar é operação comum — cobrar credencial em toda # execução treina o operador a colar token sem pensar. if [ -z "$GHCR_TOKEN" ] && docker pull -q "$IMAGEM" >/dev/null 2>&1; then info "esta máquina já tem acesso" elif [ -z "$GHCR_TOKEN" ] && docker image inspect "$IMAGEM" >/dev/null 2>&1; then # A imagem já está aqui e o registro não respondeu. Vale para uma máquina sem saída para a # internet, ou para a imagem transferida à mão — instalar com o que existe é melhor que parar. info "usando o instalador que já está nesta máquina" else if [ -z "$GHCR_USER" ] || [ -z "$GHCR_TOKEN" ]; then passo "Acesso" # O link já chega com o escopo marcado e com um nome sugerido. Sem ele, a pessoa cai na tela de # tokens do GitHub com dezenas de caixas e, na dúvida, marca `repo` — que numa VPS de cliente # entrega os repositórios privados junto com a máquina. cat </dev/null 2>&1 \ || morre "o token não foi aceito. Confira o usuário e se o token foi criado com read:packages: ${LINK_TOKEN}" info "acesso confirmado" # Token válido e sem acesso é o erro MAIS comum aqui, e o mais confuso: a pessoa acabou de criar o # token, ele funciona, e mesmo assim não baixa. Falta o convite ao pacote — que só quem publica # pode dar. Dizer isso evita a pessoa refazer o token cinco vezes achando que errou. docker pull -q "$IMAGEM" >/dev/null 2>&1 \ || morre "o token funciona, mas esta conta não tem acesso ao instalador. Não é erro seu: falta liberarem o seu usuário do GitHub (${GHCR_USER}). Peça o acesso a quem te passou o convite da Chatleady." fi mkdir -p "$DESTINO" docker run --rm -v "${DESTINO}:/destino" "$IMAGEM" >/dev/null info "instalador pronto em ${DESTINO}/installer" # ── instalação ──────────────────────────────────────────────────────────────────────────────── # O CLI roda no HOST: é aqui que ele enxerga portas, systemd e o Docker de verdade. Sem argumentos, # ele mesmo pergunta o que falta — uma pergunta por vez, com prévia antes de gravar. export GHCR_TOKEN GHCR_USER if [ "$#" -gt 0 ]; then exec "${DESTINO}/installer/chatleady" install "$@" fi if tem_terminal; then exec "${DESTINO}/installer/chatleady" install < "$TTY" fi cat <